change focus from bot to just bindings, code cleanup

pull/1/head
Syfaro 10 years ago
parent 5d6f84e9b2
commit 9cf4f13772
  1. 52
      README.md
  2. 131
      bot.go
  3. 144
      helpers.go
  4. 429
      methods.go
  5. 99
      plugin_fa.go
  6. 76
      plugin_help.go
  7. 153
      plugin_manage.go
  8. 2
      types.go
  9. 22
      updates.go

@ -1,54 +1,6 @@
# Golang Telegram bot using the Bot API # Golang Telegram bindings for the Bot API
A simple Golang bot for the Telegram Bot API
Really simple bot for interacting with the Telegram Bot API, not nearly done yet. Expect frequent breaking changes! Bindings for interacting with the Telegram Bot API, not nearly done yet.
All methods have been added, and all features should be available. All methods have been added, and all features should be available.
If you want a feature that hasn't been added yet, open an issue and I'll see what I can do. If you want a feature that hasn't been added yet, open an issue and I'll see what I can do.
There's a few plugins in here, named as `plugin_*.go`.
## Getting started
After installing all the dependencies, run
```
go build
./telegram-bot-api -newbot
```
Fill in any asked information, enable whatever plugins, etc.
## Plugins
All plugins implement the `Plugin` interface.
```go
type Plugin interface {
GetName() string
GetCommands() []string
GetHelpText() []string
GotCommand(string, Message, []string)
Setup()
}
```
`GetName` should return the plugin's name. This must be unique!
`GetCommands` should return a slice of strings, each command should look like `/help`, it must have the forward slash!
`GetHelpText` should return a slice of strings with each command and usage. You many include any number of items in here.
`GotCommand` is called when a command is executed for this plugin, the parameters are the command name, the Message struct, and a list of arguments passed to the command. The original text is available in the Message struct.
`Setup` is called when the bot first starts, if it needs any configuration, ask here.
To add your plugin, you must edit a line of code and then run the `go build` again.
```go
// current version
plugins = []Plugin{&HelpPlugin{}, &ManagePlugin{}}
// add your own plugins
plugins = []Plugin{&HelpPlugin{}, &FAPlugin{}, &ManagePlugin{}}
```

131
bot.go

@ -1,128 +1,13 @@
package main package tgbotapi
import ( type BotApi struct {
"encoding/json" Token string `json:"token"`
"flag" Debug bool `json:"debug"`
"fmt" Updates chan Update `json:"-"`
"io/ioutil"
"log"
"strings"
"time"
)
type Config struct {
Token string `json:"token"`
Plugins map[string]string `json:"plugins"`
EnabledPlugins map[string]bool `json:"enabled"`
}
type Plugin interface {
GetName() string
GetCommands() []string
GetHelpText() []string
GotCommand(string, Message, []string)
Setup()
} }
var bot *BotApi func NewBotApi(token string) *BotApi {
var plugins []Plugin return &BotApi{
var config Config Token: token,
var configPath *string
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)
}
json.Unmarshal(data, &config)
bot = NewBotApi(BotConfig{
token: config.Token,
debug: true,
})
plugins = []Plugin{&HelpPlugin{}, &FAPlugin{}, &ManagePlugin{}}
for _, plugin := range plugins {
val, ok := config.EnabledPlugins[plugin.GetName()]
if !ok {
fmt.Printf("Enable '%s'? [y/N] ", plugin.GetName())
var enabled string
fmt.Scanln(&enabled)
if strings.ToLower(enabled) == "y" {
plugin.Setup()
log.Printf("Plugin '%s' started!\n", plugin.GetName())
config.EnabledPlugins[plugin.GetName()] = true
} else {
config.EnabledPlugins[plugin.GetName()] = false
}
}
if val {
plugin.Setup()
log.Printf("Plugin '%s' started!\n", plugin.GetName())
}
saveConfig()
} }
ticker := time.NewTicker(time.Second)
lastUpdate := 0
for range ticker.C {
update := NewUpdate(lastUpdate + 1)
update.Timeout = 30
updates, err := bot.getUpdates(update)
if err != nil {
log.Panic(err)
}
for _, update := range updates {
lastUpdate = update.UpdateId
if update.Message.Text == "" {
continue
}
for _, plugin := range plugins {
val, _ := config.EnabledPlugins[plugin.GetName()]
if !val {
continue
}
parts := strings.Split(update.Message.Text, " ")
command := parts[0]
for _, cmd := range plugin.GetCommands() {
if cmd == command {
if bot.config.debug {
log.Printf("'%s' matched plugin '%s'", update.Message.Text, plugin.GetName())
}
args := append(parts[:0], parts[1:]...)
plugin.GotCommand(command, update.Message, args)
}
}
}
}
}
}
func saveConfig() {
data, _ := json.MarshalIndent(config, "", " ")
ioutil.WriteFile(*configPath, data, 0600)
} }

@ -0,0 +1,144 @@
package tgbotapi
import (
"net/url"
)
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 NewPhotoUpload(chatId int, filename string) PhotoConfig {
return PhotoConfig{
ChatId: chatId,
UseExistingPhoto: false,
FilePath: filename,
}
}
func NewPhotoShare(chatId int, fileId string) PhotoConfig {
return PhotoConfig{
ChatId: chatId,
UseExistingPhoto: true,
FileId: fileId,
}
}
func NewAudioUpload(chatId int, filename string) AudioConfig {
return AudioConfig{
ChatId: chatId,
UseExistingAudio: false,
FilePath: filename,
}
}
func NewAudioShare(chatId int, fileId string) AudioConfig {
return AudioConfig{
ChatId: chatId,
UseExistingAudio: true,
FileId: fileId,
}
}
func NewDocumentUpload(chatId int, filename string) DocumentConfig {
return DocumentConfig{
ChatId: chatId,
UseExistingDocument: false,
FilePath: filename,
}
}
func NewDocumentShare(chatId int, fileId string) DocumentConfig {
return DocumentConfig{
ChatId: chatId,
UseExistingDocument: true,
FileId: fileId,
}
}
func NewStickerUpload(chatId int, filename string) StickerConfig {
return StickerConfig{
ChatId: chatId,
UseExistingSticker: false,
FilePath: filename,
}
}
func NewStickerShare(chatId int, fileId string) StickerConfig {
return StickerConfig{
ChatId: chatId,
UseExistingSticker: true,
FileId: fileId,
}
}
func NewVideoUpload(chatId int, filename string) VideoConfig {
return VideoConfig{
ChatId: chatId,
UseExistingVideo: false,
FilePath: filename,
}
}
func NewVideoShare(chatId int, fileId string) VideoConfig {
return VideoConfig{
ChatId: chatId,
UseExistingVideo: true,
FileId: fileId,
}
}
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,
}
}
func NewWebhook(link string) WebhookConfig {
u, _ := url.Parse(link)
return WebhookConfig{
Url: u,
Clear: false,
}
}

@ -1,4 +1,4 @@
package main package tgbotapi
import ( import (
"bytes" "bytes"
@ -25,23 +25,98 @@ const (
CHAT_FIND_LOCATION = "find_location" CHAT_FIND_LOCATION = "find_location"
) )
type BotConfig struct { type MessageConfig struct {
token string ChatId int
debug bool Text string
DisableWebPagePreview bool
ReplyToMessageId int
ReplyMarkup interface{}
} }
type BotApi struct { type ForwardConfig struct {
config BotConfig ChatId int
FromChatId int
MessageId int
} }
func NewBotApi(config BotConfig) *BotApi { type PhotoConfig struct {
return &BotApi{ ChatId int
config: config, Caption string
} ReplyToMessageId int
ReplyMarkup interface{}
UseExistingPhoto bool
FilePath string
FileId string
}
type AudioConfig struct {
ChatId int
ReplyToMessageId int
ReplyMarkup interface{}
UseExistingAudio bool
FilePath string
FileId string
}
type DocumentConfig struct {
ChatId int
ReplyToMessageId int
ReplyMarkup interface{}
UseExistingDocument bool
FilePath string
FileId string
}
type StickerConfig struct {
ChatId int
ReplyToMessageId int
ReplyMarkup interface{}
UseExistingSticker bool
FilePath string
FileId string
}
type VideoConfig struct {
ChatId int
ReplyToMessageId int
ReplyMarkup interface{}
UseExistingVideo bool
FilePath string
FileId string
}
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
}
type UpdateConfig struct {
Offset int
Limit int
Timeout int
}
type WebhookConfig struct {
Clear bool
Url *url.URL
} }
func (bot *BotApi) makeRequest(endpoint string, params url.Values) (ApiResponse, error) { 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) resp, err := http.PostForm("https://api.telegram.org/bot"+bot.Token+"/"+endpoint, params)
defer resp.Body.Close() defer resp.Body.Close()
if err != nil { if err != nil {
return ApiResponse{}, err return ApiResponse{}, err
@ -52,7 +127,7 @@ func (bot *BotApi) makeRequest(endpoint string, params url.Values) (ApiResponse,
return ApiResponse{}, err return ApiResponse{}, err
} }
if bot.config.debug { if bot.Debug {
log.Println(endpoint, string(bytes)) log.Println(endpoint, string(bytes))
} }
@ -66,7 +141,7 @@ func (bot *BotApi) makeRequest(endpoint string, params url.Values) (ApiResponse,
return apiResp, nil return apiResp, nil
} }
func (bot *BotApi) uploadFile(endpoint string, params map[string]string, fieldname string, filename string) (ApiResponse, error) { func (bot *BotApi) UploadFile(endpoint string, params map[string]string, fieldname string, filename string) (ApiResponse, error) {
var b bytes.Buffer var b bytes.Buffer
w := multipart.NewWriter(&b) w := multipart.NewWriter(&b)
@ -96,7 +171,7 @@ func (bot *BotApi) uploadFile(endpoint string, params map[string]string, fieldna
w.Close() w.Close()
req, err := http.NewRequest("POST", "https://api.telegram.org/bot"+bot.config.token+"/"+endpoint, &b) req, err := http.NewRequest("POST", "https://api.telegram.org/bot"+bot.Token+"/"+endpoint, &b)
if err != nil { if err != nil {
return ApiResponse{}, err return ApiResponse{}, err
} }
@ -114,7 +189,7 @@ func (bot *BotApi) uploadFile(endpoint string, params map[string]string, fieldna
return ApiResponse{}, err return ApiResponse{}, err
} }
if bot.config.debug { if bot.Debug {
log.Println(string(bytes[:])) log.Println(string(bytes[:]))
} }
@ -124,8 +199,8 @@ func (bot *BotApi) uploadFile(endpoint string, params map[string]string, fieldna
return apiResp, nil return apiResp, nil
} }
func (bot *BotApi) getMe() (User, error) { func (bot *BotApi) GetMe() (User, error) {
resp, err := bot.makeRequest("getMe", nil) resp, err := bot.MakeRequest("getMe", nil)
if err != nil { if err != nil {
return User{}, err return User{}, err
} }
@ -133,14 +208,14 @@ func (bot *BotApi) getMe() (User, error) {
var user User var user User
json.Unmarshal(resp.Result, &user) json.Unmarshal(resp.Result, &user)
if bot.config.debug { if bot.Debug {
log.Printf("getMe: %+v\n", user) log.Printf("getMe: %+v\n", user)
} }
return user, nil return user, nil
} }
func (bot *BotApi) sendMessage(config MessageConfig) (Message, error) { func (bot *BotApi) SendMessage(config MessageConfig) (Message, error) {
v := url.Values{} v := url.Values{}
v.Add("chat_id", strconv.Itoa(config.ChatId)) v.Add("chat_id", strconv.Itoa(config.ChatId))
v.Add("text", config.Text) v.Add("text", config.Text)
@ -157,7 +232,7 @@ func (bot *BotApi) sendMessage(config MessageConfig) (Message, error) {
v.Add("reply_markup", string(data)) v.Add("reply_markup", string(data))
} }
resp, err := bot.makeRequest("sendMessage", v) resp, err := bot.MakeRequest("SendMessage", v)
if err != nil { if err != nil {
return Message{}, err return Message{}, err
} }
@ -165,21 +240,21 @@ func (bot *BotApi) sendMessage(config MessageConfig) (Message, error) {
var message Message var message Message
json.Unmarshal(resp.Result, &message) json.Unmarshal(resp.Result, &message)
if bot.config.debug { if bot.Debug {
log.Printf("sendMessage req : %+v\n", v) log.Printf("SendMessage req : %+v\n", v)
log.Printf("sendMessage resp: %+v\n", message) log.Printf("SendMessage resp: %+v\n", message)
} }
return message, nil return message, nil
} }
func (bot *BotApi) forwardMessage(config ForwardConfig) (Message, error) { func (bot *BotApi) ForwardMessage(config ForwardConfig) (Message, error) {
v := url.Values{} v := url.Values{}
v.Add("chat_id", strconv.Itoa(config.ChatId)) v.Add("chat_id", strconv.Itoa(config.ChatId))
v.Add("from_chat_id", strconv.Itoa(config.FromChatId)) v.Add("from_chat_id", strconv.Itoa(config.FromChatId))
v.Add("message_id", strconv.Itoa(config.MessageId)) v.Add("message_id", strconv.Itoa(config.MessageId))
resp, err := bot.makeRequest("forwardMessage", v) resp, err := bot.MakeRequest("forwardMessage", v)
if err != nil { if err != nil {
return Message{}, err return Message{}, err
} }
@ -187,7 +262,7 @@ func (bot *BotApi) forwardMessage(config ForwardConfig) (Message, error) {
var message Message var message Message
json.Unmarshal(resp.Result, &message) json.Unmarshal(resp.Result, &message)
if bot.config.debug { if bot.Debug {
log.Printf("forwardMessage req : %+v\n", v) log.Printf("forwardMessage req : %+v\n", v)
log.Printf("forwardMessage resp: %+v\n", message) log.Printf("forwardMessage resp: %+v\n", message)
} }
@ -195,7 +270,7 @@ func (bot *BotApi) forwardMessage(config ForwardConfig) (Message, error) {
return message, nil return message, nil
} }
func (bot *BotApi) sendPhoto(config PhotoConfig) (Message, error) { func (bot *BotApi) SendPhoto(config PhotoConfig) (Message, error) {
if config.UseExistingPhoto { if config.UseExistingPhoto {
v := url.Values{} v := url.Values{}
v.Add("chat_id", strconv.Itoa(config.ChatId)) v.Add("chat_id", strconv.Itoa(config.ChatId))
@ -215,7 +290,7 @@ func (bot *BotApi) sendPhoto(config PhotoConfig) (Message, error) {
v.Add("reply_markup", string(data)) v.Add("reply_markup", string(data))
} }
resp, err := bot.makeRequest("sendPhoto", v) resp, err := bot.MakeRequest("SendPhoto", v)
if err != nil { if err != nil {
return Message{}, err return Message{}, err
} }
@ -223,9 +298,9 @@ func (bot *BotApi) sendPhoto(config PhotoConfig) (Message, error) {
var message Message var message Message
json.Unmarshal(resp.Result, &message) json.Unmarshal(resp.Result, &message)
if bot.config.debug { if bot.Debug {
log.Printf("sendPhoto req : %+v\n", v) log.Printf("SendPhoto req : %+v\n", v)
log.Printf("sendPhoto resp: %+v\n", message) log.Printf("SendPhoto resp: %+v\n", message)
} }
return message, nil return message, nil
@ -248,7 +323,7 @@ func (bot *BotApi) sendPhoto(config PhotoConfig) (Message, error) {
params["reply_markup"] = string(data) params["reply_markup"] = string(data)
} }
resp, err := bot.uploadFile("sendPhoto", params, "photo", config.FilePath) resp, err := bot.UploadFile("SendPhoto", params, "photo", config.FilePath)
if err != nil { if err != nil {
return Message{}, err return Message{}, err
} }
@ -256,14 +331,14 @@ func (bot *BotApi) sendPhoto(config PhotoConfig) (Message, error) {
var message Message var message Message
json.Unmarshal(resp.Result, &message) json.Unmarshal(resp.Result, &message)
if bot.config.debug { if bot.Debug {
log.Printf("sendPhoto resp: %+v\n", message) log.Printf("SendPhoto resp: %+v\n", message)
} }
return message, nil return message, nil
} }
func (bot *BotApi) sendAudio(config AudioConfig) (Message, error) { func (bot *BotApi) SendAudio(config AudioConfig) (Message, error) {
if config.UseExistingAudio { if config.UseExistingAudio {
v := url.Values{} v := url.Values{}
v.Add("chat_id", strconv.Itoa(config.ChatId)) v.Add("chat_id", strconv.Itoa(config.ChatId))
@ -280,7 +355,7 @@ func (bot *BotApi) sendAudio(config AudioConfig) (Message, error) {
v.Add("reply_markup", string(data)) v.Add("reply_markup", string(data))
} }
resp, err := bot.makeRequest("sendAudio", v) resp, err := bot.MakeRequest("sendAudio", v)
if err != nil { if err != nil {
return Message{}, err return Message{}, err
} }
@ -288,7 +363,7 @@ func (bot *BotApi) sendAudio(config AudioConfig) (Message, error) {
var message Message var message Message
json.Unmarshal(resp.Result, &message) json.Unmarshal(resp.Result, &message)
if bot.config.debug { if bot.Debug {
log.Printf("sendAudio req : %+v\n", v) log.Printf("sendAudio req : %+v\n", v)
log.Printf("sendAudio resp: %+v\n", message) log.Printf("sendAudio resp: %+v\n", message)
} }
@ -311,7 +386,7 @@ func (bot *BotApi) sendAudio(config AudioConfig) (Message, error) {
params["reply_markup"] = string(data) params["reply_markup"] = string(data)
} }
resp, err := bot.uploadFile("sendAudio", params, "audio", config.FilePath) resp, err := bot.UploadFile("sendAudio", params, "audio", config.FilePath)
if err != nil { if err != nil {
return Message{}, err return Message{}, err
} }
@ -319,14 +394,14 @@ func (bot *BotApi) sendAudio(config AudioConfig) (Message, error) {
var message Message var message Message
json.Unmarshal(resp.Result, &message) json.Unmarshal(resp.Result, &message)
if bot.config.debug { if bot.Debug {
log.Printf("sendAudio resp: %+v\n", message) log.Printf("sendAudio resp: %+v\n", message)
} }
return message, nil return message, nil
} }
func (bot *BotApi) sendDocument(config DocumentConfig) (Message, error) { func (bot *BotApi) SendDocument(config DocumentConfig) (Message, error) {
if config.UseExistingDocument { if config.UseExistingDocument {
v := url.Values{} v := url.Values{}
v.Add("chat_id", strconv.Itoa(config.ChatId)) v.Add("chat_id", strconv.Itoa(config.ChatId))
@ -343,7 +418,7 @@ func (bot *BotApi) sendDocument(config DocumentConfig) (Message, error) {
v.Add("reply_markup", string(data)) v.Add("reply_markup", string(data))
} }
resp, err := bot.makeRequest("sendDocument", v) resp, err := bot.MakeRequest("sendDocument", v)
if err != nil { if err != nil {
return Message{}, err return Message{}, err
} }
@ -351,7 +426,7 @@ func (bot *BotApi) sendDocument(config DocumentConfig) (Message, error) {
var message Message var message Message
json.Unmarshal(resp.Result, &message) json.Unmarshal(resp.Result, &message)
if bot.config.debug { if bot.Debug {
log.Printf("sendDocument req : %+v\n", v) log.Printf("sendDocument req : %+v\n", v)
log.Printf("sendDocument resp: %+v\n", message) log.Printf("sendDocument resp: %+v\n", message)
} }
@ -374,7 +449,7 @@ func (bot *BotApi) sendDocument(config DocumentConfig) (Message, error) {
params["reply_markup"] = string(data) params["reply_markup"] = string(data)
} }
resp, err := bot.uploadFile("sendDocument", params, "document", config.FilePath) resp, err := bot.UploadFile("sendDocument", params, "document", config.FilePath)
if err != nil { if err != nil {
return Message{}, err return Message{}, err
} }
@ -382,14 +457,14 @@ func (bot *BotApi) sendDocument(config DocumentConfig) (Message, error) {
var message Message var message Message
json.Unmarshal(resp.Result, &message) json.Unmarshal(resp.Result, &message)
if bot.config.debug { if bot.Debug {
log.Printf("sendDocument resp: %+v\n", message) log.Printf("sendDocument resp: %+v\n", message)
} }
return message, nil return message, nil
} }
func (bot *BotApi) sendSticker(config StickerConfig) (Message, error) { func (bot *BotApi) SendSticker(config StickerConfig) (Message, error) {
if config.UseExistingSticker { if config.UseExistingSticker {
v := url.Values{} v := url.Values{}
v.Add("chat_id", strconv.Itoa(config.ChatId)) v.Add("chat_id", strconv.Itoa(config.ChatId))
@ -406,7 +481,7 @@ func (bot *BotApi) sendSticker(config StickerConfig) (Message, error) {
v.Add("reply_markup", string(data)) v.Add("reply_markup", string(data))
} }
resp, err := bot.makeRequest("sendSticker", v) resp, err := bot.MakeRequest("sendSticker", v)
if err != nil { if err != nil {
return Message{}, err return Message{}, err
} }
@ -414,7 +489,7 @@ func (bot *BotApi) sendSticker(config StickerConfig) (Message, error) {
var message Message var message Message
json.Unmarshal(resp.Result, &message) json.Unmarshal(resp.Result, &message)
if bot.config.debug { if bot.Debug {
log.Printf("sendSticker req : %+v\n", v) log.Printf("sendSticker req : %+v\n", v)
log.Printf("sendSticker resp: %+v\n", message) log.Printf("sendSticker resp: %+v\n", message)
} }
@ -437,7 +512,7 @@ func (bot *BotApi) sendSticker(config StickerConfig) (Message, error) {
params["reply_markup"] = string(data) params["reply_markup"] = string(data)
} }
resp, err := bot.uploadFile("sendSticker", params, "sticker", config.FilePath) resp, err := bot.UploadFile("sendSticker", params, "sticker", config.FilePath)
if err != nil { if err != nil {
return Message{}, err return Message{}, err
} }
@ -445,14 +520,14 @@ func (bot *BotApi) sendSticker(config StickerConfig) (Message, error) {
var message Message var message Message
json.Unmarshal(resp.Result, &message) json.Unmarshal(resp.Result, &message)
if bot.config.debug { if bot.Debug {
log.Printf("sendSticker resp: %+v\n", message) log.Printf("sendSticker resp: %+v\n", message)
} }
return message, nil return message, nil
} }
func (bot *BotApi) sendVideo(config VideoConfig) (Message, error) { func (bot *BotApi) SendVideo(config VideoConfig) (Message, error) {
if config.UseExistingVideo { if config.UseExistingVideo {
v := url.Values{} v := url.Values{}
v.Add("chat_id", strconv.Itoa(config.ChatId)) v.Add("chat_id", strconv.Itoa(config.ChatId))
@ -469,7 +544,7 @@ func (bot *BotApi) sendVideo(config VideoConfig) (Message, error) {
v.Add("reply_markup", string(data)) v.Add("reply_markup", string(data))
} }
resp, err := bot.makeRequest("sendVideo", v) resp, err := bot.MakeRequest("sendVideo", v)
if err != nil { if err != nil {
return Message{}, err return Message{}, err
} }
@ -477,7 +552,7 @@ func (bot *BotApi) sendVideo(config VideoConfig) (Message, error) {
var message Message var message Message
json.Unmarshal(resp.Result, &message) json.Unmarshal(resp.Result, &message)
if bot.config.debug { if bot.Debug {
log.Printf("sendVideo req : %+v\n", v) log.Printf("sendVideo req : %+v\n", v)
log.Printf("sendVideo resp: %+v\n", message) log.Printf("sendVideo resp: %+v\n", message)
} }
@ -500,7 +575,7 @@ func (bot *BotApi) sendVideo(config VideoConfig) (Message, error) {
params["reply_markup"] = string(data) params["reply_markup"] = string(data)
} }
resp, err := bot.uploadFile("sendVideo", params, "video", config.FilePath) resp, err := bot.UploadFile("sendVideo", params, "video", config.FilePath)
if err != nil { if err != nil {
return Message{}, err return Message{}, err
} }
@ -508,14 +583,14 @@ func (bot *BotApi) sendVideo(config VideoConfig) (Message, error) {
var message Message var message Message
json.Unmarshal(resp.Result, &message) json.Unmarshal(resp.Result, &message)
if bot.config.debug { if bot.Debug {
log.Printf("sendVideo resp: %+v\n", message) log.Printf("sendVideo resp: %+v\n", message)
} }
return message, nil return message, nil
} }
func (bot *BotApi) sendLocation(config LocationConfig) (Message, error) { func (bot *BotApi) SendLocation(config LocationConfig) (Message, error) {
v := url.Values{} v := url.Values{}
v.Add("chat_id", strconv.Itoa(config.ChatId)) v.Add("chat_id", strconv.Itoa(config.ChatId))
v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64)) v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64))
@ -532,7 +607,7 @@ func (bot *BotApi) sendLocation(config LocationConfig) (Message, error) {
v.Add("reply_markup", string(data)) v.Add("reply_markup", string(data))
} }
resp, err := bot.makeRequest("sendLocation", v) resp, err := bot.MakeRequest("sendLocation", v)
if err != nil { if err != nil {
return Message{}, err return Message{}, err
} }
@ -540,7 +615,7 @@ func (bot *BotApi) sendLocation(config LocationConfig) (Message, error) {
var message Message var message Message
json.Unmarshal(resp.Result, &message) json.Unmarshal(resp.Result, &message)
if bot.config.debug { if bot.Debug {
log.Printf("sendLocation req : %+v\n", v) log.Printf("sendLocation req : %+v\n", v)
log.Printf("sendLocation resp: %+v\n", message) log.Printf("sendLocation resp: %+v\n", message)
} }
@ -548,12 +623,12 @@ func (bot *BotApi) sendLocation(config LocationConfig) (Message, error) {
return message, nil return message, nil
} }
func (bot *BotApi) sendChatAction(config ChatActionConfig) error { func (bot *BotApi) SendChatAction(config ChatActionConfig) error {
v := url.Values{} v := url.Values{}
v.Add("chat_id", strconv.Itoa(config.ChatId)) v.Add("chat_id", strconv.Itoa(config.ChatId))
v.Add("action", config.Action) v.Add("action", config.Action)
_, err := bot.makeRequest("sendChatAction", v) _, err := bot.MakeRequest("sendChatAction", v)
if err != nil { if err != nil {
return err return err
} }
@ -561,7 +636,7 @@ func (bot *BotApi) sendChatAction(config ChatActionConfig) error {
return nil return nil
} }
func (bot *BotApi) getUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) { func (bot *BotApi) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
v := url.Values{} v := url.Values{}
v.Add("user_id", strconv.Itoa(config.UserId)) v.Add("user_id", strconv.Itoa(config.UserId))
if config.Offset != 0 { if config.Offset != 0 {
@ -571,7 +646,7 @@ func (bot *BotApi) getUserProfilePhotos(config UserProfilePhotosConfig) (UserPro
v.Add("limit", strconv.Itoa(config.Limit)) v.Add("limit", strconv.Itoa(config.Limit))
} }
resp, err := bot.makeRequest("getUserProfilePhotos", v) resp, err := bot.MakeRequest("getUserProfilePhotos", v)
if err != nil { if err != nil {
return UserProfilePhotos{}, err return UserProfilePhotos{}, err
} }
@ -579,7 +654,7 @@ func (bot *BotApi) getUserProfilePhotos(config UserProfilePhotosConfig) (UserPro
var profilePhotos UserProfilePhotos var profilePhotos UserProfilePhotos
json.Unmarshal(resp.Result, &profilePhotos) json.Unmarshal(resp.Result, &profilePhotos)
if bot.config.debug { if bot.Debug {
log.Printf("getUserProfilePhotos req : %+v\n", v) log.Printf("getUserProfilePhotos req : %+v\n", v)
log.Printf("getUserProfilePhotos resp: %+v\n", profilePhotos) log.Printf("getUserProfilePhotos resp: %+v\n", profilePhotos)
} }
@ -587,7 +662,7 @@ func (bot *BotApi) getUserProfilePhotos(config UserProfilePhotosConfig) (UserPro
return profilePhotos, nil return profilePhotos, nil
} }
func (bot *BotApi) getUpdates(config UpdateConfig) ([]Update, error) { func (bot *BotApi) GetUpdates(config UpdateConfig) ([]Update, error) {
v := url.Values{} v := url.Values{}
if config.Offset > 0 { if config.Offset > 0 {
v.Add("offset", strconv.Itoa(config.Offset)) v.Add("offset", strconv.Itoa(config.Offset))
@ -599,7 +674,7 @@ func (bot *BotApi) getUpdates(config UpdateConfig) ([]Update, error) {
v.Add("timeout", strconv.Itoa(config.Timeout)) v.Add("timeout", strconv.Itoa(config.Timeout))
} }
resp, err := bot.makeRequest("getUpdates", v) resp, err := bot.MakeRequest("getUpdates", v)
if err != nil { if err != nil {
return []Update{}, err return []Update{}, err
} }
@ -607,230 +682,20 @@ func (bot *BotApi) getUpdates(config UpdateConfig) ([]Update, error) {
var updates []Update var updates []Update
json.Unmarshal(resp.Result, &updates) json.Unmarshal(resp.Result, &updates)
if bot.config.debug { if bot.Debug {
log.Printf("getUpdates: %+v\n", updates) log.Printf("getUpdates: %+v\n", updates)
} }
return updates, nil return updates, nil
} }
func (bot *BotApi) setWebhook(v url.Values) error { func (bot *BotApi) SetWebhook(config WebhookConfig) error {
_, err := bot.makeRequest("setWebhook", v) v := url.Values{}
if !config.Clear {
return err v.Add("url", config.Url.String())
}
type UpdateConfig struct {
Offset int
Limit int
Timeout int
}
type MessageConfig struct {
ChatId int
Text string
DisableWebPagePreview bool
ReplyToMessageId int
ReplyMarkup interface{}
}
type ForwardConfig struct {
ChatId int
FromChatId int
MessageId int
}
type PhotoConfig struct {
ChatId int
Caption string
ReplyToMessageId int
ReplyMarkup interface{}
UseExistingPhoto bool
FilePath string
FileId string
}
type AudioConfig struct {
ChatId int
ReplyToMessageId int
ReplyMarkup interface{}
UseExistingAudio bool
FilePath string
FileId string
}
type DocumentConfig struct {
ChatId int
ReplyToMessageId int
ReplyMarkup interface{}
UseExistingDocument bool
FilePath string
FileId string
}
type StickerConfig struct {
ChatId int
ReplyToMessageId int
ReplyMarkup interface{}
UseExistingSticker bool
FilePath string
FileId string
}
type VideoConfig struct {
ChatId int
ReplyToMessageId int
ReplyMarkup interface{}
UseExistingVideo bool
FilePath string
FileId string
}
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 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 NewPhotoUpload(chatId int, filename string) PhotoConfig {
return PhotoConfig{
ChatId: chatId,
UseExistingPhoto: false,
FilePath: filename,
}
}
func NewPhotoShare(chatId int, fileId string) PhotoConfig {
return PhotoConfig{
ChatId: chatId,
UseExistingPhoto: true,
FileId: fileId,
}
}
func NewAudioUpload(chatId int, filename string) AudioConfig {
return AudioConfig{
ChatId: chatId,
UseExistingAudio: false,
FilePath: filename,
}
}
func NewAudioShare(chatId int, fileId string) AudioConfig {
return AudioConfig{
ChatId: chatId,
UseExistingAudio: true,
FileId: fileId,
}
}
func NewDocumentUpload(chatId int, filename string) DocumentConfig {
return DocumentConfig{
ChatId: chatId,
UseExistingDocument: false,
FilePath: filename,
}
}
func NewDocumentShare(chatId int, fileId string) DocumentConfig {
return DocumentConfig{
ChatId: chatId,
UseExistingDocument: true,
FileId: fileId,
}
}
func NewStickerUpload(chatId int, filename string) StickerConfig {
return StickerConfig{
ChatId: chatId,
UseExistingSticker: false,
FilePath: filename,
}
}
func NewStickerShare(chatId int, fileId string) StickerConfig {
return StickerConfig{
ChatId: chatId,
UseExistingSticker: true,
FileId: fileId,
}
}
func NewVideoUpload(chatId int, filename string) VideoConfig {
return VideoConfig{
ChatId: chatId,
UseExistingVideo: false,
FilePath: filename,
}
}
func NewVideoShare(chatId int, fileId string) VideoConfig {
return VideoConfig{
ChatId: chatId,
UseExistingVideo: true,
FileId: fileId,
}
}
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 { _, err := bot.MakeRequest("setWebhook", v)
return UserProfilePhotosConfig{
UserId: userId,
Offset: 0,
Limit: 0,
}
}
func NewUpdate(offset int) UpdateConfig { return err
return UpdateConfig{
Offset: offset,
Limit: 0,
Timeout: 0,
}
} }

@ -1,99 +0,0 @@
package main
import (
"fmt"
"github.com/PuerkitoBio/goquery"
"github.com/ddliu/go-httpclient"
"io"
"net/http"
"os"
"strconv"
"strings"
)
type FAPlugin struct {
}
func (plugin *FAPlugin) GetName() string {
return "FA Mirrorer"
}
func (plugin *FAPlugin) GetCommands() []string {
return []string{"/fa"}
}
func (plugin *FAPlugin) GetHelpText() []string {
return []string{"/fa [link] - mirrors an image from FurAffinity"}
}
func (plugin *FAPlugin) Setup() {
a, ok := config.Plugins["fa_a"]
if !ok {
fmt.Print("FurAffinity Cookie a: ")
fmt.Scanln(&a)
config.Plugins["fa_a"] = a
}
b, ok := config.Plugins["fa_b"]
if !ok {
fmt.Print("FurAffinity Cookie b: ")
fmt.Scanln(&b)
config.Plugins["fa_b"] = b
}
}
func (plugin *FAPlugin) GotCommand(command string, message Message, args []string) {
if len(args) == 0 {
bot.sendMessage(NewMessage(message.Chat.Id, "You need to include a link!"))
return
}
bot.sendChatAction(NewChatAction(message.Chat.Id, CHAT_UPLOAD_PHOTO))
_, err := strconv.Atoi(args[0])
if err == nil {
args[0] = "http://www.furaffinity.net/view/" + args[0]
}
resp, err := httpclient.WithCookie(&http.Cookie{
Name: "b",
Value: config.Plugins["fa_b"],
}).WithCookie(&http.Cookie{
Name: "a",
Value: config.Plugins["fa_a"],
}).Get(args[0], nil)
if err != nil {
bot.sendMessage(NewMessage(message.Chat.Id, "ERR : "+err.Error()))
}
defer resp.Body.Close()
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
bot.sendMessage(NewMessage(message.Chat.Id, "ERR : "+err.Error()))
}
sel := doc.Find("#submissionImg")
for i := range sel.Nodes {
single := sel.Eq(i)
val, _ := single.Attr("src")
tokens := strings.Split(val, "/")
fileName := tokens[len(tokens)-1]
output, _ := os.Create(fileName)
defer output.Close()
defer os.Remove(output.Name())
resp, _ := http.Get("http:" + val)
defer resp.Body.Close()
io.Copy(output, resp.Body)
bot.sendPhoto(NewPhotoUpload(message.Chat.Id, output.Name()))
}
}

@ -1,76 +0,0 @@
package main
import (
"bytes"
"log"
)
type HelpPlugin struct {
}
func (plugin *HelpPlugin) GetName() string {
return "Plugins help"
}
func (plugin *HelpPlugin) GetCommands() []string {
return []string{"/help"}
}
func (plugin *HelpPlugin) GetHelpText() []string {
return []string{"/help (/command) - returns help about a command"}
}
func (plugin *HelpPlugin) Setup() {
}
func (plugin *HelpPlugin) GotCommand(command string, message Message, args []string) {
msg := NewMessage(message.Chat.Id, "")
msg.ReplyToMessageId = message.MessageId
msg.DisableWebPagePreview = true
var buffer bytes.Buffer
if len(args) > 0 {
for _, plug := range plugins {
for _, cmd := range plug.GetCommands() {
log.Println(cmd)
log.Println(args[0])
log.Println(args[0][1:])
if cmd == args[0] || cmd[1:] == args[0] {
buffer.WriteString(plug.GetName())
buffer.WriteString("\n")
for _, help := range plug.GetHelpText() {
buffer.WriteString(" ")
buffer.WriteString(help)
buffer.WriteString("\n")
}
}
}
}
} else {
buffer.WriteString(config.Plugins["about_text"])
buffer.WriteString("\n\n")
for _, plug := range plugins {
val, _ := config.EnabledPlugins[plugin.GetName()]
buffer.WriteString(plug.GetName())
if !val {
buffer.WriteString(" (disabled)")
}
buffer.WriteString("\n")
for _, cmd := range plug.GetHelpText() {
buffer.WriteString(" ")
buffer.WriteString(cmd)
buffer.WriteString("\n")
}
buffer.WriteString("\n")
}
}
msg.Text = buffer.String()
bot.sendMessage(msg)
}

@ -1,153 +0,0 @@
package main
import (
"fmt"
"log"
"strings"
)
type ManagePlugin struct {
}
func (plugin *ManagePlugin) GetName() string {
return "Plugin manager"
}
func (plugin *ManagePlugin) GetCommands() []string {
return []string{
"/enable",
"Enable",
"/disable",
"Disable",
"/reload",
}
}
func (plugin *ManagePlugin) GetHelpText() []string {
return []string{
"/enable [name] - enables a plugin",
"/disable [name] - disables a plugin",
"/reload - reloads bot configuration",
}
}
func (plugin *ManagePlugin) Setup() {
}
func (plugin *ManagePlugin) GotCommand(command string, message Message, args []string) {
log.Println(command)
if command == "/enable" {
keyboard := [][]string{}
hasDisabled := false
for _, plug := range plugins {
enabled, _ := config.EnabledPlugins[plug.GetName()]
if enabled {
continue
}
hasDisabled = true
keyboard = append(keyboard, []string{"Enable " + plug.GetName()})
}
if !hasDisabled {
msg := NewMessage(message.Chat.Id, "All plugins are enabled!")
msg.ReplyToMessageId = message.MessageId
bot.sendMessage(msg)
return
}
msg := NewMessage(message.Chat.Id, "Please specify which plugin to enable")
msg.ReplyToMessageId = message.MessageId
msg.ReplyMarkup = ReplyKeyboardMarkup{
Keyboard: keyboard,
OneTimeKeyboard: true,
Selective: true,
ResizeKeyboard: true,
}
bot.sendMessage(msg)
} else if command == "Enable" {
pluginName := strings.SplitN(message.Text, " ", 2)
msg := NewMessage(message.Chat.Id, "")
msg.ReplyToMessageId = message.MessageId
msg.ReplyMarkup = ReplyKeyboardHide{
HideKeyboard: true,
Selective: true,
}
_, ok := config.EnabledPlugins[pluginName[1]]
if !ok {
msg.Text = "Unknown plugin!"
msg.ReplyToMessageId = message.MessageId
bot.sendMessage(msg)
return
}
config.EnabledPlugins[pluginName[1]] = true
msg.Text = fmt.Sprintf("Enabled '%s'!", pluginName[1])
bot.sendMessage(msg)
} else if command == "/disable" {
keyboard := [][]string{}
hasEnabled := false
for _, plug := range plugins {
enabled, _ := config.EnabledPlugins[plug.GetName()]
if !enabled {
continue
}
hasEnabled = true
keyboard = append(keyboard, []string{"Disable " + plug.GetName()})
}
if !hasEnabled {
msg := NewMessage(message.Chat.Id, "All plugins are disabled!")
msg.ReplyToMessageId = message.MessageId
bot.sendMessage(msg)
return
}
msg := NewMessage(message.Chat.Id, "Please specify which plugin to disable")
msg.ReplyToMessageId = message.MessageId
msg.ReplyMarkup = ReplyKeyboardMarkup{
Keyboard: keyboard,
OneTimeKeyboard: true,
Selective: true,
ResizeKeyboard: true,
}
bot.sendMessage(msg)
} else if command == "Disable" {
pluginName := strings.SplitN(message.Text, " ", 2)
msg := NewMessage(message.Chat.Id, "")
msg.ReplyToMessageId = message.MessageId
msg.ReplyMarkup = ReplyKeyboardHide{
HideKeyboard: true,
Selective: true,
}
_, ok := config.EnabledPlugins[pluginName[1]]
if !ok {
msg.Text = "Unknown plugin!"
msg.ReplyToMessageId = message.MessageId
bot.sendMessage(msg)
return
}
config.EnabledPlugins[pluginName[1]] = false
msg.Text = fmt.Sprintf("Disabled '%s'!", pluginName[1])
bot.sendMessage(msg)
}
saveConfig()
}

@ -1,4 +1,4 @@
package main package tgbotapi
import ( import (
"encoding/json" "encoding/json"

@ -0,0 +1,22 @@
package tgbotapi
func (bot *BotApi) UpdatesChan(config UpdateConfig) (chan Update, error) {
bot.Updates = make(chan Update, 100)
go func() {
updates, err := bot.GetUpdates(config)
if err != nil {
panic(err)
}
for _, update := range updates {
if update.UpdateId > config.Offset {
config.Offset = update.UpdateId + 1
}
bot.Updates <- update
}
}()
return bot.Updates, nil
}