Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
*.dll
*.so
*.dylib
.env
.idea
.DS_Store
common.yaml

# Test binary, built with `go test -c`
*.test
Expand Down
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,53 @@
# ospp-cr-bot
开源之夏 - 一种通用的 Code Review 机器人

## How to Build

```bash
go mod tidy
go build ./cmd/main.go
```

## How to Run

```bash
./main
```

## How to Use

首先,请确保你已阅读过飞书开放平台文档并创建好你的应用。

参考:[https://open.feishu.cn/document/ukTMukTMukTM/uITNz4iM1MjLyUzM](https://open.feishu.cn/document/ukTMukTMukTM/uITNz4iM1MjLyUzM)

在项目根目录下创建 `.env` 文件,并填入如下内容:

```
LarkAppId=cli_a2f1d921fbxxxxxx
LarkAppSecret=Ss4kqZbQxhujO3BsIHcQZb3lDkxxxxxx
VerificationToken=6mLPrm022g3IrQcF8RUSLgVJ2txxxxxx
EncryptKey=xxx
```

程序会自动读取当前目录下的 `common.yaml` 文件。通常该文件的结构如下

```yaml
tasks:
- name: "Task 1" // 名称
repo: lyleshaw/go-best-practice // 仓库地址
repoType: github // 仓库类型,可选值:github, gitlab
recevier: oc_a0553eda9014c201e6969b478895c230 // 接收人的飞书 ID,如何获取见下
recevierType: group // 接收人类型,可选值:group, user
pushChannel: lark // 推送渠道,可选值:lark, slack
- name: "Task 2"
repo: xxx/xxx
```

请根据需求自定义上述参数。

## How to Get My Lark ID

请在启动服务后,拉入群聊并 @cr-bot-test,bot 会获取你的 Lark ID 并回复。

![img.png](docs/imgs/img.png)
![img.png](img.png)
39 changes: 39 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package main

import (
"github.com/gin-gonic/gin"
"github.com/lyleshaw/ospp-cr-bot/internal/pkg/config"
"github.com/lyleshaw/ospp-cr-bot/internal/pkg/eventListener"
"github.com/lyleshaw/ospp-cr-bot/internal/pkg/pushChannel/lark"
"github.com/lyleshaw/ospp-cr-bot/pkg/utils/log"
"github.com/sirupsen/logrus"
)

var (
isDebug bool
)

func init() {
initLog()
config.InitConfig()
}

func initLog() {
if isDebug {
logrus.SetLevel(logrus.DebugLevel)
log.Infof("Log level is: %s.", logrus.GetLevel())
} else {
logrus.SetLevel(logrus.InfoLevel)
}
}

func main() {
router := gin.Default()
router.POST("/github/webhook", eventListener.GitHubWebHook)
router.POST("/api/lark/callback", lark.Callback)
err := router.Run(":3000")
if err != nil {
return
}
return
}
46 changes: 46 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module github.com/lyleshaw/ospp-cr-bot

go 1.17

require (
github.com/faabiosr/cachego v0.15.0
github.com/fastwego/feishu v1.0.0-beta.4
github.com/gin-gonic/gin v1.7.7
github.com/sirupsen/logrus v1.8.1
github.com/spf13/viper v1.11.0
github.com/valyala/fasttemplate v1.2.1
gopkg.in/gookit/color.v1 v1.1.6
gopkg.in/yaml.v2 v2.4.0
)

require (
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.4.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/ugorji/go/codec v1.1.7 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)
874 changes: 874 additions & 0 deletions go.sum

Large diffs are not rendered by default.

Binary file added img.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions internal/pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package config

import (
"fmt"

"gopkg.in/yaml.v2"
"io/ioutil"
)

var (
Cfg *Config
)

type Config struct {
Tasks []Tasks `yaml:"tasks"`
}
type Tasks struct {
Name string `yaml:"name"`
Repo string `yaml:"repo"`
RepoType string `yaml:"repoType"`
Recevier string `yaml:"recevier"`
RecevierType string `yaml:"recevierType"`
PushChannel string `yaml:"pushChannel"`
}

func InitConfig() {
config, err := ioutil.ReadFile("./common.yaml")
if err != nil {
fmt.Print(err)
}

err = yaml.Unmarshal(config, &Cfg)
if err != nil {
fmt.Println("error")
}
}

func QueryReceiveIDByRepo(repo string) (string, error) {
for _, task := range Cfg.Tasks {
if task.Repo == repo {
return task.Recevier, nil
}
}
return "", fmt.Errorf("repo not found")
}
88 changes: 88 additions & 0 deletions internal/pkg/eventListener/eventListener.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package eventListener

import (
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"github.com/lyleshaw/ospp-cr-bot/internal/pkg/config"
"github.com/valyala/fasttemplate"
"io/ioutil"
"net/url"
"strconv"

"github.com/lyleshaw/ospp-cr-bot/internal/pkg/pushChannel/lark"
)

// Event defines a GitHub hook event type
type Event string

// GitHub hook types
const (
IssueCommentEvent Event = "issue_comment"
IssuesEvent Event = "issues"
PullRequestEvent Event = "pull_request"
PullRequestReviewEvent Event = "pull_request_review"
PullRequestReviewCommentEvent Event = "pull_request_review_comment"
)

// GitHubWebHook .
// @router /github/webhook [POST]
func GitHubWebHook(c *gin.Context) {
event := c.GetHeader("X-GitHub-Event")
if event == "" {
return
}

body, _ := ioutil.ReadAll(c.Request.Body)
bodyStr := string(body)
bodyStr = bodyStr[8:]
bodyStr, _ = url.QueryUnescape(bodyStr)

gitHubEvent := Event(event)

switch gitHubEvent {
case IssueCommentEvent:
var req IssueCommentPayload
_ = json.Unmarshal([]byte(bodyStr), &req)
// Do whatever you want from here...
fmt.Printf("%+v\n", req)
fmt.Printf("%s\n", req.Repository.FullName)
case IssuesEvent:
var req IssuesPayload
_ = json.Unmarshal([]byte(bodyStr), &req)
// Do whatever you want from here...
fmt.Printf("%+v\n", req)
fmt.Printf("%s\n", req.Repository.FullName)
case PullRequestEvent:
var req PullRequestPayload
_ = json.Unmarshal([]byte(bodyStr), &req)
t := fasttemplate.New(lark.SEND_PR_MSG, "{{", "}}")
prMsg := t.ExecuteString(map[string]interface{}{
"Time": req.PullRequest.CreatedAt.Format("2006年01月02日 15:04:05"),
"PRTitle": req.PullRequest.Title,
"Login": req.PullRequest.User.Login,
"PRNumber": strconv.FormatInt(req.PullRequest.Number, 10),
"PRURL": req.PullRequest.HTMLURL,
})
receiveID, err := config.QueryReceiveIDByRepo(req.Repository.FullName)
if err != nil {
return
}
lark.SendMessage(receiveID, prMsg)
case PullRequestReviewEvent:
var req PullRequestReviewPayload
_ = json.Unmarshal([]byte(bodyStr), &req)
// Do whatever you want from here...
fmt.Printf("%+v\n", req)
fmt.Printf("%s\n", req.PullRequest.Head.Repo.FullName)
fmt.Printf("%s\n", req.PullRequest.Head.User.Login)
case PullRequestReviewCommentEvent:
var req PullRequestReviewCommentPayload
_ = json.Unmarshal([]byte(bodyStr), &req)
// Do whatever you want from here...
fmt.Printf("%+v\n", req)
fmt.Printf("%s\n", req.PullRequest.Head.Repo.FullName)
fmt.Printf("%s\n", req.PullRequest.Head.User.Login)
}
c.JSON(200, nil)
}
Loading