1. 分析参数
gogs推送入参如下
{ "ref": "refs/heads/develop", "before": "28e1879d029cb852e4844d9c718537df08844e03", "after": "bffeb74224043ba2feb48d137756c8a9331c449a", "compare_url": "http://localhost:3000/unknwon/webhooks/compare/28e1879d029cb852e4844d9c718537df08844e03...bffeb74224043ba2feb48d137756c8a9331c449a", "commits": [ { "id": "bffeb74224043ba2feb48d137756c8a9331c449a", "message": "!@#0^%u003eu003eu003eu003eu003cu003cu003cu003cu003eu003eu003eu003e ", "url": "http://localhost:3000/unknwon/webhooks/commit/bffeb74224043ba2feb48d137756c8a9331c449a", "author": { "name": "Unknwon", "email": "[email protected]", "username": "unknwon" }, "committer": { "name": "Unknwon", "email": "[email protected]", "username": "unknwon" }, "timestamp": "2017-03-13T13:52:11-04:00" } ], "repository": { "id": 140, "owner": { "id": 1, "login": "unknwon", "full_name": "Unknwon", "email": "[email protected]", "avatar_url": "https://secure.gravatar.com/avatar/d8b2871cdac01b57bbda23716cc03b96", "username": "unknwon" }, "name": "webhooks", "full_name": "unknwon/webhooks", "description": "", "private": false, "fork": false, "html_url": "http://localhost:3000/unknwon/webhooks", "ssh_url": "ssh://unknwon@localhost:2222/unknwon/webhooks.git", "clone_url": "http://localhost:3000/unknwon/webhooks.git", "website": "", "stars_count": 0, "forks_count": 1, "watchers_count": 1, "open_issues_count": 7, "default_branch": "master", "created_at": "2017-02-26T04:29:06-05:00", "updated_at": "2017-03-13T13:51:58-04:00" }, "pusher": { "id": 1, "login": "unknwon", "full_name": "Unknwon", "email": "[email protected]", "avatar_url": "https://secure.gravatar.com/avatar/d8b2871cdac01b57bbda23716cc03b96", "username": "unknwon" }, "sender": { "id": 1, "login": "unknwon", "full_name": "Unknwon", "email": "[email protected]", "avatar_url": "https://secure.gravatar.com/avatar/d8b2871cdac01b57bbda23716cc03b96", "username": "unknwon" } }
对我们而言,有需要通知的就:谁推送了代码到哪个分支,什么时间,仓库地址和代码详情,还有就是变更的代码明细。
2. 开发接口
使用go中gin框架作为基础,将需要处理的参数声明成结构体
type Commit struct { ID string `json:"id"` Message string `json:"message"` URL string `json:"url"` Author Author `json:"author"` Committer Committer `json:"committer"` Modified []string `json:"modified"` Timestamp string `json:"timestamp"` } type Author struct { Name string `json:"name"` Email string `json:"email"` Username string `json:"username"` } type Committer struct { Name string `json:"name"` Email string `json:"email"` Username string `json:"username"` } type Repository struct { HTMLURL string `json:"html_url"` } type PushEvent struct { Ref string `json:"ref"` Commits []Commit `json:"commits"` Repository Repository `json:"repository"` }
处理请求参数
func GetMessage(c *gin.Context) { body, err := io.ReadAll(c.Request.Body) if err != nil { c.JSON(500, gin.H{"error": "无法读取请求体"}) return } var pushEvent PushEvent err = json.Unmarshal(body, &pushEvent) if err != nil { c.JSON(400, gin.H{"error": "JSON 解析错误"}) return } var times string var info string var author string var link string var timestamp int64 var sign string now := time.Now() // 获取时间戳(秒) timestamp = now.Unix() for _, commit := range pushEvent.Commits { // 将时间字符串解析为时间对象 timeObj, err := time.Parse(time.RFC3339, commit.Timestamp) if err != nil { fmt.Println("时间解析错误:", err) continue } // 格式化时间为 "2024/01/15" formattedTime := timeObj.Format("2006/01/02") times = formattedTime info = commit.Message link = commit.URL author = commit.Author.Name }
将入参读取,序列化成json,然后把json数据装回结构体,然后取出参数
并赋值
代码解耦再单独写一个推送飞书的方法,新建结构体
type MessageData struct { Timestamp int64 Sign string Author string Stash string Branch string Time string Info string Link string }
func pushFeiShu(data MessageData) {
url := “https://open.feishu.cn/open-apis/bot/v2/hook/你的机器人hookid”
// 构建飞书消息体 msg := map[string]interface{}{ "timestamp": data.Timestamp, "sign": data.Sign, // 得到的签名字符串。 "msg_type": "interactive", "card": map[string]interface{}{ "config": map[string]interface{}{ "wide_screen_mode": true, }, "header": map[string]interface{}{ "template": "green", "title": map[string]interface{}{ "tag": "plain_text", "content": fmt.Sprintf("?? %s推送了代码", data.Author), }, }, "elements": []map[string]interface{}{ { "fields": []map[string]interface{}{ { "is_short": false, "text": map[string]interface{}{ "content": fmt.Sprintf("? 目标仓库: %s", data.Stash), "tag": "lark_md", }, }, { "is_short": false, "text": map[string]interface{}{ "content": fmt.Sprintf("?? 目标分支: %s", data.Branch), "tag": "lark_md", }, }, { "is_short": false, "text": map[string]interface{}{ "content": fmt.Sprintf("?? 推送时间: %s", data.Time), "tag": "lark_md", }, }, { "is_short": false, "text": map[string]interface{}{ "content": fmt.Sprintf("?? 最新提交:%s", data.Info), "tag": "lark_md", }, }, }, "tag": "div", }, { "tag": "action", "actions": []map[string]interface{}{ { "tag": "button", "text": map[string]interface{}{ "tag": "plain_text", "content": "查看代码", }, "type": "primary", "multi_url": map[string]interface{}{ "url": data.Link, }, }, }, }, }, }, } // 将消息体转换为 JSON 字节数组 payload, err := json.Marshal(msg) if err != nil { fmt.Println("消息体转换失败:", err) return } // 创建一个 HTTP 请求 req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload)) if err != nil { fmt.Println("创建请求失败:", err) return } // 设置请求头 req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Println("请求发送失败:", err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("读取响应失败:", err) return } fmt.Println("响应:", string(body))
}
push方法调用
data := MessageData{ Author: author, Timestamp: timestamp, Sign: sign, Stash: pushEvent.Repository.HTMLURL, Branch: pushEvent.Ref, Time: times, Info: info, Link: link, } pushFeiShu(data) c.JSON(200, gin.H{"message": "处理成功"})
注意:
如果飞书机器人配置了验证签名则需要单独处理添加签名,代码如下
package utils import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "fmt" ) func GenSign(secret string, timestamp int64) (string, error) { //timestamp + key 做sha256, 再进行base64 encode stringToSign := fmt.Sprintf("%v", timestamp) + " " + secret var data []byte h := hmac.New(sha256.New, []byte(stringToSign)) _, err := h.Write(data) if err != nil { return "", err } signature := base64.StdEncoding.EncodeToString(h.Sum(nil)) return signature, nil }
3. 配置gogs
添加接口地址
测试推送