├── adapter ├── dingtalk │ ├── readme.md │ ├── dingtalk.go │ ├── incoming.go │ ├── outgoing.go │ └── message.go ├── bearychat │ ├── readme.md │ ├── request.go │ ├── response.go │ └── bearychat.go ├── adapter.go ├── wework │ ├── msg_type.go │ ├── user.go │ └── wework.go └── wechat │ ├── group.go │ ├── wechat.go │ └── assisant.go ├── robot ├── scripts │ ├── echo.bat │ ├── hello.py │ ├── echo.sh │ ├── hello.php │ └── echo.yml ├── .gitignore ├── plugins │ ├── plugins.go │ ├── hello │ │ └── hello.go │ ├── ping │ │ └── ping.go │ └── vote │ │ ├── setup.go │ │ └── vote.go └── rboot.go ├── .gitignore ├── go.mod ├── rule.go ├── brain └── leveldb │ ├── brain_test.go │ └── brain.go ├── hook.go ├── LICENSE ├── .env.example ├── timing_test.go ├── http.go ├── message.go ├── worker.go ├── timing.go ├── adapter.go ├── plugins.go ├── readme.md ├── scripts.go ├── brain.go ├── router.go ├── envload.go └── robot.go /adapter/dingtalk/readme.md: -------------------------------------------------------------------------------- 1 | ### dingtalk暂停维护 -------------------------------------------------------------------------------- /adapter/bearychat/readme.md: -------------------------------------------------------------------------------- 1 | ### brarychat已经停止运营 -------------------------------------------------------------------------------- /robot/scripts/echo.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo %1 3 | -------------------------------------------------------------------------------- /robot/scripts/hello.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | print("Hello, robot! i am a python script") -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-* 2 | .autowatcher 3 | .DS_Store 4 | *.swp 5 | *.swo 6 | #*.*# 7 | .exe 8 | .idea 9 | .logs 10 | *.log 11 | 12 | test 13 | -------------------------------------------------------------------------------- /robot/scripts/echo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | command=$1 4 | shift 5 | 6 | case "$command" in 7 | "echo") 8 | echo "$1" 9 | ;; 10 | esac 11 | -------------------------------------------------------------------------------- /adapter/adapter.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import ( 4 | _ "github.com/ghaoo/rboot/adapter/bearychat" 5 | _ "github.com/ghaoo/rboot/adapter/dingtalk" 6 | _ "github.com/ghaoo/rboot/adapter/wework" 7 | ) 8 | -------------------------------------------------------------------------------- /robot/.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-* 2 | .DS_Store 3 | .autowatcher 4 | 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | 10 | *.swp 11 | *.swo 12 | *.log 13 | 14 | .idea 15 | .logs 16 | .data 17 | .vscode 18 | .vs 19 | .env -------------------------------------------------------------------------------- /robot/plugins/plugins.go: -------------------------------------------------------------------------------- 1 | package plugins 2 | 3 | import ( 4 | _ "github.com/ghaoo/rboot/robot/plugins/hello" 5 | _ "github.com/ghaoo/rboot/robot/plugins/ping" 6 | _ "github.com/ghaoo/rboot/robot/plugins/vote" 7 | ) 8 | -------------------------------------------------------------------------------- /robot/scripts/hello.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 3 | 11 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ghaoo/rboot 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/fatih/color v1.12.0 7 | github.com/ghaoo/wechat v0.0.0-20200807092035-693f7c60f29d 8 | github.com/ghaoo/wxwork v0.0.0-20210908011729-aab93f2bf45b 9 | github.com/gorilla/mux v1.8.0 10 | github.com/sirupsen/logrus v1.8.1 11 | github.com/syndtr/goleveldb v1.0.0 12 | go.etcd.io/bbolt v1.3.6 13 | gopkg.in/yaml.v2 v2.4.0 14 | ) 15 | -------------------------------------------------------------------------------- /adapter/bearychat/request.go: -------------------------------------------------------------------------------- 1 | package bearychat 2 | 3 | // 消息传入请求 4 | type Request struct { 5 | Token string `json:"token,omitempty"` 6 | TimeStamp int64 `json:"ts,omitempty"` 7 | Text string `json:"text,omitempty"` 8 | TriggerWord string `json:"trigger_word,omitempty"` 9 | Subdomain string `json:"subdomain,omitempty"` 10 | ChannelName string `json:"channel_name,omitempty"` 11 | UserName string `json:"user_name,omitempty"` 12 | } 13 | -------------------------------------------------------------------------------- /adapter/wework/msg_type.go: -------------------------------------------------------------------------------- 1 | package wework 2 | 3 | const ( 4 | MSG_TYPE_TEXT = "text" // 文本消息 5 | MSG_TYPE_IMAGE = "image" // 图片消息 6 | MSG_TYPE_VOICE = "voice" // 语音消息 7 | MSG_TYPE_VIDEO = "video" // 视频消息 8 | MSG_TYPE_FILE = "file" // 文件消息 9 | MSG_TYPE_TEXTCARD = "textcard" // 文本卡片消息 10 | MSG_TYPE_MARKDOWN = "markdown" // markdown消息 11 | MSG_TYPE_LOCATION = "location" // 位置消息 12 | MSG_TYPE_LINK = "link" // 位置消息 13 | MSG_TYPE_EVENT = "event" // 事件消息 14 | ) 15 | -------------------------------------------------------------------------------- /robot/plugins/hello/hello.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | import "github.com/ghaoo/rboot" 4 | 5 | func init() { 6 | // 注册脚本 7 | rboot.RegisterPlugin(`hello`, rboot.Plugin{ 8 | // 脚本处理函数 9 | Action: func(bot *rboot.Robot, incoming *rboot.Message) []*rboot.Message { 10 | return rboot.NewMessages("Hello World!") 11 | }, 12 | Ruleset: map[string]string{`hello`: `^!hello`}, // 脚本规则集 13 | Usage: map[string]string{ 14 | "!hello": "say hello world", 15 | }, 16 | Description: `example 'Hello World' script for rboot`, 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /rule.go: -------------------------------------------------------------------------------- 1 | package rboot 2 | 3 | import ( 4 | "regexp" 5 | ) 6 | 7 | // Rule 消息处理器接口,暂时只支持正则 8 | type Rule interface { 9 | Match(pattern, msg string) ([]string, bool) 10 | } 11 | 12 | // Regex 正则消息处理器 13 | type Regex struct{} 14 | 15 | // Match 当匹配失败时返回 false,返回true证明匹配成功并返回消息的匹配文本和子表达式的匹配 16 | func (reg *Regex) Match(pattern, msg string) ([]string, bool) { 17 | r := regexp.MustCompile(pattern) 18 | 19 | if submatch := r.FindStringSubmatch(msg); submatch != nil { 20 | return submatch, true 21 | } 22 | 23 | return nil, false 24 | } 25 | -------------------------------------------------------------------------------- /robot/scripts/echo.yml: -------------------------------------------------------------------------------- 1 | name: hi 2 | version: 0.1.0 3 | ruleset: 4 | hi: "^!hi" 5 | usage: 6 | hi: echo hello world and 你好 7 | description: 脚本插件示例 8 | command: 9 | - 10 | cmd: 11 | - echo hello world 12 | - 13 | dir: scripts 14 | cmd: 15 | - .\echo.bat hellobat 16 | - 17 | dir: scripts 18 | cmd: 19 | - ./echo.sh helloshell 20 | - 21 | dir: scripts 22 | cmd: 23 | - ./hello.php 24 | - 25 | dir: scripts 26 | cmd: 27 | - ./hello.py 28 | 29 | -------------------------------------------------------------------------------- /brain/leveldb/brain_test.go: -------------------------------------------------------------------------------- 1 | package leveldb 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestGoleveldb_Set(t *testing.T) { 9 | db := NewLevelDB() 10 | defer db.Close() 11 | 12 | err := db.Set("test", "test", []byte("GoLevelDB")) 13 | if err != nil { 14 | t.Error(err) 15 | } 16 | } 17 | 18 | func TestGoleveldb_Get(t *testing.T) { 19 | db := NewLevelDB() 20 | defer db.Close() 21 | 22 | val, err := db.Get("test", "test") 23 | 24 | if err != nil { 25 | t.Error(err) 26 | } 27 | 28 | fmt.Println(string(val)) 29 | } 30 | 31 | func TestGoleveldb_Remove(t *testing.T) { 32 | db := NewLevelDB() 33 | defer db.Close() 34 | 35 | err := db.Remove("test", "test") 36 | if err != nil { 37 | t.Error(err) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /hook.go: -------------------------------------------------------------------------------- 1 | package rboot 2 | 3 | const ( 4 | HOOK_BEFORE_INCOMING int = iota + 1 5 | HOOK_AFTER_OUTGOING 6 | ) 7 | 8 | var hookType = map[int]string{ 9 | HOOK_BEFORE_INCOMING: "传入消息前", 10 | HOOK_AFTER_OUTGOING: "消息传出后", 11 | } 12 | 13 | var AllHookTypes = []int{ 14 | HOOK_BEFORE_INCOMING, 15 | HOOK_AFTER_OUTGOING, 16 | } 17 | 18 | // Hook interface 19 | type Hook interface { 20 | Types() []int 21 | Fire(*Message) error 22 | } 23 | 24 | type Hooks map[int][]Hook 25 | 26 | // Add 新增钩子 27 | func (hooks Hooks) Add(hook Hook) { 28 | for _, typ := range hook.Types() { 29 | hooks[typ] = append(hooks[typ], hook) 30 | } 31 | } 32 | 33 | // Fire 执行 34 | func (hooks Hooks) Fire(typ int, bot *Robot, msg *Message) error { 35 | for _, hook := range hooks[typ] { 36 | if err := hook.Fire(msg); err != nil { 37 | return err 38 | } 39 | } 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /adapter/wechat/group.go: -------------------------------------------------------------------------------- 1 | package wechat 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "strconv" 8 | "time" 9 | 10 | sdk "github.com/ghaoo/wechat" 11 | ) 12 | 13 | type groupUser struct { 14 | UserName string 15 | } 16 | 17 | // 创建群聊 18 | func (w *wx) createChatRoom(users []groupUser) error { 19 | 20 | url := fmt.Sprintf(`%s/webwxcreatechatroom?r=%s&%s&lang=zh_CN`, w.client.BaseURL, strconv.FormatInt(time.Now().Unix(), 10), w.client.PassTicketKV()) 21 | 22 | data := map[string]interface{}{ 23 | `BaseRequest`: w.client.BaseRequest, 24 | `MemberCount`: len(users), 25 | `MemberList`: users, 26 | `Topic`: ``, 27 | } 28 | 29 | bs, _ := json.Marshal(data) 30 | 31 | var resp sdk.Response 32 | 33 | err := w.client.Execute(url, bytes.NewReader(bs), &resp) 34 | if err != nil { 35 | return err 36 | } 37 | if resp.IsSuccess() { 38 | return nil 39 | } 40 | return resp.Error() 41 | } 42 | -------------------------------------------------------------------------------- /robot/rboot.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/ghaoo/rboot" 5 | _ "github.com/ghaoo/rboot/adapter" 6 | "github.com/ghaoo/rboot/brain/leveldb" 7 | _ "github.com/ghaoo/rboot/robot/plugins" 8 | "github.com/sirupsen/logrus" 9 | ) 10 | 11 | func main() { 12 | 13 | bot := rboot.New() 14 | 15 | bot.SetBrain(leveldb.NewLevelDB()) 16 | 17 | bot.Go() 18 | } 19 | 20 | func init() { 21 | logrus.SetLevel(logrus.TraceLevel) 22 | logrus.SetFormatter(&logrus.TextFormatter{ 23 | ForceColors: true, 24 | }) 25 | /*logfile := filepath.Join(os.Getenv("CACHE_PATH"), "log/go.log") 26 | 27 | writer, _ := rotatelogs.New( 28 | logfile+".%Y%m%d", 29 | rotatelogs.WithLinkName(logfile), 30 | rotatelogs.WithRotationCount(1000), 31 | rotatelogs.WithRotationTime(time.Duration(24)*time.Hour), 32 | ) 33 | 34 | logrus.SetFormatter(&logrus.JSONFormatter{}) 35 | logrus.SetOutput(writer) 36 | 37 | logrus.SetLevel(logrus.TraceLevel)*/ 38 | } 39 | -------------------------------------------------------------------------------- /robot/plugins/ping/ping.go: -------------------------------------------------------------------------------- 1 | package ping 2 | 3 | import ( 4 | "github.com/ghaoo/rboot" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func setup(bot *rboot.Robot, in *rboot.Message) (msg []*rboot.Message) { 10 | return rboot.NewMessages(randReply()) 11 | } 12 | 13 | func randReply() string { 14 | rand.Seed(int64(time.Now().UnixNano())) 15 | replies := []string{ 16 | "yeah um.. pong?", 17 | "WHAT?! jeeze.", 18 | "what? oh, um SYNACKSYN? ENOSPEAKTCP.", 19 | "RST (lulz)", 20 | "64 bytes from go.away.your.annoying icmp_seq=0 ttl=42 time=42.596 ms", 21 | "hmm?", 22 | "ack. what?", 23 | "pong. what?", 24 | "yup. still here.", 25 | "super busy just now.. Can I get back to you in like 5min?", 26 | } 27 | content := replies[rand.Intn(len(replies))] 28 | 29 | return content 30 | } 31 | 32 | func init() { 33 | rboot.RegisterPlugin(`ping`, rboot.Plugin{ 34 | Action: setup, 35 | Ruleset: map[string]string{ 36 | `ping`: `^!(?:ping|PING)`, 37 | }, 38 | Usage: map[string]string{ 39 | "!ping": "随机返回一句回答", 40 | }, 41 | Description: `测试脚本`, 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /robot/plugins/vote/setup.go: -------------------------------------------------------------------------------- 1 | package vote 2 | 3 | import ( 4 | "github.com/ghaoo/rboot" 5 | ) 6 | 7 | var vote = new(Vote) 8 | 9 | func setup(bot *rboot.Robot, in *rboot.Message) []*rboot.Message { 10 | rule := in.Header.Get("rule") 11 | args := in.Header["args"] 12 | 13 | switch rule { 14 | case `new_vote`: 15 | return vote.New(args[1], args[2]) 16 | case `voting`: 17 | return vote.Voting(in.Sender, args[1]) 18 | case `stop_vote`: 19 | return vote.Stop() 20 | case `result`: 21 | return vote.Result() 22 | } 23 | 24 | return nil 25 | } 26 | 27 | func init() { 28 | rboot.RegisterPlugin(`vote`, rboot.Plugin{ 29 | Action: setup, 30 | Ruleset: map[string]string{ 31 | `voting`: `^@@(.+)`, 32 | `result`: `^!投票结果`, 33 | `new_vote`: `^!投票(.+)[ ]?\[(.+)\]`, 34 | `stop_vote`: `^!投票结束`, 35 | }, 36 | Usage: map[string]string{ 37 | "!投票 [选项1 选项2...]": "新建投票", 38 | "@@