mirror of https://github.com/tucnak/telebot
Compare commits
41 Commits
v3.3.1-bet
...
v3
Author | SHA1 | Date |
---|---|---|
Demian | 4d9811176e | 6 days ago |
Demian | cc61a965b4 | 2 months ago |
Demian | 7ee7bd3a21 | 2 months ago |
demget | 898c3e18f9 | 2 months ago |
Nikolay Pavlovich | f1007b7c08 | 2 months ago |
Demian | 4e8d423603 | 2 months ago |
Олегсей | 0442831ecc | 2 months ago |
Demian | 7655e7a9ca | 2 months ago |
Demian | 9c7a18be08 | 2 months ago |
Олегсей | 39d57c70bd | 2 months ago |
Demian | a02de5490f | 2 months ago |
Megum1n | 08f2e85c39 | 2 months ago |
zhongqi | b19cf78b1f | 2 months ago |
Олегсей | e8d6ed91d9 | 2 months ago |
David Shekunts | 14ff5ae5d9 | 2 months ago |
JunJi | e90e62d100 | 2 months ago |
mhrlife | 226075b9d0 | 2 months ago |
Asadbek | 8dd54ae1f6 | 2 months ago |
Taras | f1b0fab4df | 2 months ago |
Maxim Nurmukhametov | af64372b8e | 2 months ago |
Demian | 8b69e47820 | 2 months ago |
Demian | 6713166829 | 2 months ago |
Олегсей | 1c5b5de314 | 2 months ago |
Олегсей | d67653533a | 2 months ago |
Demian | c8882690a2 | 2 months ago |
Олегсей | c37b2b97b3 | 2 months ago |
Demian | ca0a8527e0 | 2 months ago |
Олегсей | ffbf081045 | 2 months ago |
Demian | 864bef4e4d | 2 months ago |
Demian | 2c98c59344 | 2 months ago |
Demian | a8acf84301 | 4 months ago |
Demian | 10fbde9b66 | 4 months ago |
Demian | 1acc928a92 | 4 months ago |
Demian | c9cec214a9 | 4 months ago |
Demian | c4fce10013 | 4 months ago |
Demian | 47227931ec | 4 months ago |
Nash-Well | e0c24df69d | 4 months ago |
Nash-Well | 77f77a6840 | 4 months ago |
Demian | a26ba9f0bd | 6 months ago |
Demian | af50a953b5 | 6 months ago |
nash | e16cc083f7 | 6 months ago |
@ -1,377 +1,118 @@
|
||||
package telebot
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Raw lets you call any method of Bot API manually.
|
||||
// It also handles API errors, so you only need to unwrap
|
||||
// result field from json data.
|
||||
func (b *Bot) Raw(method string, payload interface{}) ([]byte, error) {
|
||||
url := b.URL + "/bot" + b.Token + "/" + method
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Cancel the request immediately without waiting for the timeout
|
||||
// when bot is about to stop.
|
||||
// This may become important if doing long polling with long timeout.
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
b.stopMu.RLock()
|
||||
stopCh := b.stopClient
|
||||
b.stopMu.RUnlock()
|
||||
|
||||
select {
|
||||
case <-stopCh:
|
||||
cancel()
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, &buf)
|
||||
if err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := b.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
resp.Close = true
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
|
||||
if b.verbose {
|
||||
verbose(method, payload, data)
|
||||
}
|
||||
|
||||
// returning data as well
|
||||
return data, extractOk(data)
|
||||
}
|
||||
|
||||
func (b *Bot) sendFiles(method string, files map[string]File, params map[string]string) ([]byte, error) {
|
||||
rawFiles := make(map[string]interface{})
|
||||
for name, f := range files {
|
||||
switch {
|
||||
case f.InCloud():
|
||||
params[name] = f.FileID
|
||||
case f.FileURL != "":
|
||||
params[name] = f.FileURL
|
||||
case f.OnDisk():
|
||||
rawFiles[name] = f.FileLocal
|
||||
case f.FileReader != nil:
|
||||
rawFiles[name] = f.FileReader
|
||||
default:
|
||||
return nil, fmt.Errorf("telebot: file for field %s doesn't exist", name)
|
||||
}
|
||||
}
|
||||
|
||||
if len(rawFiles) == 0 {
|
||||
return b.Raw(method, params)
|
||||
}
|
||||
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
writer := multipart.NewWriter(pipeWriter)
|
||||
|
||||
go func() {
|
||||
defer pipeWriter.Close()
|
||||
|
||||
for field, file := range rawFiles {
|
||||
if err := addFileToWriter(writer, files[field].fileName, field, file); err != nil {
|
||||
pipeWriter.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
for field, value := range params {
|
||||
if err := writer.WriteField(field, value); err != nil {
|
||||
pipeWriter.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := writer.Close(); err != nil {
|
||||
pipeWriter.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
url := b.URL + "/bot" + b.Token + "/" + method
|
||||
|
||||
resp, err := b.client.Post(url, writer.FormDataContentType(), pipeReader)
|
||||
if err != nil {
|
||||
err = wrapError(err)
|
||||
pipeReader.CloseWithError(err)
|
||||
return nil, err
|
||||
}
|
||||
resp.Close = true
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusInternalServerError {
|
||||
return nil, ErrInternal
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
|
||||
return data, extractOk(data)
|
||||
}
|
||||
|
||||
func addFileToWriter(writer *multipart.Writer, filename, field string, file interface{}) error {
|
||||
var reader io.Reader
|
||||
if r, ok := file.(io.Reader); ok {
|
||||
reader = r
|
||||
} else if path, ok := file.(string); ok {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
reader = f
|
||||
} else {
|
||||
return fmt.Errorf("telebot: file for field %v should be io.ReadCloser or string", field)
|
||||
}
|
||||
|
||||
part, err := writer.CreateFormFile(field, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(part, reader)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *File) process(name string, files map[string]File) string {
|
||||
switch {
|
||||
case f.InCloud():
|
||||
return f.FileID
|
||||
case f.FileURL != "":
|
||||
return f.FileURL
|
||||
case f.OnDisk() || f.FileReader != nil:
|
||||
files[name] = *f
|
||||
return "attach://" + name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (b *Bot) sendText(to Recipient, text string, opt *SendOptions) (*Message, error) {
|
||||
params := map[string]string{
|
||||
"chat_id": to.Recipient(),
|
||||
"text": text,
|
||||
}
|
||||
b.embedSendOptions(params, opt)
|
||||
|
||||
data, err := b.Raw("sendMessage", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return extractMessage(data)
|
||||
}
|
||||
|
||||
func (b *Bot) sendMedia(media Media, params map[string]string, files map[string]File) (*Message, error) {
|
||||
kind := media.MediaType()
|
||||
what := "send" + strings.Title(kind)
|
||||
|
||||
if kind == "videoNote" {
|
||||
kind = "video_note"
|
||||
}
|
||||
|
||||
sendFiles := map[string]File{kind: *media.MediaFile()}
|
||||
for k, v := range files {
|
||||
sendFiles[k] = v
|
||||
}
|
||||
|
||||
data, err := b.sendFiles(what, sendFiles, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return extractMessage(data)
|
||||
}
|
||||
|
||||
func (b *Bot) getMe() (*User, error) {
|
||||
data, err := b.Raw("getMe", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
Result *User
|
||||
}
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
return resp.Result, nil
|
||||
}
|
||||
|
||||
func (b *Bot) getUpdates(offset, limit int, timeout time.Duration, allowed []string) ([]Update, error) {
|
||||
params := map[string]string{
|
||||
"offset": strconv.Itoa(offset),
|
||||
"timeout": strconv.Itoa(int(timeout / time.Second)),
|
||||
}
|
||||
|
||||
data, _ := json.Marshal(allowed)
|
||||
params["allowed_updates"] = string(data)
|
||||
|
||||
if limit != 0 {
|
||||
params["limit"] = strconv.Itoa(limit)
|
||||
}
|
||||
|
||||
data, err := b.Raw("getUpdates", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
Result []Update
|
||||
}
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
return resp.Result, nil
|
||||
}
|
||||
|
||||
// extractOk checks given result for error. If result is ok returns nil.
|
||||
// In other cases it extracts API error. If error is not presented
|
||||
// in errors.go, it will be prefixed with `unknown` keyword.
|
||||
func extractOk(data []byte) error {
|
||||
var e struct {
|
||||
Ok bool `json:"ok"`
|
||||
Code int `json:"error_code"`
|
||||
Description string `json:"description"`
|
||||
Parameters map[string]interface{} `json:"parameters"`
|
||||
}
|
||||
if json.NewDecoder(bytes.NewReader(data)).Decode(&e) != nil {
|
||||
return nil // FIXME
|
||||
}
|
||||
if e.Ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := Err(e.Description)
|
||||
switch err {
|
||||
case nil:
|
||||
case ErrGroupMigrated:
|
||||
migratedTo, ok := e.Parameters["migrate_to_chat_id"]
|
||||
if !ok {
|
||||
return NewError(e.Code, e.Description)
|
||||
}
|
||||
|
||||
return GroupError{
|
||||
err: err.(*Error),
|
||||
MigratedTo: int64(migratedTo.(float64)),
|
||||
}
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
||||
switch e.Code {
|
||||
case http.StatusTooManyRequests:
|
||||
retryAfter, ok := e.Parameters["retry_after"]
|
||||
if !ok {
|
||||
return NewError(e.Code, e.Description)
|
||||
}
|
||||
|
||||
err = FloodError{
|
||||
err: NewError(e.Code, e.Description),
|
||||
RetryAfter: int(retryAfter.(float64)),
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("telegram: %s (%d)", e.Description, e.Code)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// extractMessage extracts common Message result from given data.
|
||||
// Should be called after extractOk or b.Raw() to handle possible errors.
|
||||
func extractMessage(data []byte) (*Message, error) {
|
||||
var resp struct {
|
||||
Result *Message
|
||||
}
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
var resp struct {
|
||||
Result bool
|
||||
}
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
if resp.Result {
|
||||
return nil, ErrTrueResult
|
||||
}
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
return resp.Result, nil
|
||||
}
|
||||
|
||||
func (b *Bot) forwardCopyMessages(to Recipient, msgs []Editable, key string, opts ...*SendOptions) ([]Message, error) {
|
||||
params := map[string]string{
|
||||
"chat_id": to.Recipient(),
|
||||
}
|
||||
|
||||
embedMessages(params, msgs)
|
||||
|
||||
if len(opts) > 0 {
|
||||
b.embedSendOptions(params, opts[0])
|
||||
}
|
||||
|
||||
data, err := b.Raw(key, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
Result []Message
|
||||
}
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
var resp struct {
|
||||
Result bool
|
||||
}
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
return resp.Result, nil
|
||||
}
|
||||
|
||||
func verbose(method string, payload interface{}, data []byte) {
|
||||
body, _ := json.Marshal(payload)
|
||||
body = bytes.ReplaceAll(body, []byte(`\"`), []byte(`"`))
|
||||
body = bytes.ReplaceAll(body, []byte(`"{`), []byte(`{`))
|
||||
body = bytes.ReplaceAll(body, []byte(`}"`), []byte(`}`))
|
||||
|
||||
indent := func(b []byte) string {
|
||||
var buf bytes.Buffer
|
||||
json.Indent(&buf, b, "", " ")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
log.Printf(
|
||||
"[verbose] telebot: sent request\nMethod: %v\nParams: %v\nResponse: %v",
|
||||
method, indent(body), indent(data),
|
||||
)
|
||||
import "io"
|
||||
|
||||
// API is the interface that wraps all basic methods for interacting
|
||||
// with Telegram Bot API.
|
||||
type API interface {
|
||||
Raw(method string, payload interface{}) ([]byte, error)
|
||||
|
||||
Accept(query *PreCheckoutQuery, errorMessage ...string) error
|
||||
AddStickerToSet(of Recipient, name string, sticker InputSticker) error
|
||||
AdminsOf(chat *Chat) ([]ChatMember, error)
|
||||
Answer(query *Query, resp *QueryResponse) error
|
||||
AnswerWebApp(query *Query, r Result) (*WebAppMessage, error)
|
||||
ApproveJoinRequest(chat Recipient, user *User) error
|
||||
Ban(chat *Chat, member *ChatMember, revokeMessages ...bool) error
|
||||
BanSenderChat(chat *Chat, sender Recipient) error
|
||||
BusinessConnection(id string) (*BusinessConnection, error)
|
||||
ChatByID(id int64) (*Chat, error)
|
||||
ChatByUsername(name string) (*Chat, error)
|
||||
ChatMemberOf(chat, user Recipient) (*ChatMember, error)
|
||||
Close() (bool, error)
|
||||
CloseGeneralTopic(chat *Chat) error
|
||||
CloseTopic(chat *Chat, topic *Topic) error
|
||||
Commands(opts ...interface{}) ([]Command, error)
|
||||
Copy(to Recipient, msg Editable, opts ...interface{}) (*Message, error)
|
||||
CopyMany(to Recipient, msgs []Editable, opts ...*SendOptions) ([]Message, error)
|
||||
CreateInviteLink(chat Recipient, link *ChatInviteLink) (*ChatInviteLink, error)
|
||||
CreateInvoiceLink(i Invoice) (string, error)
|
||||
CreateStickerSet(of Recipient, set *StickerSet) error
|
||||
CreateTopic(chat *Chat, topic *Topic) (*Topic, error)
|
||||
CustomEmojiStickers(ids []string) ([]Sticker, error)
|
||||
DeclineJoinRequest(chat Recipient, user *User) error
|
||||
DefaultRights(forChannels bool) (*Rights, error)
|
||||
Delete(msg Editable) error
|
||||
DeleteCommands(opts ...interface{}) error
|
||||
DeleteGroupPhoto(chat *Chat) error
|
||||
DeleteGroupStickerSet(chat *Chat) error
|
||||
DeleteMany(msgs []Editable) error
|
||||
DeleteSticker(sticker string) error
|
||||
DeleteStickerSet(name string) error
|
||||
DeleteTopic(chat *Chat, topic *Topic) error
|
||||
Download(file *File, localFilename string) error
|
||||
Edit(msg Editable, what interface{}, opts ...interface{}) (*Message, error)
|
||||
EditCaption(msg Editable, caption string, opts ...interface{}) (*Message, error)
|
||||
EditGeneralTopic(chat *Chat, topic *Topic) error
|
||||
EditInviteLink(chat Recipient, link *ChatInviteLink) (*ChatInviteLink, error)
|
||||
EditMedia(msg Editable, media Inputtable, opts ...interface{}) (*Message, error)
|
||||
EditReplyMarkup(msg Editable, markup *ReplyMarkup) (*Message, error)
|
||||
EditTopic(chat *Chat, topic *Topic) error
|
||||
File(file *File) (io.ReadCloser, error)
|
||||
FileByID(fileID string) (File, error)
|
||||
Forward(to Recipient, msg Editable, opts ...interface{}) (*Message, error)
|
||||
ForwardMany(to Recipient, msgs []Editable, opts ...*SendOptions) ([]Message, error)
|
||||
GameScores(user Recipient, msg Editable) ([]GameHighScore, error)
|
||||
HideGeneralTopic(chat *Chat) error
|
||||
InviteLink(chat *Chat) (string, error)
|
||||
Leave(chat Recipient) error
|
||||
Len(chat *Chat) (int, error)
|
||||
Logout() (bool, error)
|
||||
MenuButton(chat *User) (*MenuButton, error)
|
||||
MyDescription(language string) (*BotInfo, error)
|
||||
MyName(language string) (*BotInfo, error)
|
||||
MyShortDescription(language string) (*BotInfo, error)
|
||||
Notify(to Recipient, action ChatAction, threadID ...int) error
|
||||
Pin(msg Editable, opts ...interface{}) error
|
||||
ProfilePhotosOf(user *User) ([]Photo, error)
|
||||
Promote(chat *Chat, member *ChatMember) error
|
||||
React(to Recipient, msg Editable, r Reactions) error
|
||||
RefundStars(to Recipient, chargeID string) error
|
||||
RemoveWebhook(dropPending ...bool) error
|
||||
ReopenGeneralTopic(chat *Chat) error
|
||||
ReopenTopic(chat *Chat, topic *Topic) error
|
||||
ReplaceStickerInSet(of Recipient, stickerSet, oldSticker string, sticker InputSticker) (bool, error)
|
||||
Reply(to *Message, what interface{}, opts ...interface{}) (*Message, error)
|
||||
Respond(c *Callback, resp ...*CallbackResponse) error
|
||||
Restrict(chat *Chat, member *ChatMember) error
|
||||
RevokeInviteLink(chat Recipient, link string) (*ChatInviteLink, error)
|
||||
Send(to Recipient, what interface{}, opts ...interface{}) (*Message, error)
|
||||
SendAlbum(to Recipient, a Album, opts ...interface{}) ([]Message, error)
|
||||
SendPaid(to Recipient, stars int, a PaidAlbum, opts ...interface{}) (*Message, error)
|
||||
SetAdminTitle(chat *Chat, user *User, title string) error
|
||||
SetCommands(opts ...interface{}) error
|
||||
SetCustomEmojiStickerSetThumb(name, id string) error
|
||||
SetDefaultRights(rights Rights, forChannels bool) error
|
||||
SetGameScore(user Recipient, msg Editable, score GameHighScore) (*Message, error)
|
||||
SetGroupDescription(chat *Chat, description string) error
|
||||
SetGroupPermissions(chat *Chat, perms Rights) error
|
||||
SetGroupStickerSet(chat *Chat, setName string) error
|
||||
SetGroupTitle(chat *Chat, title string) error
|
||||
SetMenuButton(chat *User, mb interface{}) error
|
||||
SetMyDescription(desc, language string) error
|
||||
SetMyName(name, language string) error
|
||||
SetMyShortDescription(desc, language string) error
|
||||
SetStickerEmojis(sticker string, emojis []string) error
|
||||
SetStickerKeywords(sticker string, keywords []string) error
|
||||
SetStickerMaskPosition(sticker string, mask MaskPosition) error
|
||||
SetStickerPosition(sticker string, position int) error
|
||||
SetStickerSetThumb(of Recipient, set *StickerSet) error
|
||||
SetStickerSetTitle(s StickerSet) error
|
||||
SetWebhook(w *Webhook) error
|
||||
Ship(query *ShippingQuery, what ...interface{}) error
|
||||
StarTransactions(offset, limit int) ([]StarTransaction, error)
|
||||
StickerSet(name string) (*StickerSet, error)
|
||||
StopLiveLocation(msg Editable, opts ...interface{}) (*Message, error)
|
||||
StopPoll(msg Editable, opts ...interface{}) (*Poll, error)
|
||||
TopicIconStickers() ([]Sticker, error)
|
||||
Unban(chat *Chat, user *User, forBanned ...bool) error
|
||||
UnbanSenderChat(chat *Chat, sender Recipient) error
|
||||
UnhideGeneralTopic(chat *Chat) error
|
||||
Unpin(chat Recipient, messageID ...int) error
|
||||
UnpinAll(chat Recipient) error
|
||||
UnpinAllGeneralTopicMessages(chat *Chat) error
|
||||
UnpinAllTopicMessages(chat *Chat, topic *Topic) error
|
||||
UploadSticker(to Recipient, format StickerSetFormat, f File) (*File, error)
|
||||
UserBoosts(chat, user Recipient) ([]Boost, error)
|
||||
Webhook() (*Webhook, error)
|
||||
}
|
||||
|
@ -0,0 +1,377 @@
|
||||
package telebot
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Raw lets you call any method of Bot API manually.
|
||||
// It also handles API errors, so you only need to unwrap
|
||||
// result field from json data.
|
||||
func (b *Bot) Raw(method string, payload interface{}) ([]byte, error) {
|
||||
url := b.URL + "/bot" + b.Token + "/" + method
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Cancel the request immediately without waiting for the timeout
|
||||
// when bot is about to stop.
|
||||
// This may become important if doing long polling with long timeout.
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
b.stopMu.RLock()
|
||||
stopCh := b.stopClient
|
||||
b.stopMu.RUnlock()
|
||||
|
||||
select {
|
||||
case <-stopCh:
|
||||
cancel()
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, &buf)
|
||||
if err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := b.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
resp.Close = true
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
|
||||
if b.verbose {
|
||||
verbose(method, payload, data)
|
||||
}
|
||||
|
||||
// returning data as well
|
||||
return data, extractOk(data)
|
||||
}
|
||||
|
||||
func (b *Bot) sendFiles(method string, files map[string]File, params map[string]string) ([]byte, error) {
|
||||
rawFiles := make(map[string]interface{})
|
||||
for name, f := range files {
|
||||
switch {
|
||||
case f.InCloud():
|
||||
params[name] = f.FileID
|
||||
case f.FileURL != "":
|
||||
params[name] = f.FileURL
|
||||
case f.OnDisk():
|
||||
rawFiles[name] = f.FileLocal
|
||||
case f.FileReader != nil:
|
||||
rawFiles[name] = f.FileReader
|
||||
default:
|
||||
return nil, fmt.Errorf("telebot: file for field %s doesn't exist", name)
|
||||
}
|
||||
}
|
||||
|
||||
if len(rawFiles) == 0 {
|
||||
return b.Raw(method, params)
|
||||
}
|
||||
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
writer := multipart.NewWriter(pipeWriter)
|
||||
|
||||
go func() {
|
||||
defer pipeWriter.Close()
|
||||
|
||||
for field, file := range rawFiles {
|
||||
if err := addFileToWriter(writer, files[field].fileName, field, file); err != nil {
|
||||
pipeWriter.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
for field, value := range params {
|
||||
if err := writer.WriteField(field, value); err != nil {
|
||||
pipeWriter.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := writer.Close(); err != nil {
|
||||
pipeWriter.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
url := b.URL + "/bot" + b.Token + "/" + method
|
||||
|
||||
resp, err := b.client.Post(url, writer.FormDataContentType(), pipeReader)
|
||||
if err != nil {
|
||||
err = wrapError(err)
|
||||
pipeReader.CloseWithError(err)
|
||||
return nil, err
|
||||
}
|
||||
resp.Close = true
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusInternalServerError {
|
||||
return nil, ErrInternal
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
|
||||
return data, extractOk(data)
|
||||
}
|
||||
|
||||
func addFileToWriter(writer *multipart.Writer, filename, field string, file interface{}) error {
|
||||
var reader io.Reader
|
||||
if r, ok := file.(io.Reader); ok {
|
||||
reader = r
|
||||
} else if path, ok := file.(string); ok {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
reader = f
|
||||
} else {
|
||||
return fmt.Errorf("telebot: file for field %v should be io.ReadCloser or string", field)
|
||||
}
|
||||
|
||||
part, err := writer.CreateFormFile(field, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(part, reader)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *File) process(name string, files map[string]File) string {
|
||||
switch {
|
||||
case f.InCloud():
|
||||
return f.FileID
|
||||
case f.FileURL != "":
|
||||
return f.FileURL
|
||||
case f.OnDisk() || f.FileReader != nil:
|
||||
files[name] = *f
|
||||
return "attach://" + name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (b *Bot) sendText(to Recipient, text string, opt *SendOptions) (*Message, error) {
|
||||
params := map[string]string{
|
||||
"chat_id": to.Recipient(),
|
||||
"text": text,
|
||||
}
|
||||
b.embedSendOptions(params, opt)
|
||||
|
||||
data, err := b.Raw("sendMessage", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return extractMessage(data)
|
||||
}
|
||||
|
||||
func (b *Bot) sendMedia(media Media, params map[string]string, files map[string]File) (*Message, error) {
|
||||
kind := media.MediaType()
|
||||
what := "send" + strings.Title(kind)
|
||||
|
||||
if kind == "videoNote" {
|
||||
kind = "video_note"
|
||||
}
|
||||
|
||||
sendFiles := map[string]File{kind: *media.MediaFile()}
|
||||
for k, v := range files {
|
||||
sendFiles[k] = v
|
||||
}
|
||||
|
||||
data, err := b.sendFiles(what, sendFiles, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return extractMessage(data)
|
||||
}
|
||||
|
||||
func (b *Bot) getMe() (*User, error) {
|
||||
data, err := b.Raw("getMe", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
Result *User
|
||||
}
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
return resp.Result, nil
|
||||
}
|
||||
|
||||
func (b *Bot) getUpdates(offset, limit int, timeout time.Duration, allowed []string) ([]Update, error) {
|
||||
params := map[string]string{
|
||||
"offset": strconv.Itoa(offset),
|
||||
"timeout": strconv.Itoa(int(timeout / time.Second)),
|
||||
}
|
||||
|
||||
data, _ := json.Marshal(allowed)
|
||||
params["allowed_updates"] = string(data)
|
||||
|
||||
if limit != 0 {
|
||||
params["limit"] = strconv.Itoa(limit)
|
||||
}
|
||||
|
||||
data, err := b.Raw("getUpdates", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
Result []Update
|
||||
}
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
return resp.Result, nil
|
||||
}
|
||||
|
||||
func (b *Bot) forwardCopyMany(to Recipient, msgs []Editable, key string, opts ...*SendOptions) ([]Message, error) {
|
||||
params := map[string]string{
|
||||
"chat_id": to.Recipient(),
|
||||
}
|
||||
|
||||
embedMessages(params, msgs)
|
||||
|
||||
if len(opts) > 0 {
|
||||
b.embedSendOptions(params, opts[0])
|
||||
}
|
||||
|
||||
data, err := b.Raw(key, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
Result []Message
|
||||
}
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
var resp struct {
|
||||
Result bool
|
||||
}
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
return resp.Result, nil
|
||||
}
|
||||
|
||||
// extractOk checks given result for error. If result is ok returns nil.
|
||||
// In other cases it extracts API error. If error is not presented
|
||||
// in errors.go, it will be prefixed with `unknown` keyword.
|
||||
func extractOk(data []byte) error {
|
||||
var e struct {
|
||||
Ok bool `json:"ok"`
|
||||
Code int `json:"error_code"`
|
||||
Description string `json:"description"`
|
||||
Parameters map[string]interface{} `json:"parameters"`
|
||||
}
|
||||
if json.NewDecoder(bytes.NewReader(data)).Decode(&e) != nil {
|
||||
return nil // FIXME
|
||||
}
|
||||
if e.Ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := Err(e.Description)
|
||||
switch err {
|
||||
case nil:
|
||||
case ErrGroupMigrated:
|
||||
migratedTo, ok := e.Parameters["migrate_to_chat_id"]
|
||||
if !ok {
|
||||
return NewError(e.Code, e.Description)
|
||||
}
|
||||
|
||||
return GroupError{
|
||||
err: err.(*Error),
|
||||
MigratedTo: int64(migratedTo.(float64)),
|
||||
}
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
||||
switch e.Code {
|
||||
case http.StatusTooManyRequests:
|
||||
retryAfter, ok := e.Parameters["retry_after"]
|
||||
if !ok {
|
||||
return NewError(e.Code, e.Description)
|
||||
}
|
||||
|
||||
err = FloodError{
|
||||
err: NewError(e.Code, e.Description),
|
||||
RetryAfter: int(retryAfter.(float64)),
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("telegram: %s (%d)", e.Description, e.Code)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// extractMessage extracts common Message result from given data.
|
||||
// Should be called after extractOk or b.Raw() to handle possible errors.
|
||||
func extractMessage(data []byte) (*Message, error) {
|
||||
var resp struct {
|
||||
Result *Message
|
||||
}
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
var resp struct {
|
||||
Result bool
|
||||
}
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
if resp.Result {
|
||||
return nil, ErrTrueResult
|
||||
}
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
return resp.Result, nil
|
||||
}
|
||||
|
||||
func verbose(method string, payload interface{}, data []byte) {
|
||||
body, _ := json.Marshal(payload)
|
||||
body = bytes.ReplaceAll(body, []byte(`\"`), []byte(`"`))
|
||||
body = bytes.ReplaceAll(body, []byte(`"{`), []byte(`{`))
|
||||
body = bytes.ReplaceAll(body, []byte(`}"`), []byte(`}`))
|
||||
|
||||
indent := func(b []byte) string {
|
||||
var buf bytes.Buffer
|
||||
json.Indent(&buf, b, "", " ")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
log.Printf(
|
||||
"[verbose] telebot: sent request\nMethod: %v\nParams: %v\nResponse: %v",
|
||||
method, indent(body), indent(data),
|
||||
)
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
package telebot
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BusinessConnection struct {
|
||||
// Unique identifier of the business connection
|
||||
ID string `json:"id"`
|
||||
|
||||
// Business account user that created the business connection
|
||||
Sender *User `json:"user"`
|
||||
|
||||
// Identifier of a private chat with the user who created the business connection. This
|
||||
// number may have more than 32 significant bits and some programming languages may
|
||||
// have difficulty/silent defects in interpreting it. But it has at most 52 significant bits,
|
||||
// so a 64-bit integer or double-precision float type are safe for storing this identifier.
|
||||
UserChatID int64 `json:"user_chat_id"`
|
||||
|
||||
// Unixtime, use BusinessConnection.Time() to get time.Time.
|
||||
Unixtime int64 `json:"date"`
|
||||
|
||||
// True, if the bot can act on behalf of the business account in chats that were active in the last 24 hours
|
||||
CanReply bool `json:"can_reply"`
|
||||
|
||||
// True, if the connection is active
|
||||
Enabled bool `json:"is_enabled"`
|
||||
}
|
||||
|
||||
// Time returns the moment of business connection creation in local time.
|
||||
func (b *BusinessConnection) Time() time.Time {
|
||||
return time.Unix(b.Unixtime, 0)
|
||||
}
|
||||
|
||||
type BusinessMessagesDeleted struct {
|
||||
// Unique identifier of the business connection
|
||||
BusinessConnectionID string `json:"business_connection_id"`
|
||||
|
||||
// Information about a chat in the business account. The bot
|
||||
// may not have access to the chat or the corresponding user.
|
||||
Chat *Chat `json:"chat"`
|
||||
|
||||
// The list of identifiers of deleted messages in the chat of the business account
|
||||
MessageIDs []int `json:"message_ids"`
|
||||
}
|
||||
|
||||
type BusinessIntro struct {
|
||||
// (Optional)
|
||||
// Title text of the business intro
|
||||
Title string `json:"title"`
|
||||
|
||||
// Message text of the business intro
|
||||
Message string `json:"message"`
|
||||
|
||||
// Sticker of the business intro
|
||||
Sticker *Sticker `json:"sticker"`
|
||||
}
|
||||
|
||||
type BusinessLocation struct {
|
||||
// Address of the business
|
||||
Address string `json:"address"`
|
||||
|
||||
// (Optional) Location of the business
|
||||
Location *Location `json:"location"`
|
||||
}
|
||||
|
||||
type BusinessOpeningHoursInterval struct {
|
||||
// The minute's sequence number in a week, starting on Monday,
|
||||
// marking the start of the time interval during which the business
|
||||
// is open; 0 - 7 * 24 * 60
|
||||
OpeningMinute int `json:"opening_minute"`
|
||||
|
||||
// The minute's sequence number in a week, starting on Monday,
|
||||
// marking the start of the time interval during which the business
|
||||
// is open; 0 - 7 * 24 * 60
|
||||
ClosingMinute int `json:"closing_minute"`
|
||||
}
|
||||
|
||||
type BusinessOpeningHours struct {
|
||||
// Unique name of the time zone for which the opening hours are defined
|
||||
Timezone string `json:"time_zone_name"`
|
||||
|
||||
// List of time intervals describing business opening hours
|
||||
OpeningHours []BusinessOpeningHoursInterval `json:"opening_hours"`
|
||||
}
|
||||
|
||||
// BusinessConnection returns the information about the connection of the bot with a business account.
|
||||
func (b *Bot) BusinessConnection(id string) (*BusinessConnection, error) {
|
||||
params := map[string]string{
|
||||
"business_connection_id": id,
|
||||
}
|
||||
|
||||
data, err := b.Raw("getBusinessConnection", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
Result *BusinessConnection
|
||||
}
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return nil, wrapError(err)
|
||||
}
|
||||
return resp.Result, nil
|
||||
}
|
@ -1 +1,8 @@
|
||||
article_message: This is an article.
|
||||
|
||||
nested:
|
||||
example: |-
|
||||
This is {{ . }}.
|
||||
another:
|
||||
example: |-
|
||||
This is {{ . }}.
|
||||
|
@ -0,0 +1,87 @@
|
||||
package react
|
||||
|
||||
import (
|
||||
tele "gopkg.in/telebot.v3"
|
||||
)
|
||||
|
||||
type Reaction = tele.Reaction
|
||||
|
||||
func React(r ...Reaction) tele.Reactions {
|
||||
return tele.Reactions{Reactions: r}
|
||||
}
|
||||
|
||||
// Currently available emojis.
|
||||
var (
|
||||
ThumbUp = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "👍"}
|
||||
ThumbDown = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "👎"}
|
||||
Heart = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "❤"}
|
||||
Fire = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🔥"}
|
||||
HeartEyes = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "😍"}
|
||||
ClappingHands = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "👏"}
|
||||
GrinningFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "😁"}
|
||||
ThinkingFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🤔"}
|
||||
ExplodingHead = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🤯"}
|
||||
ScreamingFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "😱"}
|
||||
SwearingFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🤬"}
|
||||
CryingFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "😢"}
|
||||
PartyPopper = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🎉"}
|
||||
StarStruck = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🤩"}
|
||||
VomitingFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🤮"}
|
||||
PileOfPoo = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "💩"}
|
||||
PrayingHands = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🙏"}
|
||||
OkHand = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "👌"}
|
||||
DoveOfPeace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🕊"}
|
||||
ClownFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🤡"}
|
||||
YawningFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🥱"}
|
||||
WoozyFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🥴"}
|
||||
Whale = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🐳"}
|
||||
HeartOnFire = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "❤🔥"}
|
||||
MoonFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🌚"}
|
||||
HotDog = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🌭"}
|
||||
HundredPoints = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "💯"}
|
||||
RollingOnTheFloorLaughing = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🤣"}
|
||||
Lightning = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "⚡"}
|
||||
Banana = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🍌"}
|
||||
Trophy = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🏆"}
|
||||
BrokenHeart = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "💔"}
|
||||
FaceWithRaisedEyebrow = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🤨"}
|
||||
NeutralFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "😐"}
|
||||
Strawberry = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🍓"}
|
||||
Champagne = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🍾"}
|
||||
KissMark = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "💋"}
|
||||
MiddleFinger = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🖕"}
|
||||
EvilFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "😈"}
|
||||
SleepingFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "😴"}
|
||||
LoudlyCryingFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "😭"}
|
||||
NerdFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🤓"}
|
||||
Ghost = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "👻"}
|
||||
Engineer = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "👨💻"}
|
||||
Eyes = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "👀"}
|
||||
JackOLantern = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🎃"}
|
||||
NoMonkey = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🙈"}
|
||||
SmilingFaceWithHalo = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "😇"}
|
||||
FearfulFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "😨"}
|
||||
Handshake = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🤝"}
|
||||
WritingHand = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "✍"}
|
||||
HuggingFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🤗"}
|
||||
Brain = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🫡"}
|
||||
SantaClaus = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🎅"}
|
||||
ChristmasTree = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🎄"}
|
||||
Snowman = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "☃"}
|
||||
NailPolish = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "💅"}
|
||||
ZanyFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🤪"}
|
||||
Moai = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🗿"}
|
||||
Cool = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🆒"}
|
||||
HeartWithArrow = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "💘"}
|
||||
HearMonkey = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🙉"}
|
||||
Unicorn = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🦄"}
|
||||
FaceBlowingKiss = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "😘"}
|
||||
Pill = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "💊"}
|
||||
SpeaklessMonkey = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🙊"}
|
||||
Sunglasses = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "😎"}
|
||||
AlienMonster = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "👾"}
|
||||
ManShrugging = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🤷♂️"}
|
||||
PersonShrugging = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🤷"}
|
||||
WomanShrugging = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "🤷♀️"}
|
||||
PoutingFace = Reaction{Type: tele.ReactionTypeEmoji, Emoji: "😡"}
|
||||
)
|
@ -1,81 +0,0 @@
|
||||
package react
|
||||
|
||||
import "gopkg.in/telebot.v3"
|
||||
|
||||
type Reaction = telebot.Reaction
|
||||
|
||||
// Currently available emojis.
|
||||
var (
|
||||
ThumbUp = Reaction{Emoji: "👍"}
|
||||
ThumbDown = Reaction{Emoji: "👎"}
|
||||
Heart = Reaction{Emoji: "❤"}
|
||||
Fire = Reaction{Emoji: "🔥"}
|
||||
HeartEyes = Reaction{Emoji: "😍"}
|
||||
ClappingHands = Reaction{Emoji: "👏"}
|
||||
GrinningFace = Reaction{Emoji: "😁"}
|
||||
ThinkingFace = Reaction{Emoji: "🤔"}
|
||||
ExplodingHead = Reaction{Emoji: "🤯"}
|
||||
ScreamingFace = Reaction{Emoji: "😱"}
|
||||
SwearingFace = Reaction{Emoji: "🤬"}
|
||||
CryingFace = Reaction{Emoji: "😢"}
|
||||
PartyPopper = Reaction{Emoji: "🎉"}
|
||||
StarStruck = Reaction{Emoji: "🤩"}
|
||||
VomitingFace = Reaction{Emoji: "🤮"}
|
||||
PileOfPoo = Reaction{Emoji: "💩"}
|
||||
PrayingHands = Reaction{Emoji: "🙏"}
|
||||
OkHand = Reaction{Emoji: "👌"}
|
||||
DoveOfPeace = Reaction{Emoji: "🕊"}
|
||||
ClownFace = Reaction{Emoji: "🤡"}
|
||||
YawningFace = Reaction{Emoji: "🥱"}
|
||||
WoozyFace = Reaction{Emoji: "🥴"}
|
||||
Whale = Reaction{Emoji: "🐳"}
|
||||
HeartOnFire = Reaction{Emoji: "❤🔥"}
|
||||
MoonFace = Reaction{Emoji: "🌚"}
|
||||
HotDog = Reaction{Emoji: "🌭"}
|
||||
HundredPoints = Reaction{Emoji: "💯"}
|
||||
RollingOnTheFloorLaughing = Reaction{Emoji: "🤣"}
|
||||
Lightning = Reaction{Emoji: "⚡"}
|
||||
Banana = Reaction{Emoji: "🍌"}
|
||||
Trophy = Reaction{Emoji: "🏆"}
|
||||
BrokenHeart = Reaction{Emoji: "💔"}
|
||||
FaceWithRaisedEyebrow = Reaction{Emoji: "🤨"}
|
||||
NeutralFace = Reaction{Emoji: "😐"}
|
||||
Strawberry = Reaction{Emoji: "🍓"}
|
||||
Champagne = Reaction{Emoji: "🍾"}
|
||||
KissMark = Reaction{Emoji: "💋"}
|
||||
MiddleFinger = Reaction{Emoji: "🖕"}
|
||||
EvilFace = Reaction{Emoji: "😈"}
|
||||
SleepingFace = Reaction{Emoji: "😴"}
|
||||
LoudlyCryingFace = Reaction{Emoji: "😭"}
|
||||
NerdFace = Reaction{Emoji: "🤓"}
|
||||
Ghost = Reaction{Emoji: "👻"}
|
||||
Engineer = Reaction{Emoji: "👨💻"}
|
||||
Eyes = Reaction{Emoji: "👀"}
|
||||
JackOLantern = Reaction{Emoji: "🎃"}
|
||||
NoMonkey = Reaction{Emoji: "🙈"}
|
||||
SmilingFaceWithHalo = Reaction{Emoji: "😇"}
|
||||
FearfulFace = Reaction{Emoji: "😨"}
|
||||
Handshake = Reaction{Emoji: "🤝"}
|
||||
WritingHand = Reaction{Emoji: "✍"}
|
||||
HuggingFace = Reaction{Emoji: "🤗"}
|
||||
Brain = Reaction{Emoji: "🫡"}
|
||||
SantaClaus = Reaction{Emoji: "🎅"}
|
||||
ChristmasTree = Reaction{Emoji: "🎄"}
|
||||
Snowman = Reaction{Emoji: "☃"}
|
||||
NailPolish = Reaction{Emoji: "💅"}
|
||||
ZanyFace = Reaction{Emoji: "🤪"}
|
||||
Moai = Reaction{Emoji: "🗿"}
|
||||
Cool = Reaction{Emoji: "🆒"}
|
||||
HeartWithArrow = Reaction{Emoji: "💘"}
|
||||
HearMonkey = Reaction{Emoji: "🙉"}
|
||||
Unicorn = Reaction{Emoji: "🦄"}
|
||||
FaceBlowingKiss = Reaction{Emoji: "😘"}
|
||||
Pill = Reaction{Emoji: "💊"}
|
||||
SpeaklessMonkey = Reaction{Emoji: "🙊"}
|
||||
Sunglasses = Reaction{Emoji: "😎"}
|
||||
AlienMonster = Reaction{Emoji: "👾"}
|
||||
ManShrugging = Reaction{Emoji: "🤷♂️"}
|
||||
PersonShrugging = Reaction{Emoji: "🤷"}
|
||||
WomanShrugging = Reaction{Emoji: "🤷♀️"}
|
||||
PoutingFace = Reaction{Emoji: "😡"}
|
||||
)
|
@ -0,0 +1,73 @@
|
||||
package telebot
|
||||
|
||||
import "time"
|
||||
|
||||
type TransactionType = string
|
||||
|
||||
const (
|
||||
TransactionTypeUser TransactionType = "user"
|
||||
TransactionTypeFragment TransactionType = "fragment"
|
||||
TransactionPartnerTelegramAds TransactionType = "telegram_ads"
|
||||
TransactionTypeOther TransactionType = "other"
|
||||
)
|
||||
|
||||
type RevenueState = string
|
||||
|
||||
const (
|
||||
RevenueStatePending RevenueState = "pending"
|
||||
RevenueStateSucceeded RevenueState = "succeeded"
|
||||
RevenueStateFailed RevenueState = "failed"
|
||||
)
|
||||
|
||||
type StarTransaction struct {
|
||||
// Unique identifier of the transaction. Coincides with the identifier of the
|
||||
// original transaction for refund transactions. Coincides with
|
||||
// SuccessfulPayment.telegram_payment_charge_id for successful incoming
|
||||
// payments from users.
|
||||
ID string `json:"id"`
|
||||
|
||||
// Number of Telegram Stars transferred by the transaction
|
||||
Amount int `json:"amount"`
|
||||
|
||||
// Date the transaction was created in Unix time
|
||||
Unixtime int64 `json:"date"`
|
||||
|
||||
// (Optional) Source of an incoming transaction (e.g., a user purchasing goods
|
||||
// or services, Fragment refunding a failed withdrawal). Only for incoming transactions
|
||||
Source TransactionPartner `json:"source"`
|
||||
|
||||
// (Optional) Receiver of an outgoing transaction (e.g., a user for a purchase
|
||||
// refund, Fragment for a withdrawal). Only for outgoing transactions
|
||||
Receiver TransactionPartner `json:"receiver"`
|
||||
}
|
||||
|
||||
type TransactionPartner struct {
|
||||
// Type of the state
|
||||
Type TransactionType `json:"type"`
|
||||
User *User `json:"user,omitempty"`
|
||||
Payload string `json:"invoice_payload"`
|
||||
|
||||
// (Optional) State of the transaction if the transaction is outgoing$$
|
||||
Withdrawal RevenueWithdrawal `json:"withdrawal_state,omitempty"`
|
||||
}
|
||||
|
||||
type RevenueWithdrawal struct {
|
||||
// Type of the state
|
||||
Type RevenueState `json:"type"`
|
||||
|
||||
// Date the withdrawal was completed in Unix time
|
||||
Unixtime int `json:"date,omitempty"`
|
||||
|
||||
// An HTTPS URL that can be used to see transaction details
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
// Time returns the date of the transaction.
|
||||
func (c *StarTransaction) Time() time.Time {
|
||||
return time.Unix(c.Unixtime, 0)
|
||||
}
|
||||
|
||||
// Time returns the date of the withdrawal.
|
||||
func (s *RevenueWithdrawal) Time() time.Time {
|
||||
return time.Unix(int64(s.Unixtime), 0)
|
||||
}
|
Loading…
Reference in New Issue