Add agent => bot
This commit is contained in:
parent
b4874806df
commit
7faab93f9e
7
agent/.travis.yml
Normal file
7
agent/.travis.yml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.10.x
|
||||||
|
- 1.11.x
|
||||||
|
notifications:
|
||||||
|
slack:
|
||||||
|
secure: j+1gw9+LEu6GNisO18UoYOT8x9SGGoy57xJTmSQcWLGmhlcRwdvRzBJTdnZQ/sItokJSufW4K4cVZy4749fq7EaHqMtcuFkwju/i5EVOHliOlTqvo6bZaCThvuSfknBmHK0oU3u0DZBDAqR7BGTRztB+8A4gUNTVYumkP1sVWqoF8lvkM276Il1oprWeRyAdCpBNDuiUlMC0SpJmfUKrqpE64xe1iOCedPTPj7cbgnl35NBll0poXPIp/XNYn4ZsNP100vb5LSyvGHO5Z5XB3LZJit3wNWB3zI9y5Dr4d2EAV/c1VWqalbbT0/CLvSEnP++ubZhnK7sTMhgYxIlMtNTT/81MplVTeKcS+UFBw/aeTzrjhx6g5FNQiN2cnurs/O0Xwoq0Oh01l4uTIU4xUGQFtba/WFPDAO7cEWk8T1Z/fjkD22bDvkm3C2XXsA5nXeIjLhODKRV2fc5/Slxi2p5znhVRFukxnvPh0vVWh8yeukyjYjQXQ2n7Cl+UDBlggWGtRAYOnNo1uhLPShVoJwc/NjnRX5vJPbnjkASkEutqWAw/Rmwh+wx5+WNiYjo/VE8ZJPKyRK+0dQqhOrP73HWJxO9sMFucymeFVcxa4z3B7Nn7TBo1BhCyAdohT9V19gqfRO+TA8kifz8BF8jWBYUBOSQZrg92bYGkia3ROMs=
|
197
agent/README.md
Normal file
197
agent/README.md
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
# Agent
|
||||||
|
|
||||||
|
Agent is a library used to create commands, inputs and robot services
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
- [Commands](#commands) - Commands are functions executed by the bot based on text based pattern matching.
|
||||||
|
- [Inputs](#inputs) - Inputs are plugins for communication e.g Slack, Telegram, IRC, etc.
|
||||||
|
- [Services](#services) - Write bots as micro services
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
Commands are functions executed by the bot based on text based pattern matching.
|
||||||
|
|
||||||
|
### Write a Command
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/micro/go-bot/command"
|
||||||
|
|
||||||
|
func Ping() command.Command {
|
||||||
|
usage := "ping"
|
||||||
|
description := "Returns pong"
|
||||||
|
|
||||||
|
return command.NewCommand("ping", usage, desc, func(args ...string) ([]byte, error) {
|
||||||
|
return []byte("pong"), nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Register the command
|
||||||
|
|
||||||
|
Add the command to the Commands map with a pattern key that can be matched by golang/regexp.Match
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/micro/go-bot/command"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
command.Commands["^ping$"] = Ping()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rebuild Micro
|
||||||
|
|
||||||
|
Build binary
|
||||||
|
```shell
|
||||||
|
cd github.com/micro/micro
|
||||||
|
|
||||||
|
// For local use
|
||||||
|
go build -i -o micro ./main.go
|
||||||
|
|
||||||
|
// For docker image
|
||||||
|
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-w' -i -o micro ./main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
Inputs are plugins for communication e.g Slack, HipChat, XMPP, IRC, SMTP, etc, etc.
|
||||||
|
|
||||||
|
New inputs can be added in the following way.
|
||||||
|
|
||||||
|
### Write an Input
|
||||||
|
|
||||||
|
Write an input that satisfies the Input interface.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Input interface {
|
||||||
|
// Provide cli flags
|
||||||
|
Flags() []cli.Flag
|
||||||
|
// Initialise input using cli context
|
||||||
|
Init(*cli.Context) error
|
||||||
|
// Stream events from the input
|
||||||
|
Stream() (Conn, error)
|
||||||
|
// Start the input
|
||||||
|
Start() error
|
||||||
|
// Stop the input
|
||||||
|
Stop() error
|
||||||
|
// name of the input
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Register the input
|
||||||
|
|
||||||
|
Add the input to the Inputs map.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/micro/micro/bot/input"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
input.Inputs["name"] = MyInput
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rebuild Micro
|
||||||
|
|
||||||
|
Build binary
|
||||||
|
```shell
|
||||||
|
cd github.com/micro/micro
|
||||||
|
|
||||||
|
// For local use
|
||||||
|
go build -i -o micro ./main.go
|
||||||
|
|
||||||
|
// For docker image
|
||||||
|
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-w' -i -o micro ./main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
## Services
|
||||||
|
|
||||||
|
The micro bot supports the ability to create commands as micro services.
|
||||||
|
|
||||||
|
### How does it work?
|
||||||
|
|
||||||
|
The bot watches the service registry for services with it's namespace. The default namespace is `go.micro.bot`.
|
||||||
|
Any service within this namespace will automatically be added to the list of available commands. When a command
|
||||||
|
is executed, the bot will call the service with method `Command.Exec`. It also expects the method `Command.Help`
|
||||||
|
to exist for usage info.
|
||||||
|
|
||||||
|
|
||||||
|
The service interface is as follows and can be found at [go-bot/proto](https://github.com/micro/go-bot/blob/master/proto/bot.proto)
|
||||||
|
|
||||||
|
```
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package go.micro.bot;
|
||||||
|
|
||||||
|
service Command {
|
||||||
|
rpc Help(HelpRequest) returns (HelpResponse) {};
|
||||||
|
rpc Exec(ExecRequest) returns (ExecResponse) {};
|
||||||
|
}
|
||||||
|
|
||||||
|
message HelpRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
message HelpResponse {
|
||||||
|
string usage = 1;
|
||||||
|
string description = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExecRequest {
|
||||||
|
repeated string args = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExecResponse {
|
||||||
|
bytes result = 1;
|
||||||
|
string error = 2;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
Here's an example echo command as a microservice
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/micro/go-micro"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
proto "github.com/micro/go-bot/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Command struct{}
|
||||||
|
|
||||||
|
// Help returns the command usage
|
||||||
|
func (c *Command) Help(ctx context.Context, req *proto.HelpRequest, rsp *proto.HelpResponse) error {
|
||||||
|
// Usage should include the name of the command
|
||||||
|
rsp.Usage = "echo"
|
||||||
|
rsp.Description = "This is an example bot command as a micro service which echos the message"
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes the command
|
||||||
|
func (c *Command) Exec(ctx context.Context, req *proto.ExecRequest, rsp *proto.ExecResponse) error {
|
||||||
|
rsp.Result = []byte(strings.Join(req.Args, " "))
|
||||||
|
// rsp.Error could be set to return an error instead
|
||||||
|
// the function error would only be used for service level issues
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
service := micro.NewService(
|
||||||
|
micro.Name("go.micro.bot.echo"),
|
||||||
|
)
|
||||||
|
|
||||||
|
service.Init()
|
||||||
|
|
||||||
|
proto.RegisterCommandHandler(service.Server(), new(Command))
|
||||||
|
|
||||||
|
if err := service.Run(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
2
agent/agent.go
Normal file
2
agent/agent.go
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// Package agent provides an interface for building robots
|
||||||
|
package agent
|
54
agent/command/command.go
Normal file
54
agent/command/command.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Package command is an interface for defining bot commands
|
||||||
|
package command
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Commmands keyed by golang/regexp patterns
|
||||||
|
// regexp.Match(key, input) is used to match
|
||||||
|
Commands = map[string]Command{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Command is the interface for specific named
|
||||||
|
// commands executed via plugins or the bot.
|
||||||
|
type Command interface {
|
||||||
|
// Executes the command with args passed in
|
||||||
|
Exec(args ...string) ([]byte, error)
|
||||||
|
// Usage of the command
|
||||||
|
Usage() string
|
||||||
|
// Description of the command
|
||||||
|
Description() string
|
||||||
|
// Name of the command
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type cmd struct {
|
||||||
|
name string
|
||||||
|
usage string
|
||||||
|
description string
|
||||||
|
exec func(args ...string) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmd) Description() string {
|
||||||
|
return c.description
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmd) Exec(args ...string) ([]byte, error) {
|
||||||
|
return c.exec(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmd) Usage() string {
|
||||||
|
return c.usage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmd) String() string {
|
||||||
|
return c.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCommand helps quickly create a new command
|
||||||
|
func NewCommand(name, usage, description string, exec func(args ...string) ([]byte, error)) Command {
|
||||||
|
return &cmd{
|
||||||
|
name: name,
|
||||||
|
usage: usage,
|
||||||
|
description: description,
|
||||||
|
exec: exec,
|
||||||
|
}
|
||||||
|
}
|
65
agent/command/command_test.go
Normal file
65
agent/command/command_test.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommand(t *testing.T) {
|
||||||
|
c := &cmd{
|
||||||
|
name: "test",
|
||||||
|
usage: "test usage",
|
||||||
|
description: "test description",
|
||||||
|
exec: func(args ...string) ([]byte, error) {
|
||||||
|
return []byte("test"), nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.String() != c.name {
|
||||||
|
t.Fatalf("expected name %s got %s", c.name, c.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Usage() != c.usage {
|
||||||
|
t.Fatalf("expected usage %s got %s", c.usage, c.Usage())
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Description() != c.description {
|
||||||
|
t.Fatalf("expected description %s got %s", c.description, c.Description())
|
||||||
|
}
|
||||||
|
|
||||||
|
if r, err := c.Exec(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if string(r) != "test" {
|
||||||
|
t.Fatalf("expected exec result test got %s", string(r))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewCommand(t *testing.T) {
|
||||||
|
c := &cmd{
|
||||||
|
name: "test",
|
||||||
|
usage: "test usage",
|
||||||
|
description: "test description",
|
||||||
|
exec: func(args ...string) ([]byte, error) {
|
||||||
|
return []byte("test"), nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
nc := NewCommand(c.name, c.usage, c.description, c.exec)
|
||||||
|
|
||||||
|
if nc.String() != c.name {
|
||||||
|
t.Fatalf("expected name %s got %s", c.name, nc.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if nc.Usage() != c.usage {
|
||||||
|
t.Fatalf("expected usage %s got %s", c.usage, nc.Usage())
|
||||||
|
}
|
||||||
|
|
||||||
|
if nc.Description() != c.description {
|
||||||
|
t.Fatalf("expected description %s got %s", c.description, nc.Description())
|
||||||
|
}
|
||||||
|
|
||||||
|
if r, err := nc.Exec(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if string(r) != "test" {
|
||||||
|
t.Fatalf("expected exec result test got %s", string(r))
|
||||||
|
}
|
||||||
|
}
|
22
agent/input/discord/README.md
Normal file
22
agent/input/discord/README.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# Discord input for micro-bot
|
||||||
|
[Discord](https://discordapp.com) support for micro bot based on [discordgo](github.com/bwmarrin/discordgo).
|
||||||
|
|
||||||
|
This was originally written by Aleksandr Tihomirov (@zet4) and can be found at https://github.com/zet4/micro-misc/.
|
||||||
|
|
||||||
|
## Options
|
||||||
|
### discord_token
|
||||||
|
|
||||||
|
You have to supply an application token via `--discord_token`.
|
||||||
|
|
||||||
|
Head over to Discord's [developer introduction](https://discordapp.com/developers/docs/intro)
|
||||||
|
to learn how to create applications and how the API works.
|
||||||
|
|
||||||
|
### discord_prefix
|
||||||
|
|
||||||
|
Set a command prefix with `--discord_prefix`. The default prefix is `Micro `.
|
||||||
|
You can mention the bot or use the prefix to run a command.
|
||||||
|
|
||||||
|
### discord_whitelist
|
||||||
|
|
||||||
|
Pass a list of comma-separated user IDs with `--discord_whitelist`. Only allow
|
||||||
|
these users to use the bot.
|
94
agent/input/discord/conn.go
Normal file
94
agent/input/discord/conn.go
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package discord
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"github.com/micro/go-micro/agent/input"
|
||||||
|
"github.com/micro/go-log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type discordConn struct {
|
||||||
|
master *discordInput
|
||||||
|
exit chan struct{}
|
||||||
|
recv chan *discordgo.Message
|
||||||
|
|
||||||
|
sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConn(master *discordInput) *discordConn {
|
||||||
|
conn := &discordConn{
|
||||||
|
master: master,
|
||||||
|
exit: make(chan struct{}),
|
||||||
|
recv: make(chan *discordgo.Message),
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.master.session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||||
|
if m.Author.ID == master.botID {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
whitelisted := false
|
||||||
|
for _, ID := range conn.master.whitelist {
|
||||||
|
if m.Author.ID == ID {
|
||||||
|
whitelisted = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(master.whitelist) > 0 && !whitelisted {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var valid bool
|
||||||
|
m.Message.Content, valid = conn.master.prefixfn(m.Message.Content)
|
||||||
|
if !valid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.recv <- m.Message
|
||||||
|
})
|
||||||
|
|
||||||
|
return conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *discordConn) Recv(event *input.Event) error {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-dc.exit:
|
||||||
|
return errors.New("connection closed")
|
||||||
|
case msg := <-dc.recv:
|
||||||
|
|
||||||
|
event.From = msg.ChannelID + ":" + msg.Author.ID
|
||||||
|
event.To = dc.master.botID
|
||||||
|
event.Type = input.TextEvent
|
||||||
|
event.Data = []byte(msg.Content)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *discordConn) Send(e *input.Event) error {
|
||||||
|
fields := strings.Split(e.To, ":")
|
||||||
|
_, err := dc.master.session.ChannelMessageSend(fields[0], string(e.Data))
|
||||||
|
if err != nil {
|
||||||
|
log.Log("[bot][loop][send]", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *discordConn) Close() error {
|
||||||
|
if err := dc.master.session.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-dc.exit:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
close(dc.exit)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
153
agent/input/discord/discord.go
Normal file
153
agent/input/discord/discord.go
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package discord
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"github.com/micro/cli"
|
||||||
|
"github.com/micro/go-micro/agent/input"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
input.Inputs["discord"] = newInput()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInput() *discordInput {
|
||||||
|
return &discordInput{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type discordInput struct {
|
||||||
|
token string
|
||||||
|
whitelist []string
|
||||||
|
prefix string
|
||||||
|
prefixfn func(string) (string, bool)
|
||||||
|
botID string
|
||||||
|
|
||||||
|
session *discordgo.Session
|
||||||
|
|
||||||
|
sync.Mutex
|
||||||
|
running bool
|
||||||
|
exit chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *discordInput) Flags() []cli.Flag {
|
||||||
|
return []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "discord_token",
|
||||||
|
EnvVar: "MICRO_DISCORD_TOKEN",
|
||||||
|
Usage: "Discord token (prefix with Bot if it's for bot account)",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "discord_whitelist",
|
||||||
|
EnvVar: "MICRO_DISCORD_WHITELIST",
|
||||||
|
Usage: "Discord Whitelist (seperated by ,)",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "discord_prefix",
|
||||||
|
Usage: "Discord Prefix",
|
||||||
|
EnvVar: "MICRO_DISCORD_PREFIX",
|
||||||
|
Value: "Micro ",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *discordInput) Init(ctx *cli.Context) error {
|
||||||
|
token := ctx.String("discord_token")
|
||||||
|
whitelist := ctx.String("discord_whitelist")
|
||||||
|
prefix := ctx.String("discord_prefix")
|
||||||
|
|
||||||
|
if len(token) == 0 {
|
||||||
|
return errors.New("require token")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.token = token
|
||||||
|
d.prefix = prefix
|
||||||
|
|
||||||
|
if len(whitelist) > 0 {
|
||||||
|
d.whitelist = strings.Split(whitelist, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *discordInput) Start() error {
|
||||||
|
if len(d.token) == 0 {
|
||||||
|
return errors.New("missing discord configuration")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Lock()
|
||||||
|
defer d.Unlock()
|
||||||
|
|
||||||
|
if d.running {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
d.session, err = discordgo.New(d.token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := d.session.User("@me")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.botID = u.ID
|
||||||
|
d.prefixfn = CheckPrefixFactory(fmt.Sprintf("<@%s> ", d.botID), fmt.Sprintf("<@!%s> ", d.botID), d.prefix)
|
||||||
|
|
||||||
|
d.exit = make(chan struct{})
|
||||||
|
d.running = true
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *discordInput) Stream() (input.Conn, error) {
|
||||||
|
d.Lock()
|
||||||
|
defer d.Unlock()
|
||||||
|
if !d.running {
|
||||||
|
return nil, errors.New("not running")
|
||||||
|
}
|
||||||
|
|
||||||
|
//Fire-n-forget close just in case...
|
||||||
|
d.session.Close()
|
||||||
|
|
||||||
|
conn := newConn(d)
|
||||||
|
if err := d.session.Open(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *discordInput) Stop() error {
|
||||||
|
d.Lock()
|
||||||
|
defer d.Unlock()
|
||||||
|
|
||||||
|
if !d.running {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
close(d.exit)
|
||||||
|
d.running = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *discordInput) String() string {
|
||||||
|
return "discord"
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckPrefixFactory Creates a prefix checking function and stuff.
|
||||||
|
func CheckPrefixFactory(prefixes ...string) func(string) (string, bool) {
|
||||||
|
return func(content string) (string, bool) {
|
||||||
|
for _, prefix := range prefixes {
|
||||||
|
if strings.HasPrefix(content, prefix) {
|
||||||
|
return strings.TrimPrefix(content, prefix), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
55
agent/input/input.go
Normal file
55
agent/input/input.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Package input is an interface for bot inputs
|
||||||
|
package input
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/micro/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EventType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
TextEvent EventType = "text"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Inputs keyed by name
|
||||||
|
// Example slack or hipchat
|
||||||
|
Inputs = map[string]Input{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Event is the unit sent and received
|
||||||
|
type Event struct {
|
||||||
|
Type EventType
|
||||||
|
From string
|
||||||
|
To string
|
||||||
|
Data []byte
|
||||||
|
Meta map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input is an interface for sources which
|
||||||
|
// provide a way to communicate with the bot.
|
||||||
|
// Slack, HipChat, XMPP, etc.
|
||||||
|
type Input interface {
|
||||||
|
// Provide cli flags
|
||||||
|
Flags() []cli.Flag
|
||||||
|
// Initialise input using cli context
|
||||||
|
Init(*cli.Context) error
|
||||||
|
// Stream events from the input
|
||||||
|
Stream() (Conn, error)
|
||||||
|
// Start the input
|
||||||
|
Start() error
|
||||||
|
// Stop the input
|
||||||
|
Stop() error
|
||||||
|
// name of the input
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conn interface provides a way to
|
||||||
|
// send and receive events. Send and
|
||||||
|
// Recv both block until succeeding
|
||||||
|
// or failing.
|
||||||
|
type Conn interface {
|
||||||
|
Close() error
|
||||||
|
Recv(*Event) error
|
||||||
|
Send(*Event) error
|
||||||
|
}
|
160
agent/input/slack/conn.go
Normal file
160
agent/input/slack/conn.go
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
package slack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/micro/go-micro/agent/input"
|
||||||
|
"github.com/nlopes/slack"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Satisfies the input.Conn interface
|
||||||
|
type slackConn struct {
|
||||||
|
auth *slack.AuthTestResponse
|
||||||
|
rtm *slack.RTM
|
||||||
|
exit chan bool
|
||||||
|
|
||||||
|
sync.Mutex
|
||||||
|
names map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slackConn) run() {
|
||||||
|
// func retrieves user names and maps to IDs
|
||||||
|
setNames := func() {
|
||||||
|
names := make(map[string]string)
|
||||||
|
users, err := s.rtm.Client.GetUsers()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, user := range users {
|
||||||
|
names[user.ID] = user.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
s.names = names
|
||||||
|
s.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
setNames()
|
||||||
|
|
||||||
|
t := time.NewTicker(time.Minute)
|
||||||
|
defer t.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.exit:
|
||||||
|
return
|
||||||
|
case <-t.C:
|
||||||
|
setNames()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slackConn) getName(id string) string {
|
||||||
|
s.Lock()
|
||||||
|
name := s.names[id]
|
||||||
|
s.Unlock()
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slackConn) Close() error {
|
||||||
|
select {
|
||||||
|
case <-s.exit:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
close(s.exit)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slackConn) Recv(event *input.Event) error {
|
||||||
|
if event == nil {
|
||||||
|
return errors.New("event cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.exit:
|
||||||
|
return errors.New("connection closed")
|
||||||
|
case e := <-s.rtm.IncomingEvents:
|
||||||
|
switch ev := e.Data.(type) {
|
||||||
|
case *slack.MessageEvent:
|
||||||
|
// only accept type message
|
||||||
|
if ev.Type != "message" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// only accept DMs or messages to me
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(ev.Channel, "D"):
|
||||||
|
case strings.HasPrefix(ev.Text, s.auth.User):
|
||||||
|
case strings.HasPrefix(ev.Text, fmt.Sprintf("<@%s>", s.auth.UserID)):
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip username from text
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(ev.Text, s.auth.User):
|
||||||
|
args := strings.Split(ev.Text, " ")[1:]
|
||||||
|
ev.Text = strings.Join(args, " ")
|
||||||
|
event.To = s.auth.User
|
||||||
|
case strings.HasPrefix(ev.Text, fmt.Sprintf("<@%s>", s.auth.UserID)):
|
||||||
|
args := strings.Split(ev.Text, " ")[1:]
|
||||||
|
ev.Text = strings.Join(args, " ")
|
||||||
|
event.To = s.auth.UserID
|
||||||
|
}
|
||||||
|
|
||||||
|
if event.Meta == nil {
|
||||||
|
event.Meta = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill in the blanks
|
||||||
|
event.From = ev.Channel + ":" + ev.User
|
||||||
|
event.Type = input.TextEvent
|
||||||
|
event.Data = []byte(ev.Text)
|
||||||
|
event.Meta["reply"] = ev
|
||||||
|
return nil
|
||||||
|
case *slack.InvalidAuthEvent:
|
||||||
|
return errors.New("invalid credentials")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slackConn) Send(event *input.Event) error {
|
||||||
|
var channel, message, name string
|
||||||
|
|
||||||
|
if len(event.To) == 0 {
|
||||||
|
return errors.New("require Event.To")
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(event.To, ":")
|
||||||
|
|
||||||
|
if len(parts) == 2 {
|
||||||
|
channel = parts[0]
|
||||||
|
name = s.getName(parts[1])
|
||||||
|
// try using reply meta
|
||||||
|
} else if ev, ok := event.Meta["reply"]; ok {
|
||||||
|
channel = ev.(*slack.MessageEvent).Channel
|
||||||
|
name = s.getName(ev.(*slack.MessageEvent).User)
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't know where to send the message
|
||||||
|
if len(channel) == 0 {
|
||||||
|
return errors.New("could not determine who message is to")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(name) == 0 || strings.HasPrefix(channel, "D") {
|
||||||
|
message = string(event.Data)
|
||||||
|
} else {
|
||||||
|
message = fmt.Sprintf("@%s: %s", name, string(event.Data))
|
||||||
|
}
|
||||||
|
|
||||||
|
s.rtm.SendMessage(s.rtm.NewOutgoingMessage(message, channel))
|
||||||
|
return nil
|
||||||
|
}
|
147
agent/input/slack/slack.go
Normal file
147
agent/input/slack/slack.go
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
package slack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/micro/cli"
|
||||||
|
"github.com/micro/go-micro/agent/input"
|
||||||
|
"github.com/nlopes/slack"
|
||||||
|
)
|
||||||
|
|
||||||
|
type slackInput struct {
|
||||||
|
debug bool
|
||||||
|
token string
|
||||||
|
|
||||||
|
sync.Mutex
|
||||||
|
running bool
|
||||||
|
exit chan bool
|
||||||
|
|
||||||
|
api *slack.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
input.Inputs["slack"] = NewInput()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *slackInput) Flags() []cli.Flag {
|
||||||
|
return []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "slack_debug",
|
||||||
|
Usage: "Slack debug output",
|
||||||
|
EnvVar: "MICRO_SLACK_DEBUG",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "slack_token",
|
||||||
|
Usage: "Slack token",
|
||||||
|
EnvVar: "MICRO_SLACK_TOKEN",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *slackInput) Init(ctx *cli.Context) error {
|
||||||
|
debug := ctx.Bool("slack_debug")
|
||||||
|
token := ctx.String("slack_token")
|
||||||
|
|
||||||
|
if len(token) == 0 {
|
||||||
|
return errors.New("missing slack token")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.debug = debug
|
||||||
|
p.token = token
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *slackInput) Stream() (input.Conn, error) {
|
||||||
|
p.Lock()
|
||||||
|
defer p.Unlock()
|
||||||
|
|
||||||
|
if !p.running {
|
||||||
|
return nil, errors.New("not running")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test auth
|
||||||
|
auth, err := p.api.AuthTest()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rtm := p.api.NewRTM()
|
||||||
|
exit := make(chan bool)
|
||||||
|
|
||||||
|
go rtm.ManageConnection()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-p.exit:
|
||||||
|
select {
|
||||||
|
case <-exit:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
close(exit)
|
||||||
|
}
|
||||||
|
case <-exit:
|
||||||
|
}
|
||||||
|
|
||||||
|
rtm.Disconnect()
|
||||||
|
}()
|
||||||
|
|
||||||
|
conn := &slackConn{
|
||||||
|
auth: auth,
|
||||||
|
rtm: rtm,
|
||||||
|
exit: exit,
|
||||||
|
names: make(map[string]string),
|
||||||
|
}
|
||||||
|
|
||||||
|
go conn.run()
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *slackInput) Start() error {
|
||||||
|
if len(p.token) == 0 {
|
||||||
|
return errors.New("missing slack token")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Lock()
|
||||||
|
defer p.Unlock()
|
||||||
|
|
||||||
|
if p.running {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
api := slack.New(p.token, slack.OptionDebug(p.debug))
|
||||||
|
|
||||||
|
// test auth
|
||||||
|
_, err := api.AuthTest()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.api = api
|
||||||
|
p.exit = make(chan bool)
|
||||||
|
p.running = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *slackInput) Stop() error {
|
||||||
|
p.Lock()
|
||||||
|
defer p.Unlock()
|
||||||
|
|
||||||
|
if !p.running {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
close(p.exit)
|
||||||
|
p.running = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *slackInput) String() string {
|
||||||
|
return "slack"
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInput() input.Input {
|
||||||
|
return &slackInput{}
|
||||||
|
}
|
18
agent/input/telegram/README.md
Normal file
18
agent/input/telegram/README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Telegram Messenger input for micro bot
|
||||||
|
[Telegram](https://telegram.org) support for micro bot based on [telegram-bot-api](https://github.com/go-telegram-bot-api/telegram-bot-api).
|
||||||
|
|
||||||
|
## Options
|
||||||
|
### --telegram_token (required)
|
||||||
|
|
||||||
|
Sets bot's token for interacting with API.
|
||||||
|
|
||||||
|
Head over to Telegram's [API documentation](https://core.telegram.org/bots/api)
|
||||||
|
to learn how to create bots and how the API works.
|
||||||
|
|
||||||
|
### --telegram_debug
|
||||||
|
|
||||||
|
Sets the debug flag to make the bot's output verbose.
|
||||||
|
|
||||||
|
### --telegram_whitelist
|
||||||
|
|
||||||
|
Sets a list of comma-separated nicknames (without @ symbol in the beginning) for interacting with bot. Only these users can use the bot.
|
115
agent/input/telegram/conn.go
Normal file
115
agent/input/telegram/conn.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
package telegram
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/forestgiant/sliceutil"
|
||||||
|
"github.com/micro/go-micro/agent/input"
|
||||||
|
"github.com/micro/go-log"
|
||||||
|
"gopkg.in/telegram-bot-api.v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
type telegramConn struct {
|
||||||
|
input *telegramInput
|
||||||
|
|
||||||
|
recv <-chan tgbotapi.Update
|
||||||
|
exit chan bool
|
||||||
|
|
||||||
|
syncCond *sync.Cond
|
||||||
|
mutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConn(input *telegramInput) (*telegramConn, error) {
|
||||||
|
conn := &telegramConn{
|
||||||
|
input: input,
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.syncCond = sync.NewCond(&conn.mutex)
|
||||||
|
|
||||||
|
go conn.run()
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *telegramConn) run() {
|
||||||
|
u := tgbotapi.NewUpdate(0)
|
||||||
|
u.Timeout = 60
|
||||||
|
updates, err := tc.input.api.GetUpdatesChan(u)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tc.recv = updates
|
||||||
|
tc.syncCond.Signal()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-tc.exit:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *telegramConn) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *telegramConn) Recv(event *input.Event) error {
|
||||||
|
if event == nil {
|
||||||
|
return errors.New("event cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
if tc.recv == nil {
|
||||||
|
tc.mutex.Lock()
|
||||||
|
tc.syncCond.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
update := <-tc.recv
|
||||||
|
|
||||||
|
if update.Message == nil || (len(tc.input.whitelist) > 0 && !sliceutil.Contains(tc.input.whitelist, update.Message.From.UserName)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if event.Meta == nil {
|
||||||
|
event.Meta = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
event.Type = input.TextEvent
|
||||||
|
event.From = update.Message.From.UserName
|
||||||
|
event.To = tc.input.api.Self.UserName
|
||||||
|
event.Data = []byte(update.Message.Text)
|
||||||
|
event.Meta["chatId"] = update.Message.Chat.ID
|
||||||
|
event.Meta["chatType"] = update.Message.Chat.Type
|
||||||
|
event.Meta["messageId"] = update.Message.MessageID
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *telegramConn) Send(event *input.Event) error {
|
||||||
|
messageText := strings.TrimSpace(string(event.Data))
|
||||||
|
|
||||||
|
chatId := event.Meta["chatId"].(int64)
|
||||||
|
chatType := ChatType(event.Meta["chatType"].(string))
|
||||||
|
|
||||||
|
msgConfig := tgbotapi.NewMessage(chatId, messageText)
|
||||||
|
msgConfig.ParseMode = tgbotapi.ModeHTML
|
||||||
|
|
||||||
|
if sliceutil.Contains([]ChatType{Group, Supergroup}, chatType) {
|
||||||
|
msgConfig.ReplyToMessageID = event.Meta["messageId"].(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := tc.input.api.Send(msgConfig)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// probably it could be because of nested HTML tags -- telegram doesn't allow nested tags
|
||||||
|
log.Log("[telegram][Send] error:", err)
|
||||||
|
msgConfig.Text = "This bot couldn't send the response (Internal error)"
|
||||||
|
tc.input.api.Send(msgConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
101
agent/input/telegram/telegram.go
Normal file
101
agent/input/telegram/telegram.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package telegram
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/micro/cli"
|
||||||
|
"github.com/micro/go-micro/agent/input"
|
||||||
|
"gopkg.in/telegram-bot-api.v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
type telegramInput struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
debug bool
|
||||||
|
token string
|
||||||
|
whitelist []string
|
||||||
|
|
||||||
|
api *tgbotapi.BotAPI
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChatType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Private ChatType = "private"
|
||||||
|
Group ChatType = "group"
|
||||||
|
Supergroup ChatType = "supergroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
input.Inputs["telegram"] = &telegramInput{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ti *telegramInput) Flags() []cli.Flag {
|
||||||
|
return []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "telegram_debug",
|
||||||
|
EnvVar: "MICRO_TELEGRAM_DEBUG",
|
||||||
|
Usage: "Telegram debug output",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "telegram_token",
|
||||||
|
EnvVar: "MICRO_TELEGRAM_TOKEN",
|
||||||
|
Usage: "Telegram token",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "telegram_whitelist",
|
||||||
|
EnvVar: "MICRO_TELEGRAM_WHITELIST",
|
||||||
|
Usage: "Telegram bot's users (comma-separated values)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ti *telegramInput) Init(ctx *cli.Context) error {
|
||||||
|
ti.debug = ctx.Bool("telegram_debug")
|
||||||
|
ti.token = ctx.String("telegram_token")
|
||||||
|
|
||||||
|
whitelist := ctx.String("telegram_whitelist")
|
||||||
|
|
||||||
|
if whitelist != "" {
|
||||||
|
ti.whitelist = strings.Split(whitelist, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ti.token) == 0 {
|
||||||
|
return errors.New("missing telegram token")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ti *telegramInput) Stream() (input.Conn, error) {
|
||||||
|
ti.Lock()
|
||||||
|
defer ti.Unlock()
|
||||||
|
|
||||||
|
return newConn(ti)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ti *telegramInput) Start() error {
|
||||||
|
ti.Lock()
|
||||||
|
defer ti.Unlock()
|
||||||
|
|
||||||
|
api, err := tgbotapi.NewBotAPI(ti.token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ti.api = api
|
||||||
|
|
||||||
|
api.Debug = ti.debug
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ti *telegramInput) Stop() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *telegramInput) String() string {
|
||||||
|
return "telegram"
|
||||||
|
}
|
118
agent/proto/bot.micro.go
Normal file
118
agent/proto/bot.micro.go
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||||
|
// source: github.com/micro/go-bot/proto/bot.proto
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package go_micro_bot is a generated protocol buffer package.
|
||||||
|
|
||||||
|
It is generated from these files:
|
||||||
|
github.com/micro/go-bot/proto/bot.proto
|
||||||
|
|
||||||
|
It has these top-level messages:
|
||||||
|
HelpRequest
|
||||||
|
HelpResponse
|
||||||
|
ExecRequest
|
||||||
|
ExecResponse
|
||||||
|
*/
|
||||||
|
package go_micro_bot
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
import (
|
||||||
|
client "github.com/micro/go-micro/client"
|
||||||
|
server "github.com/micro/go-micro/server"
|
||||||
|
context "context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ context.Context
|
||||||
|
var _ client.Option
|
||||||
|
var _ server.Option
|
||||||
|
|
||||||
|
// Client API for Command service
|
||||||
|
|
||||||
|
type CommandService interface {
|
||||||
|
Help(ctx context.Context, in *HelpRequest, opts ...client.CallOption) (*HelpResponse, error)
|
||||||
|
Exec(ctx context.Context, in *ExecRequest, opts ...client.CallOption) (*ExecResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type commandService struct {
|
||||||
|
c client.Client
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCommandService(name string, c client.Client) CommandService {
|
||||||
|
if c == nil {
|
||||||
|
c = client.NewClient()
|
||||||
|
}
|
||||||
|
if len(name) == 0 {
|
||||||
|
name = "go.micro.bot"
|
||||||
|
}
|
||||||
|
return &commandService{
|
||||||
|
c: c,
|
||||||
|
name: name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *commandService) Help(ctx context.Context, in *HelpRequest, opts ...client.CallOption) (*HelpResponse, error) {
|
||||||
|
req := c.c.NewRequest(c.name, "Command.Help", in)
|
||||||
|
out := new(HelpResponse)
|
||||||
|
err := c.c.Call(ctx, req, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *commandService) Exec(ctx context.Context, in *ExecRequest, opts ...client.CallOption) (*ExecResponse, error) {
|
||||||
|
req := c.c.NewRequest(c.name, "Command.Exec", in)
|
||||||
|
out := new(ExecResponse)
|
||||||
|
err := c.c.Call(ctx, req, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server API for Command service
|
||||||
|
|
||||||
|
type CommandHandler interface {
|
||||||
|
Help(context.Context, *HelpRequest, *HelpResponse) error
|
||||||
|
Exec(context.Context, *ExecRequest, *ExecResponse) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterCommandHandler(s server.Server, hdlr CommandHandler, opts ...server.HandlerOption) error {
|
||||||
|
type _command interface {
|
||||||
|
Help(ctx context.Context, in *HelpRequest, out *HelpResponse) error
|
||||||
|
Exec(ctx context.Context, in *ExecRequest, out *ExecResponse) error
|
||||||
|
}
|
||||||
|
type Command struct {
|
||||||
|
_command
|
||||||
|
}
|
||||||
|
h := &commandHandler{hdlr}
|
||||||
|
return s.Handle(s.NewHandler(&Command{h}, opts...))
|
||||||
|
}
|
||||||
|
|
||||||
|
type commandHandler struct {
|
||||||
|
CommandHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *commandHandler) Help(ctx context.Context, in *HelpRequest, out *HelpResponse) error {
|
||||||
|
return h.CommandHandler.Help(ctx, in, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *commandHandler) Exec(ctx context.Context, in *ExecRequest, out *ExecResponse) error {
|
||||||
|
return h.CommandHandler.Exec(ctx, in, out)
|
||||||
|
}
|
210
agent/proto/bot.pb.go
Normal file
210
agent/proto/bot.pb.go
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// source: github.com/micro/go-bot/proto/bot.proto
|
||||||
|
|
||||||
|
package go_micro_bot
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
type HelpRequest struct {
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HelpRequest) Reset() { *m = HelpRequest{} }
|
||||||
|
func (m *HelpRequest) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*HelpRequest) ProtoMessage() {}
|
||||||
|
func (*HelpRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_bot_654832eab83ed4b5, []int{0}
|
||||||
|
}
|
||||||
|
func (m *HelpRequest) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_HelpRequest.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *HelpRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_HelpRequest.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *HelpRequest) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_HelpRequest.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *HelpRequest) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_HelpRequest.Size(m)
|
||||||
|
}
|
||||||
|
func (m *HelpRequest) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_HelpRequest.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_HelpRequest proto.InternalMessageInfo
|
||||||
|
|
||||||
|
type HelpResponse struct {
|
||||||
|
Usage string `protobuf:"bytes,1,opt,name=usage,proto3" json:"usage,omitempty"`
|
||||||
|
Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HelpResponse) Reset() { *m = HelpResponse{} }
|
||||||
|
func (m *HelpResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*HelpResponse) ProtoMessage() {}
|
||||||
|
func (*HelpResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_bot_654832eab83ed4b5, []int{1}
|
||||||
|
}
|
||||||
|
func (m *HelpResponse) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_HelpResponse.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *HelpResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_HelpResponse.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *HelpResponse) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_HelpResponse.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *HelpResponse) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_HelpResponse.Size(m)
|
||||||
|
}
|
||||||
|
func (m *HelpResponse) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_HelpResponse.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_HelpResponse proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *HelpResponse) GetUsage() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Usage
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HelpResponse) GetDescription() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Description
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExecRequest struct {
|
||||||
|
Args []string `protobuf:"bytes,1,rep,name=args,proto3" json:"args,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ExecRequest) Reset() { *m = ExecRequest{} }
|
||||||
|
func (m *ExecRequest) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*ExecRequest) ProtoMessage() {}
|
||||||
|
func (*ExecRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_bot_654832eab83ed4b5, []int{2}
|
||||||
|
}
|
||||||
|
func (m *ExecRequest) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_ExecRequest.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *ExecRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_ExecRequest.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *ExecRequest) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_ExecRequest.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *ExecRequest) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_ExecRequest.Size(m)
|
||||||
|
}
|
||||||
|
func (m *ExecRequest) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_ExecRequest.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_ExecRequest proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *ExecRequest) GetArgs() []string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Args
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExecResponse struct {
|
||||||
|
Result []byte `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"`
|
||||||
|
Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ExecResponse) Reset() { *m = ExecResponse{} }
|
||||||
|
func (m *ExecResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*ExecResponse) ProtoMessage() {}
|
||||||
|
func (*ExecResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_bot_654832eab83ed4b5, []int{3}
|
||||||
|
}
|
||||||
|
func (m *ExecResponse) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_ExecResponse.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *ExecResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_ExecResponse.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *ExecResponse) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_ExecResponse.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *ExecResponse) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_ExecResponse.Size(m)
|
||||||
|
}
|
||||||
|
func (m *ExecResponse) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_ExecResponse.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_ExecResponse proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *ExecResponse) GetResult() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.Result
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ExecResponse) GetError() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Error
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*HelpRequest)(nil), "go.micro.bot.HelpRequest")
|
||||||
|
proto.RegisterType((*HelpResponse)(nil), "go.micro.bot.HelpResponse")
|
||||||
|
proto.RegisterType((*ExecRequest)(nil), "go.micro.bot.ExecRequest")
|
||||||
|
proto.RegisterType((*ExecResponse)(nil), "go.micro.bot.ExecResponse")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterFile("github.com/micro/go-bot/proto/bot.proto", fileDescriptor_bot_654832eab83ed4b5)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileDescriptor_bot_654832eab83ed4b5 = []byte{
|
||||||
|
// 246 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x41, 0x4b, 0xc4, 0x30,
|
||||||
|
0x10, 0x85, 0xb7, 0xba, 0xae, 0xec, 0xb4, 0x5e, 0x82, 0x48, 0xdd, 0x53, 0xcd, 0xc5, 0xbd, 0x98,
|
||||||
|
0x82, 0x5e, 0x05, 0x0f, 0xa2, 0x78, 0xee, 0x3f, 0x68, 0xba, 0x43, 0x2c, 0x6c, 0x3b, 0x35, 0x99,
|
||||||
|
0x82, 0xff, 0xc1, 0x3f, 0x2d, 0x4d, 0x72, 0x08, 0xcb, 0xde, 0xe6, 0x65, 0x86, 0xf7, 0xbe, 0x17,
|
||||||
|
0x78, 0x34, 0x3d, 0x7f, 0xcf, 0x5a, 0x75, 0x34, 0xd4, 0x43, 0xdf, 0x59, 0xaa, 0x0d, 0x3d, 0x69,
|
||||||
|
0xe2, 0x7a, 0xb2, 0xc4, 0x54, 0x6b, 0x62, 0xe5, 0x27, 0x51, 0x18, 0x52, 0xfe, 0x40, 0x69, 0x62,
|
||||||
|
0x79, 0x03, 0xf9, 0x17, 0x1e, 0xa7, 0x06, 0x7f, 0x66, 0x74, 0x2c, 0x3f, 0xa1, 0x08, 0xd2, 0x4d,
|
||||||
|
0x34, 0x3a, 0x14, 0xb7, 0x70, 0x35, 0xbb, 0xd6, 0x60, 0x99, 0x55, 0xd9, 0x7e, 0xdb, 0x04, 0x21,
|
||||||
|
0x2a, 0xc8, 0x0f, 0xe8, 0x3a, 0xdb, 0x4f, 0xdc, 0xd3, 0x58, 0x5e, 0xf8, 0x5d, 0xfa, 0x24, 0x1f,
|
||||||
|
0x20, 0xff, 0xf8, 0xc5, 0x2e, 0xda, 0x0a, 0x01, 0xeb, 0xd6, 0x1a, 0x57, 0x66, 0xd5, 0xe5, 0x7e,
|
||||||
|
0xdb, 0xf8, 0x59, 0xbe, 0x42, 0x11, 0x4e, 0x62, 0xd4, 0x1d, 0x6c, 0x2c, 0xba, 0xf9, 0xc8, 0x3e,
|
||||||
|
0xab, 0x68, 0xa2, 0x5a, 0x10, 0xd0, 0x5a, 0xb2, 0x31, 0x26, 0x88, 0xe7, 0xbf, 0x0c, 0xae, 0xdf,
|
||||||
|
0x69, 0x18, 0xda, 0xf1, 0x20, 0xde, 0x60, 0xbd, 0x40, 0x8b, 0x7b, 0x95, 0x56, 0x53, 0x49, 0xaf,
|
||||||
|
0xdd, 0xee, 0xdc, 0x2a, 0x04, 0xcb, 0xd5, 0x62, 0xb0, 0xa0, 0x9c, 0x1a, 0x24, 0x0d, 0x4e, 0x0d,
|
||||||
|
0x52, 0x72, 0xb9, 0xd2, 0x1b, 0xff, 0xb5, 0x2f, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xcb, 0x77,
|
||||||
|
0xdf, 0x28, 0x85, 0x01, 0x00, 0x00,
|
||||||
|
}
|
25
agent/proto/bot.proto
Normal file
25
agent/proto/bot.proto
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package go.micro.bot;
|
||||||
|
|
||||||
|
service Command {
|
||||||
|
rpc Help(HelpRequest) returns (HelpResponse) {};
|
||||||
|
rpc Exec(ExecRequest) returns (ExecResponse) {};
|
||||||
|
}
|
||||||
|
|
||||||
|
message HelpRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
message HelpResponse {
|
||||||
|
string usage = 1;
|
||||||
|
string description = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExecRequest {
|
||||||
|
repeated string args = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExecResponse {
|
||||||
|
bytes result = 1;
|
||||||
|
string error = 2;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user