444 lines
10 KiB
Go
444 lines
10 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"io/ioutil"
|
||
|
"log"
|
||
|
"net/http"
|
||
|
"net/url"
|
||
|
"strconv"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
CHAT_TYPING = "typing"
|
||
|
CHAT_UPLOAD_PHOTO = "upload_photo"
|
||
|
CHAT_RECORD_VIDEO = "record_video"
|
||
|
CHAT_UPLOAD_VIDEO = "upload_video"
|
||
|
CHAT_RECORD_AUDIO = "record_audio"
|
||
|
CHAT_UPLOAD_AUDIO = "upload_audio"
|
||
|
CHAT_UPLOAD_DOCUMENT = "upload_document"
|
||
|
CHAT_FIND_LOCATION = "find_location"
|
||
|
)
|
||
|
|
||
|
type BotConfig struct {
|
||
|
token string
|
||
|
debug bool
|
||
|
}
|
||
|
|
||
|
type BotApi struct {
|
||
|
config BotConfig
|
||
|
}
|
||
|
|
||
|
type ApiResponse struct {
|
||
|
Ok bool `json:"ok"`
|
||
|
Result json.RawMessage `json:"result"`
|
||
|
}
|
||
|
|
||
|
type Update struct {
|
||
|
UpdateId int `json:"update_id"`
|
||
|
Message Message `json:"message"`
|
||
|
}
|
||
|
|
||
|
type User struct {
|
||
|
Id int `json:"id"`
|
||
|
FirstName string `json:"first_name"`
|
||
|
LastName string `json:"last_name"`
|
||
|
UserName string `json:"username"`
|
||
|
}
|
||
|
|
||
|
type GroupChat struct {
|
||
|
Id int `json:"id"`
|
||
|
Title string `json:"title"`
|
||
|
}
|
||
|
|
||
|
type UserOrGroupChat struct {
|
||
|
Id int `json:"id"`
|
||
|
FirstName string `json:"first_name"`
|
||
|
LastName string `json:"last_name"`
|
||
|
UserName string `json:"username"`
|
||
|
Title string `json:"title"`
|
||
|
}
|
||
|
|
||
|
type Message struct {
|
||
|
MessageId int `json:"message_id"`
|
||
|
From User `json:"from"`
|
||
|
Date int `json:"date"`
|
||
|
Chat UserOrGroupChat `json:"chat"`
|
||
|
ForwardFrom User `json:"forward_from"`
|
||
|
ForwardDate int `json:"forward_date"`
|
||
|
ReplyToMessage *Message `json:"reply_to_message"`
|
||
|
Text string `json:"text"`
|
||
|
Audio Audio `json:"audio"`
|
||
|
Document Document `json:"document"`
|
||
|
Photo []PhotoSize `json:"photo"`
|
||
|
Sticker Sticker `json:"sticker"`
|
||
|
Video Video `json:"video"`
|
||
|
Contact Contact `json:"contact"`
|
||
|
Location Location `json:"location"`
|
||
|
NewChatParticipant User `json:"new_chat_participant"`
|
||
|
LeftChatParticipant User `json:"left_chat_participant"`
|
||
|
NewChatTitle string `json:"new_chat_title"`
|
||
|
NewChatPhoto string `json:"new_chat_photo"`
|
||
|
DeleteChatPhoto bool `json:"delete_chat_photo"`
|
||
|
GroupChatCreated bool `json:"group_chat_created"`
|
||
|
}
|
||
|
|
||
|
type PhotoSize struct {
|
||
|
FileId string `json:"file_id"`
|
||
|
Width int `json:"width"`
|
||
|
Height int `json:"height"`
|
||
|
FileSize int `json:"file_size"`
|
||
|
}
|
||
|
|
||
|
type Audio struct {
|
||
|
FileId string `json:"file_id"`
|
||
|
Duration int `json:"duration"`
|
||
|
MimeType string `json:"mime_type"`
|
||
|
FileSize int `json:"file_size"`
|
||
|
}
|
||
|
|
||
|
type Document struct {
|
||
|
FileId string `json:"file_id"`
|
||
|
Thumb PhotoSize `json:"thumb"`
|
||
|
FileName string `json:"file_name"`
|
||
|
MimeType string `json:"mime_type"`
|
||
|
FileSize int `json:"file_size"`
|
||
|
}
|
||
|
|
||
|
type Sticker struct {
|
||
|
FileId string `json:"file_id"`
|
||
|
Width int `json:"width"`
|
||
|
Height int `json:"height"`
|
||
|
Thumb PhotoSize `json:"thumb"`
|
||
|
FileSize int `json:"file_size"`
|
||
|
}
|
||
|
|
||
|
type Video struct {
|
||
|
FileId string `json:"file_id"`
|
||
|
Width int `json:"width"`
|
||
|
Height int `json:"height"`
|
||
|
Duration int `json:"duration"`
|
||
|
Thumb PhotoSize `json:"thumb"`
|
||
|
MimeType string `json:"mime_type"`
|
||
|
FileSize int `json:"file_size"`
|
||
|
Caption string `json:"caption"`
|
||
|
}
|
||
|
|
||
|
type Contact struct {
|
||
|
PhoneNumber string `json:"phone_number"`
|
||
|
FirstName string `json:"first_name"`
|
||
|
LastName string `json:"last_name"`
|
||
|
UserId string `json:"user_id"`
|
||
|
}
|
||
|
|
||
|
type Location struct {
|
||
|
Longitude float32 `json:"longitude"`
|
||
|
Latitude float32 `json:"latitude"`
|
||
|
}
|
||
|
|
||
|
type UserProfilePhotos struct {
|
||
|
TotalCount int `json:"total_count"`
|
||
|
Photos []PhotoSize `json:"photos"`
|
||
|
}
|
||
|
|
||
|
type ReplyKeyboardMarkup struct {
|
||
|
Keyboard map[string]map[string]string `json:"keyboard"`
|
||
|
ResizeKeyboard bool `json:"resize_keyboard"`
|
||
|
OneTimeKeyboard bool `json:"one_time_keyboard"`
|
||
|
Selective bool `json:"selective"`
|
||
|
}
|
||
|
|
||
|
type ReplyKeyboardHide struct {
|
||
|
HideKeyboard bool `json:"hide_keyboard"`
|
||
|
Selective bool `json:"selective"`
|
||
|
}
|
||
|
|
||
|
type ForceReply struct {
|
||
|
ForceReply bool `json:"force_reply"`
|
||
|
Selective bool `json:"force_reply"`
|
||
|
}
|
||
|
|
||
|
type UpdateConfig struct {
|
||
|
Offset int
|
||
|
Limit int
|
||
|
Timeout int
|
||
|
}
|
||
|
|
||
|
type MessageConfig struct {
|
||
|
ChatId int
|
||
|
Text string
|
||
|
DisableWebPagePreview bool
|
||
|
ReplyToMessageId int
|
||
|
}
|
||
|
|
||
|
type ForwardConfig struct {
|
||
|
ChatId int
|
||
|
FromChatId int
|
||
|
MessageId int
|
||
|
}
|
||
|
|
||
|
type LocationConfig struct {
|
||
|
ChatId int
|
||
|
Latitude float64
|
||
|
Longitude float64
|
||
|
ReplyToMessageId int
|
||
|
ReplyMarkup interface{}
|
||
|
}
|
||
|
|
||
|
type ChatActionConfig struct {
|
||
|
ChatId int
|
||
|
Action string
|
||
|
}
|
||
|
|
||
|
type UserProfilePhotosConfig struct {
|
||
|
UserId int
|
||
|
Offset int
|
||
|
Limit int
|
||
|
}
|
||
|
|
||
|
func NewBotApi(config BotConfig) *BotApi {
|
||
|
return &BotApi{
|
||
|
config: config,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (bot *BotApi) makeRequest(endpoint string, params url.Values) (ApiResponse, error) {
|
||
|
resp, err := http.PostForm("https://api.telegram.org/bot"+bot.config.token+"/"+endpoint, params)
|
||
|
defer resp.Body.Close()
|
||
|
if err != nil {
|
||
|
return ApiResponse{}, err
|
||
|
}
|
||
|
|
||
|
bytes, err := ioutil.ReadAll(resp.Body)
|
||
|
if err != nil {
|
||
|
return ApiResponse{}, nil
|
||
|
}
|
||
|
|
||
|
if bot.config.debug {
|
||
|
log.Println(string(bytes[:]))
|
||
|
}
|
||
|
|
||
|
var apiResp ApiResponse
|
||
|
json.Unmarshal(bytes, &apiResp)
|
||
|
|
||
|
return apiResp, 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)
|
||
|
|
||
|
if bot.config.debug {
|
||
|
log.Printf("getMe: %+v\n", user)
|
||
|
}
|
||
|
|
||
|
return user, nil
|
||
|
}
|
||
|
|
||
|
func (bot *BotApi) sendMessage(config MessageConfig) (Message, error) {
|
||
|
v := url.Values{}
|
||
|
v.Add("chat_id", strconv.Itoa(config.ChatId))
|
||
|
v.Add("text", config.Text)
|
||
|
v.Add("disable_web_page_preview", strconv.FormatBool(config.DisableWebPagePreview))
|
||
|
if config.ReplyToMessageId != 0 {
|
||
|
v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageId))
|
||
|
}
|
||
|
|
||
|
resp, err := bot.makeRequest("sendMessage", v)
|
||
|
if err != nil {
|
||
|
return Message{}, err
|
||
|
}
|
||
|
|
||
|
var message Message
|
||
|
json.Unmarshal(resp.Result, &message)
|
||
|
|
||
|
if bot.config.debug {
|
||
|
log.Printf("sendMessage req : %+v\n", v)
|
||
|
log.Printf("sendMessage resp: %+v\n", message)
|
||
|
}
|
||
|
|
||
|
return message, nil
|
||
|
}
|
||
|
|
||
|
func (bot *BotApi) forwardMessage(config ForwardConfig) (Message, error) {
|
||
|
v := url.Values{}
|
||
|
v.Add("chat_id", strconv.Itoa(config.ChatId))
|
||
|
v.Add("from_chat_id", strconv.Itoa(config.FromChatId))
|
||
|
v.Add("message_id", strconv.Itoa(config.MessageId))
|
||
|
|
||
|
resp, err := bot.makeRequest("forwardMessage", v)
|
||
|
if err != nil {
|
||
|
return Message{}, err
|
||
|
}
|
||
|
|
||
|
var message Message
|
||
|
json.Unmarshal(resp.Result, &message)
|
||
|
|
||
|
if bot.config.debug {
|
||
|
log.Printf("forwardMessage req : %+v\n", v)
|
||
|
log.Printf("forwardMessage resp: %+v\n", message)
|
||
|
}
|
||
|
|
||
|
return message, nil
|
||
|
}
|
||
|
|
||
|
func (bot *BotApi) sendLocation(config LocationConfig) (Message, error) {
|
||
|
v := url.Values{}
|
||
|
v.Add("chat_id", strconv.Itoa(config.ChatId))
|
||
|
v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64))
|
||
|
v.Add("longitude", strconv.FormatFloat(config.Longitude, 'f', 6, 64))
|
||
|
if config.ReplyToMessageId != 0 {
|
||
|
v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageId))
|
||
|
}
|
||
|
if config.ReplyMarkup != nil {
|
||
|
data, err := json.Marshal(config.ReplyMarkup)
|
||
|
if err != nil {
|
||
|
return Message{}, err
|
||
|
}
|
||
|
|
||
|
v.Add("reply_markup", string(data))
|
||
|
}
|
||
|
|
||
|
resp, err := bot.makeRequest("sendLocation", v)
|
||
|
if err != nil {
|
||
|
return Message{}, err
|
||
|
}
|
||
|
|
||
|
var message Message
|
||
|
json.Unmarshal(resp.Result, &message)
|
||
|
|
||
|
if bot.config.debug {
|
||
|
log.Printf("sendLocation req : %+v\n", v)
|
||
|
log.Printf("sendLocation resp: %+v\n", message)
|
||
|
}
|
||
|
|
||
|
return message, nil
|
||
|
}
|
||
|
|
||
|
func (bot *BotApi) sendChatAction(config ChatActionConfig) error {
|
||
|
v := url.Values{}
|
||
|
v.Add("chat_id", strconv.Itoa(config.ChatId))
|
||
|
v.Add("action", config.Action)
|
||
|
|
||
|
_, err := bot.makeRequest("sendChatAction", v)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return 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)
|
||
|
|
||
|
if bot.config.debug {
|
||
|
log.Printf("getUserProfilePhotos req : %+v\n", v)
|
||
|
log.Printf("getUserProfilePhotos resp: %+v\n", profilePhotos)
|
||
|
}
|
||
|
|
||
|
return profilePhotos, 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)
|
||
|
|
||
|
if bot.config.debug {
|
||
|
log.Printf("getUpdates: %+v\n", updates)
|
||
|
}
|
||
|
|
||
|
return updates, nil
|
||
|
}
|
||
|
|
||
|
func (bot *BotApi) setWebhook(v url.Values) error {
|
||
|
_, err := bot.makeRequest("setWebhook", v)
|
||
|
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
func NewMessage(chatId int, text string) MessageConfig {
|
||
|
return MessageConfig{
|
||
|
ChatId: chatId,
|
||
|
Text: text,
|
||
|
DisableWebPagePreview: false,
|
||
|
ReplyToMessageId: 0,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func NewForward(chatId int, fromChatId int, messageId int) ForwardConfig {
|
||
|
return ForwardConfig{
|
||
|
ChatId: chatId,
|
||
|
FromChatId: fromChatId,
|
||
|
MessageId: messageId,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func NewLocation(chatId int, latitude float64, longitude float64) LocationConfig {
|
||
|
return LocationConfig{
|
||
|
ChatId: chatId,
|
||
|
Latitude: latitude,
|
||
|
Longitude: longitude,
|
||
|
ReplyToMessageId: 0,
|
||
|
ReplyMarkup: nil,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func NewChatAction(chatId int, action string) ChatActionConfig {
|
||
|
return ChatActionConfig{
|
||
|
ChatId: chatId,
|
||
|
Action: action,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func NewUserProfilePhotos(userId int) UserProfilePhotosConfig {
|
||
|
return UserProfilePhotosConfig{
|
||
|
UserId: userId,
|
||
|
Offset: 0,
|
||
|
Limit: 0,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func NewUpdate(offset int) UpdateConfig {
|
||
|
return UpdateConfig{
|
||
|
Offset: offset,
|
||
|
Limit: 0,
|
||
|
Timeout: 0,
|
||
|
}
|
||
|
}
|