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

287 lines
8.0 KiB

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