change focus from bot to just bindings, code cleanup

bot-api-6.1
Syfaro 2015-06-25 23:26:24 -05:00
parent 5d6f84e9b2
commit 9cf4f13772
9 changed files with 728 additions and 1188 deletions

View File

@ -1,54 +1,6 @@
# Golang Telegram bot using the Bot API
A simple Golang bot for the Telegram Bot API
# Golang Telegram bindings for the 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.
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
View File

@ -1,128 +1,13 @@
package main
package tgbotapi
import (
"encoding/json"
"flag"
"fmt"
"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 BotApi struct {
Token string `json:"token"`
Debug bool `json:"debug"`
Updates chan Update `json:"-"`
}
type Plugin interface {
GetName() string
GetCommands() []string
GetHelpText() []string
GotCommand(string, Message, []string)
Setup()
}
var bot *BotApi
var plugins []Plugin
var config Config
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 NewBotApi(token string) *BotApi {
return &BotApi{
Token: token,
}
}
func saveConfig() {
data, _ := json.MarshalIndent(config, "", " ")
ioutil.WriteFile(*configPath, data, 0600)
}

144
helpers.go 100644
View File

@ -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,
}
}

1237
methods.go

File diff suppressed because it is too large Load Diff

View File

@ -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()))
}
}

View File

@ -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)
}

View File

@ -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()
}

View File

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

22
updates.go 100644
View File

@ -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
}