You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

webhook_discord.go 8.0 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. package models
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "strconv"
  7. "strings"
  8. "code.gitea.io/git"
  9. "code.gitea.io/gitea/modules/setting"
  10. api "code.gitea.io/sdk/gitea"
  11. )
  12. type (
  13. // DiscordEmbedFooter for Embed Footer Structure.
  14. DiscordEmbedFooter struct {
  15. Text string `json:"text"`
  16. }
  17. // DiscordEmbedAuthor for Embed Author Structure
  18. DiscordEmbedAuthor struct {
  19. Name string `json:"name"`
  20. URL string `json:"url"`
  21. IconURL string `json:"icon_url"`
  22. }
  23. // DiscordEmbedField for Embed Field Structure
  24. DiscordEmbedField struct {
  25. Name string `json:"name"`
  26. Value string `json:"value"`
  27. }
  28. // DiscordEmbed is for Embed Structure
  29. DiscordEmbed struct {
  30. Title string `json:"title"`
  31. Description string `json:"description"`
  32. URL string `json:"url"`
  33. Color int `json:"color"`
  34. Footer DiscordEmbedFooter `json:"footer"`
  35. Author DiscordEmbedAuthor `json:"author"`
  36. Fields []DiscordEmbedField `json:"fields"`
  37. }
  38. // DiscordPayload represents
  39. DiscordPayload struct {
  40. Wait bool `json:"wait"`
  41. Content string `json:"content"`
  42. Username string `json:"username"`
  43. AvatarURL string `json:"avatar_url"`
  44. TTS bool `json:"tts"`
  45. Embeds []DiscordEmbed `json:"embeds"`
  46. }
  47. // DiscordMeta contains the discord metadata
  48. DiscordMeta struct {
  49. Username string `json:"username"`
  50. IconURL string `json:"icon_url"`
  51. }
  52. )
  53. func color(clr string) int {
  54. if clr != "" {
  55. clr = strings.TrimLeft(clr, "#")
  56. if s, err := strconv.ParseInt(clr, 16, 32); err == nil {
  57. return int(s)
  58. }
  59. }
  60. return 0
  61. }
  62. var (
  63. successColor = color("1ac600")
  64. warnColor = color("ffd930")
  65. failedColor = color("ff3232")
  66. )
  67. // SetSecret sets the discord secret
  68. func (p *DiscordPayload) SetSecret(_ string) {}
  69. // JSONPayload Marshals the DiscordPayload to json
  70. func (p *DiscordPayload) JSONPayload() ([]byte, error) {
  71. data, err := json.MarshalIndent(p, "", " ")
  72. if err != nil {
  73. return []byte{}, err
  74. }
  75. return data, nil
  76. }
  77. func getDiscordCreatePayload(p *api.CreatePayload, meta *DiscordMeta) (*DiscordPayload, error) {
  78. // created tag/branch
  79. refName := git.RefEndName(p.Ref)
  80. title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
  81. return &DiscordPayload{
  82. Username: meta.Username,
  83. AvatarURL: meta.IconURL,
  84. Embeds: []DiscordEmbed{
  85. {
  86. Title: title,
  87. URL: p.Repo.HTMLURL + "/src/" + refName,
  88. Color: successColor,
  89. Author: DiscordEmbedAuthor{
  90. Name: p.Sender.UserName,
  91. URL: setting.AppURL + p.Sender.UserName,
  92. IconURL: p.Sender.AvatarURL,
  93. },
  94. },
  95. },
  96. }, nil
  97. }
  98. func getDiscordPushPayload(p *api.PushPayload, meta *DiscordMeta) (*DiscordPayload, error) {
  99. var (
  100. branchName = git.RefEndName(p.Ref)
  101. commitDesc string
  102. )
  103. var titleLink string
  104. if len(p.Commits) == 1 {
  105. commitDesc = "1 new commit"
  106. titleLink = p.Commits[0].URL
  107. } else {
  108. commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
  109. titleLink = p.CompareURL
  110. }
  111. if titleLink == "" {
  112. titleLink = p.Repo.HTMLURL + "/src/" + branchName
  113. }
  114. title := fmt.Sprintf("[%s:%s] %s", p.Repo.FullName, branchName, commitDesc)
  115. var text string
  116. // for each commit, generate attachment text
  117. for i, commit := range p.Commits {
  118. text += fmt.Sprintf("[%s](%s) %s - %s", commit.ID[:7], commit.URL,
  119. strings.TrimRight(commit.Message, "\r\n"), commit.Author.Name)
  120. // add linebreak to each commit but the last
  121. if i < len(p.Commits)-1 {
  122. text += "\n"
  123. }
  124. }
  125. return &DiscordPayload{
  126. Username: meta.Username,
  127. AvatarURL: meta.IconURL,
  128. Embeds: []DiscordEmbed{
  129. {
  130. Title: title,
  131. Description: text,
  132. URL: titleLink,
  133. Color: successColor,
  134. Author: DiscordEmbedAuthor{
  135. Name: p.Sender.UserName,
  136. URL: setting.AppURL + p.Sender.UserName,
  137. IconURL: p.Sender.AvatarURL,
  138. },
  139. },
  140. },
  141. }, nil
  142. }
  143. func getDiscordPullRequestPayload(p *api.PullRequestPayload, meta *DiscordMeta) (*DiscordPayload, error) {
  144. var text, title string
  145. var color int
  146. switch p.Action {
  147. case api.HookIssueOpened:
  148. title = fmt.Sprintf("[%s] Pull request opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
  149. text = p.PullRequest.Body
  150. color = warnColor
  151. case api.HookIssueClosed:
  152. if p.PullRequest.HasMerged {
  153. title = fmt.Sprintf("[%s] Pull request merged: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
  154. color = successColor
  155. } else {
  156. title = fmt.Sprintf("[%s] Pull request closed: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
  157. color = failedColor
  158. }
  159. text = p.PullRequest.Body
  160. case api.HookIssueReOpened:
  161. title = fmt.Sprintf("[%s] Pull request re-opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
  162. text = p.PullRequest.Body
  163. color = warnColor
  164. case api.HookIssueEdited:
  165. title = fmt.Sprintf("[%s] Pull request edited: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
  166. text = p.PullRequest.Body
  167. color = warnColor
  168. case api.HookIssueAssigned:
  169. title = fmt.Sprintf("[%s] Pull request assigned to %s: #%d %s", p.Repository.FullName,
  170. p.PullRequest.Assignee.UserName, p.Index, p.PullRequest.Title)
  171. text = p.PullRequest.Body
  172. color = successColor
  173. case api.HookIssueUnassigned:
  174. title = fmt.Sprintf("[%s] Pull request unassigned: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
  175. text = p.PullRequest.Body
  176. color = warnColor
  177. case api.HookIssueLabelUpdated:
  178. title = fmt.Sprintf("[%s] Pull request labels updated: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
  179. text = p.PullRequest.Body
  180. color = warnColor
  181. case api.HookIssueLabelCleared:
  182. title = fmt.Sprintf("[%s] Pull request labels cleared: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
  183. text = p.PullRequest.Body
  184. color = warnColor
  185. case api.HookIssueSynchronized:
  186. title = fmt.Sprintf("[%s] Pull request synchronized: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
  187. text = p.PullRequest.Body
  188. color = warnColor
  189. }
  190. return &DiscordPayload{
  191. Username: meta.Username,
  192. AvatarURL: meta.IconURL,
  193. Embeds: []DiscordEmbed{
  194. {
  195. Title: title,
  196. Description: text,
  197. URL: p.PullRequest.HTMLURL,
  198. Color: color,
  199. Author: DiscordEmbedAuthor{
  200. Name: p.Sender.UserName,
  201. URL: setting.AppURL + p.Sender.UserName,
  202. IconURL: p.Sender.AvatarURL,
  203. },
  204. },
  205. },
  206. }, nil
  207. }
  208. func getDiscordRepositoryPayload(p *api.RepositoryPayload, meta *DiscordMeta) (*DiscordPayload, error) {
  209. var title, url string
  210. var color int
  211. switch p.Action {
  212. case api.HookRepoCreated:
  213. title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
  214. url = p.Repository.HTMLURL
  215. color = successColor
  216. case api.HookRepoDeleted:
  217. title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
  218. color = warnColor
  219. }
  220. return &DiscordPayload{
  221. Username: meta.Username,
  222. AvatarURL: meta.IconURL,
  223. Embeds: []DiscordEmbed{
  224. {
  225. Title: title,
  226. URL: url,
  227. Color: color,
  228. Author: DiscordEmbedAuthor{
  229. Name: p.Sender.UserName,
  230. URL: setting.AppURL + p.Sender.UserName,
  231. IconURL: p.Sender.AvatarURL,
  232. },
  233. },
  234. },
  235. }, nil
  236. }
  237. // GetDiscordPayload converts a discord webhook into a DiscordPayload
  238. func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (*DiscordPayload, error) {
  239. s := new(DiscordPayload)
  240. discord := &DiscordMeta{}
  241. if err := json.Unmarshal([]byte(meta), &discord); err != nil {
  242. return s, errors.New("GetDiscordPayload meta json:" + err.Error())
  243. }
  244. switch event {
  245. case HookEventCreate:
  246. return getDiscordCreatePayload(p.(*api.CreatePayload), discord)
  247. case HookEventPush:
  248. return getDiscordPushPayload(p.(*api.PushPayload), discord)
  249. case HookEventPullRequest:
  250. return getDiscordPullRequestPayload(p.(*api.PullRequestPayload), discord)
  251. case HookEventRepository:
  252. return getDiscordRepositoryPayload(p.(*api.RepositoryPayload), discord)
  253. }
  254. return s, nil
  255. }