initial commit, some api methods and structs for everything
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,
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue