go语言实现gogs代码推送到飞书群聊
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^%\u003e\u003e\u003e\u003e\u003c\u003c\u003c\u003c\u003e\u003e\u003e\u003e\n","url": "http://localhost:3000/unknwon/webhooks/commit/bffeb74224043ba2feb48d137756c8a9331c449a","author": {"name": "Unknwon","email": "u@gogs.io","username": "unknwon"},"committer": {"name": "Unknwon","email": "u@gogs.io","username": "unknwon"},"timestamp": "2017-03-13T13:52:11-04:00"}],"repository": {"id": 140,"owner": {"id": 1,"login": "unknwon","full_name": "Unknwon","email": "u@gogs.io","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": "u@gogs.io","avatar_url": "https://secure.gravatar.com/avatar/d8b2871cdac01b57bbda23716cc03b96","username": "unknwon"},"sender": {"id": 1,"login": "unknwon","full_name": "Unknwon","email": "u@gogs.io","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 PushEventerr = json.Unmarshal(body, &pushEvent)if err != nil {c.JSON(400, gin.H{"error": "JSON 解析错误"})return}var times stringvar info stringvar author stringvar link stringvar timestamp int64var sign stringnow := 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 = formattedTimeinfo = commit.Messagelink = commit.URLauthor = commit.Author.Name}
将入参读取,序列化成json,然后把json数据装回结构体,然后取出参数
并赋值
代码解耦再单独写一个推送飞书的方法,新建结构体
type MessageData struct {Timestamp int64Sign stringAuthor stringStash stringBranch stringTime stringInfo stringLink 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 utilsimport ("crypto/hmac""crypto/sha256""encoding/base64""fmt"
)func GenSign(secret string, timestamp int64) (string, error) {//timestamp + key 做sha256, 再进行base64 encodestringToSign := fmt.Sprintf("%v", timestamp) + "\n" + secretvar data []byteh := 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
添加接口地址
测试推送