commit
434756c165
@ -0,0 +1,109 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"flag" |
||||||
|
"io/ioutil" |
||||||
|
"log" |
||||||
|
"strconv" |
||||||
|
"strings" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
type Config struct { |
||||||
|
Token string `json:"token"` |
||||||
|
} |
||||||
|
|
||||||
|
type Plugin interface { |
||||||
|
GetCommand() string |
||||||
|
GotCommand(Message, []string) |
||||||
|
} |
||||||
|
|
||||||
|
type ColonThree struct { |
||||||
|
} |
||||||
|
|
||||||
|
func (plugin *ColonThree) GetCommand() string { |
||||||
|
return "/three" |
||||||
|
} |
||||||
|
|
||||||
|
func (plugin *ColonThree) GotCommand(message Message, args []string) { |
||||||
|
if len(args) > 0 { |
||||||
|
n, err := strconv.Atoi(args[0]) |
||||||
|
if err != nil { |
||||||
|
msg := NewMessage(message.Chat.Id, "Bad number!") |
||||||
|
msg.ReplyToMessageId = message.MessageId |
||||||
|
|
||||||
|
bot.sendMessage(msg) |
||||||
|
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if n > 5 { |
||||||
|
msg := NewMessage(message.Chat.Id, "That's a bit much, no?") |
||||||
|
msg.ReplyToMessageId = message.MessageId |
||||||
|
|
||||||
|
bot.sendMessage(msg) |
||||||
|
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
for i := 0; i < n; i++ { |
||||||
|
bot.sendMessage(NewMessage(message.Chat.Id, ":3")) |
||||||
|
} |
||||||
|
} else { |
||||||
|
bot.sendMessage(NewMessage(message.Chat.Id, ":3")) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var bot *BotApi |
||||||
|
|
||||||
|
func main() { |
||||||
|
configPath := flag.String("config", "config.json", "path to config.json") |
||||||
|
|
||||||
|
flag.Parse() |
||||||
|
|
||||||
|
data, err := ioutil.ReadFile(*configPath) |
||||||
|
if err != nil { |
||||||
|
log.Panic(err) |
||||||
|
} |
||||||
|
|
||||||
|
var cfg Config |
||||||
|
json.Unmarshal(data, &cfg) |
||||||
|
|
||||||
|
bot = NewBotApi(BotConfig{ |
||||||
|
token: cfg.Token, |
||||||
|
debug: true, |
||||||
|
}) |
||||||
|
|
||||||
|
plugins := []Plugin{&ColonThree{}} |
||||||
|
|
||||||
|
ticker := time.NewTicker(5 * time.Second) |
||||||
|
|
||||||
|
lastUpdate := 0 |
||||||
|
|
||||||
|
for range ticker.C { |
||||||
|
updates, err := bot.getUpdates(NewUpdate(lastUpdate + 1)) |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
log.Panic(err) |
||||||
|
} |
||||||
|
|
||||||
|
for _, update := range updates { |
||||||
|
lastUpdate = update.UpdateId |
||||||
|
|
||||||
|
if update.Message.Text == "" { |
||||||
|
continue |
||||||
|
} |
||||||
|
|
||||||
|
for _, plugin := range plugins { |
||||||
|
parts := strings.Split(update.Message.Text, " ") |
||||||
|
|
||||||
|
if plugin.GetCommand() == parts[0] { |
||||||
|
parts = append(parts[:0], parts[1:]...) |
||||||
|
|
||||||
|
plugin.GotCommand(update.Message, parts) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,443 @@ |
|||||||
|
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, |
||||||
|
} |
||||||
|
} |
Reference in new issue