aboutsummaryrefslogtreecommitdiff
path: root/models
diff options
context:
space:
mode:
Diffstat (limited to 'models')
-rw-r--r--models/comment.go69
-rw-r--r--models/issue.go12
-rw-r--r--models/milestone.go2
-rw-r--r--models/pull.go1
-rw-r--r--models/webhook.go73
-rw-r--r--models/webhook_discord.go41
-rw-r--r--models/webhook_slack.go36
7 files changed, 186 insertions, 48 deletions
diff --git a/models/comment.go b/models/comment.go
index e166efd9..078be582 100644
--- a/models/comment.go
+++ b/models/comment.go
@@ -150,9 +150,13 @@ func (c *Comment) APIFormat() *api.Comment {
}
}
+func CommentHashTag(id int64) string {
+ return "issuecomment-" + com.ToStr(id)
+}
+
// HashTag returns unique hash tag for comment.
func (c *Comment) HashTag() string {
- return "issuecomment-" + com.ToStr(c.ID)
+ return CommentHashTag(c.ID)
}
// EventTag returns unique event hash tag for comment.
@@ -330,7 +334,7 @@ func CreateComment(opts *CreateCommentOptions) (comment *Comment, err error) {
// CreateIssueComment creates a plain issue comment.
func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content string, attachments []string) (*Comment, error) {
- return CreateComment(&CreateCommentOptions{
+ comment, err := CreateComment(&CreateCommentOptions{
Type: COMMENT_TYPE_COMMENT,
Doer: doer,
Repo: repo,
@@ -338,6 +342,22 @@ func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content stri
Content: content,
Attachments: attachments,
})
+ if err != nil {
+ return nil, fmt.Errorf("CreateComment: %v", err)
+ }
+
+ comment.Issue = issue
+ if err = PrepareWebhooks(repo, HOOK_EVENT_ISSUE_COMMENT, &api.IssueCommentPayload{
+ Action: api.HOOK_ISSUE_COMMENT_CREATED,
+ Issue: issue.APIFormat(),
+ Comment: comment.APIFormat(),
+ Repository: repo.APIFormat(nil),
+ Sender: doer.APIFormat(),
+ }); err != nil {
+ log.Error(2, "PrepareWebhooks [comment_id: %d]: %v", comment.ID, err)
+ }
+
+ return comment, nil
}
// CreateRefComment creates a commit reference comment to issue.
@@ -437,13 +457,33 @@ func GetCommentsByRepoIDSince(repoID, since int64) ([]*Comment, error) {
}
// UpdateComment updates information of comment.
-func UpdateComment(c *Comment) error {
- _, err := x.Id(c.ID).AllCols().Update(c)
- return err
+func UpdateComment(doer *User, c *Comment, oldContent string) (err error) {
+ if _, err = x.Id(c.ID).AllCols().Update(c); err != nil {
+ return err
+ }
+
+ if err = c.Issue.LoadAttributes(); err != nil {
+ log.Error(2, "Issue.LoadAttributes [issue_id: %d]: %v", c.IssueID, err)
+ } else if err = PrepareWebhooks(c.Issue.Repo, HOOK_EVENT_ISSUE_COMMENT, &api.IssueCommentPayload{
+ Action: api.HOOK_ISSUE_COMMENT_EDITED,
+ Issue: c.Issue.APIFormat(),
+ Comment: c.APIFormat(),
+ Changes: &api.ChangesPayload{
+ Body: &api.ChangesFromPayload{
+ From: oldContent,
+ },
+ },
+ Repository: c.Issue.Repo.APIFormat(nil),
+ Sender: doer.APIFormat(),
+ }); err != nil {
+ log.Error(2, "PrepareWebhooks [comment_id: %d]: %v", c.ID, err)
+ }
+
+ return nil
}
// DeleteCommentByID deletes the comment by given ID.
-func DeleteCommentByID(id int64) error {
+func DeleteCommentByID(doer *User, id int64) error {
comment, err := GetCommentByID(id)
if err != nil {
if IsErrCommentNotExist(err) {
@@ -468,5 +508,20 @@ func DeleteCommentByID(id int64) error {
}
}
- return sess.Commit()
+ if err = sess.Commit(); err != nil {
+ return fmt.Errorf("Commit: %v", err)
+ }
+
+ if err = comment.Issue.LoadAttributes(); err != nil {
+ log.Error(2, "Issue.LoadAttributes [issue_id: %d]: %v", comment.IssueID, err)
+ } else if err = PrepareWebhooks(comment.Issue.Repo, HOOK_EVENT_ISSUE_COMMENT, &api.IssueCommentPayload{
+ Action: api.HOOK_ISSUE_COMMENT_DELETED,
+ Issue: comment.Issue.APIFormat(),
+ Comment: comment.APIFormat(),
+ Repository: comment.Issue.Repo.APIFormat(nil),
+ Sender: doer.APIFormat(),
+ }); err != nil {
+ log.Error(2, "PrepareWebhooks [comment_id: %d]: %v", comment.ID, err)
+ }
+ return nil
}
diff --git a/models/issue.go b/models/issue.go
index da70b28a..90d57763 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -251,8 +251,6 @@ func (issue *Issue) sendLabelUpdatedWebhook(doer *User) {
}
if err != nil {
log.Error(2, "PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err)
- } else {
- go HookQueue.Add(issue.RepoID)
}
}
@@ -363,8 +361,6 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
}
if err != nil {
log.Error(2, "PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err)
- } else {
- go HookQueue.Add(issue.RepoID)
}
return nil
@@ -502,8 +498,6 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e
}
if err != nil {
log.Error(2, "PrepareWebhooks [is_pull: %v, is_closed: %v]: %v", issue.IsPull, isClosed, err)
- } else {
- go HookQueue.Add(repo.ID)
}
return nil
@@ -546,8 +540,6 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
}
if err != nil {
log.Error(2, "PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err)
- } else {
- go HookQueue.Add(issue.RepoID)
}
return nil
@@ -590,8 +582,6 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
}
if err != nil {
log.Error(2, "PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err)
- } else {
- go HookQueue.Add(issue.RepoID)
}
return nil
@@ -641,8 +631,6 @@ func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
}
if err != nil {
log.Error(4, "PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, isRemoveAssignee, err)
- } else {
- go HookQueue.Add(issue.RepoID)
}
return nil
diff --git a/models/milestone.go b/models/milestone.go
index 18c7b696..56a19cd8 100644
--- a/models/milestone.go
+++ b/models/milestone.go
@@ -348,8 +348,6 @@ func ChangeMilestoneAssign(doer *User, issue *Issue, oldMilestoneID int64) (err
}
if err != nil {
log.Error(2, "PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err)
- } else {
- go HookQueue.Add(issue.RepoID)
}
return nil
diff --git a/models/pull.go b/models/pull.go
index d3ea9c78..8e9e0c5d 100644
--- a/models/pull.go
+++ b/models/pull.go
@@ -454,7 +454,6 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
}); err != nil {
log.Error(2, "PrepareWebhooks: %v", err)
}
- go HookQueue.Add(repo.ID)
return nil
}
diff --git a/models/webhook.go b/models/webhook.go
index 2eee5555..32c3ab34 100644
--- a/models/webhook.go
+++ b/models/webhook.go
@@ -62,12 +62,13 @@ func IsValidHookContentType(name string) bool {
}
type HookEvents struct {
- Create bool `json:"create"`
- Delete bool `json:"delete"`
- Fork bool `json:"fork"`
- Push bool `json:"push"`
- Issues bool `json:"issues"`
- PullRequest bool `json:"pull_request"`
+ Create bool `json:"create"`
+ Delete bool `json:"delete"`
+ Fork bool `json:"fork"`
+ Push bool `json:"push"`
+ Issues bool `json:"issues"`
+ IssueComment bool `json:"issue_comment"`
+ PullRequest bool `json:"pull_request"`
}
// HookEvent represents events that will delivery hook.
@@ -183,28 +184,38 @@ func (w *Webhook) HasIssuesEvent() bool {
(w.ChooseEvents && w.HookEvents.Issues)
}
+// HasIssueCommentEvent returns true if hook enabled issue comment event.
+func (w *Webhook) HasIssueCommentEvent() bool {
+ return w.SendEverything ||
+ (w.ChooseEvents && w.HookEvents.IssueComment)
+}
+
// HasPullRequestEvent returns true if hook enabled pull request event.
func (w *Webhook) HasPullRequestEvent() bool {
return w.SendEverything ||
(w.ChooseEvents && w.HookEvents.PullRequest)
}
+type eventChecker struct {
+ checker func() bool
+ typ HookEventType
+}
+
func (w *Webhook) EventsArray() []string {
- events := make([]string, 0, 5)
- if w.HasCreateEvent() {
- events = append(events, string(HOOK_EVENT_CREATE))
- }
- if w.HasDeleteEvent() {
- events = append(events, string(HOOK_EVENT_DELETE))
- }
- if w.HasForkEvent() {
- events = append(events, string(HOOK_EVENT_FORK))
+ events := make([]string, 0, 7)
+ eventCheckers := []eventChecker{
+ {w.HasCreateEvent, HOOK_EVENT_CREATE},
+ {w.HasDeleteEvent, HOOK_EVENT_DELETE},
+ {w.HasForkEvent, HOOK_EVENT_FORK},
+ {w.HasPushEvent, HOOK_EVENT_PUSH},
+ {w.HasIssuesEvent, HOOK_EVENT_ISSUES},
+ {w.HasIssueCommentEvent, HOOK_EVENT_ISSUE_COMMENT},
+ {w.HasPullRequestEvent, HOOK_EVENT_PULL_REQUEST},
}
- if w.HasPushEvent() {
- events = append(events, string(HOOK_EVENT_PUSH))
- }
- if w.HasPullRequestEvent() {
- events = append(events, string(HOOK_EVENT_PULL_REQUEST))
+ for _, c := range eventCheckers {
+ if c.checker() {
+ events = append(events, string(c.typ))
+ }
}
return events
}
@@ -363,12 +374,13 @@ func IsValidHookTaskType(name string) bool {
type HookEventType string
const (
- HOOK_EVENT_CREATE HookEventType = "create"
- HOOK_EVENT_DELETE HookEventType = "delete"
- HOOK_EVENT_FORK HookEventType = "fork"
- HOOK_EVENT_PUSH HookEventType = "push"
- HOOK_EVENT_ISSUES HookEventType = "issues"
- HOOK_EVENT_PULL_REQUEST HookEventType = "pull_request"
+ HOOK_EVENT_CREATE HookEventType = "create"
+ HOOK_EVENT_DELETE HookEventType = "delete"
+ HOOK_EVENT_FORK HookEventType = "fork"
+ HOOK_EVENT_PUSH HookEventType = "push"
+ HOOK_EVENT_ISSUES HookEventType = "issues"
+ HOOK_EVENT_ISSUE_COMMENT HookEventType = "issue_comment"
+ HOOK_EVENT_PULL_REQUEST HookEventType = "pull_request"
)
// HookRequest represents hook task request information.
@@ -496,6 +508,10 @@ func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Pay
if !w.HasDeleteEvent() {
continue
}
+ case HOOK_EVENT_FORK:
+ if !w.HasForkEvent() {
+ continue
+ }
case HOOK_EVENT_PUSH:
if !w.HasPushEvent() {
continue
@@ -504,6 +520,10 @@ func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Pay
if !w.HasIssuesEvent() {
continue
}
+ case HOOK_EVENT_ISSUE_COMMENT:
+ if !w.HasIssueCommentEvent() {
+ continue
+ }
case HOOK_EVENT_PULL_REQUEST:
if !w.HasPullRequestEvent() {
continue
@@ -554,6 +574,7 @@ func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Pay
// It's safe to fail when the whole function is called during hook execution
// because resource released after exit.
+ // FIXME: need a more safe way to not call this function if it's during hook execution.
go HookQueue.Add(repo.ID)
return nil
}
diff --git a/models/webhook_discord.go b/models/webhook_discord.go
index 48016c13..0e9a36f2 100644
--- a/models/webhook_discord.go
+++ b/models/webhook_discord.go
@@ -241,6 +241,45 @@ func getDiscordIssuesPayload(p *api.IssuesPayload, slack *SlackMeta) (*DiscordPa
}, nil
}
+func getDiscordIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (*DiscordPayload, error) {
+ title := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)
+ url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
+ content := ""
+ fields := make([]*DiscordEmbedFieldObject, 0, 1)
+ switch p.Action {
+ case api.HOOK_ISSUE_COMMENT_CREATED:
+ title = "New comment: " + title
+ content = p.Comment.Body
+ case api.HOOK_ISSUE_COMMENT_EDITED:
+ title = "Comment edited: " + title
+ content = p.Comment.Body
+ case api.HOOK_ISSUE_COMMENT_DELETED:
+ title = "Comment deleted: " + title
+ url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
+ content = p.Comment.Body
+ }
+
+ color, _ := strconv.ParseInt(strings.TrimLeft(slack.Color, "#"), 16, 32)
+ return &DiscordPayload{
+ Username: slack.Username,
+ AvatarURL: slack.IconURL,
+ Embeds: []*DiscordEmbedObject{{
+ Title: title,
+ Description: content,
+ URL: url,
+ Color: int(color),
+ Footer: &DiscordEmbedFooterObject{
+ Text: p.Repository.FullName,
+ },
+ Author: &DiscordEmbedAuthorObject{
+ Name: p.Sender.UserName,
+ IconURL: p.Sender.AvatarUrl,
+ },
+ Fields: fields,
+ }},
+ }, nil
+}
+
func getDiscordPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*DiscordPayload, error) {
title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
url := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
@@ -331,6 +370,8 @@ func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (paylo
payload, err = getDiscordPushPayload(p.(*api.PushPayload), slack)
case HOOK_EVENT_ISSUES:
payload, err = getDiscordIssuesPayload(p.(*api.IssuesPayload), slack)
+ case HOOK_EVENT_ISSUE_COMMENT:
+ payload, err = getDiscordIssueCommentPayload(p.(*api.IssueCommentPayload), slack)
case HOOK_EVENT_PULL_REQUEST:
payload, err = getDiscordPullRequestPayload(p.(*api.PullRequestPayload), slack)
}
diff --git a/models/webhook_slack.go b/models/webhook_slack.go
index 96a44390..d1134ed8 100644
--- a/models/webhook_slack.go
+++ b/models/webhook_slack.go
@@ -191,6 +191,40 @@ func getSlackIssuesPayload(p *api.IssuesPayload, slack *SlackMeta) (*SlackPayloa
}, nil
}
+func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (*SlackPayload, error) {
+ senderLink := SlackLinkFormatter(setting.AppUrl+p.Sender.UserName, p.Sender.UserName)
+ titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)),
+ fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
+ var text, title, attachmentText string
+ switch p.Action {
+ case api.HOOK_ISSUE_COMMENT_CREATED:
+ text = fmt.Sprintf("[%s] New comment created by %s", p.Repository.FullName, senderLink)
+ title = titleLink
+ attachmentText = SlackTextFormatter(p.Comment.Body)
+ case api.HOOK_ISSUE_COMMENT_EDITED:
+ text = fmt.Sprintf("[%s] Comment edited by %s", p.Repository.FullName, senderLink)
+ title = titleLink
+ attachmentText = SlackTextFormatter(p.Comment.Body)
+ case api.HOOK_ISSUE_COMMENT_DELETED:
+ text = fmt.Sprintf("[%s] Comment deleted by %s", p.Repository.FullName, senderLink)
+ title = SlackLinkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index),
+ fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
+ attachmentText = SlackTextFormatter(p.Comment.Body)
+ }
+
+ return &SlackPayload{
+ Channel: slack.Channel,
+ Text: text,
+ Username: slack.Username,
+ IconURL: slack.IconURL,
+ Attachments: []*SlackAttachment{{
+ Color: slack.Color,
+ Title: title,
+ Text: attachmentText,
+ }},
+ }, nil
+}
+
func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) {
senderLink := SlackLinkFormatter(setting.AppUrl+p.Sender.UserName, p.Sender.UserName)
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
@@ -260,6 +294,8 @@ func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (payload
payload, err = getSlackPushPayload(p.(*api.PushPayload), slack)
case HOOK_EVENT_ISSUES:
payload, err = getSlackIssuesPayload(p.(*api.IssuesPayload), slack)
+ case HOOK_EVENT_ISSUE_COMMENT:
+ payload, err = getSlackIssueCommentPayload(p.(*api.IssueCommentPayload), slack)
case HOOK_EVENT_PULL_REQUEST:
payload, err = getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack)
}