From 97558f6a6ec4c53fa69035fb430ead209d9c222d Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Fri, 13 Jan 2017 13:53:37 -0500 Subject: PLT-4938 Add app package and move logic over from api package (#4931) * Add app package and move logic over from api package * Change app package functions to return errors * Move non-api tests into app package * Fix merge --- app/webhook.go | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 app/webhook.go (limited to 'app/webhook.go') diff --git a/app/webhook.go b/app/webhook.go new file mode 100644 index 000000000..dfd59349f --- /dev/null +++ b/app/webhook.go @@ -0,0 +1,155 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package app + +import ( + "crypto/tls" + "io" + "io/ioutil" + "net/http" + "strings" + + l4g "github.com/alecthomas/log4go" + "github.com/mattermost/platform/model" + "github.com/mattermost/platform/utils" +) + +const ( + TRIGGERWORDS_FULL = 0 + TRIGGERWORDS_STARTSWITH = 1 +) + +func handleWebhookEvents(post *model.Post, team *model.Team, channel *model.Channel, user *model.User) *model.AppError { + if !utils.Cfg.ServiceSettings.EnableOutgoingWebhooks { + return nil + } + + if channel.Type != model.CHANNEL_OPEN { + return nil + } + + hchan := Srv.Store.Webhook().GetOutgoingByTeam(team.Id) + result := <-hchan + if result.Err != nil { + return result.Err + } + + hooks := result.Data.([]*model.OutgoingWebhook) + if len(hooks) == 0 { + return nil + } + + splitWords := strings.Fields(post.Message) + if len(splitWords) == 0 { + return nil + } + firstWord := splitWords[0] + + relevantHooks := []*model.OutgoingWebhook{} + for _, hook := range hooks { + if hook.ChannelId == post.ChannelId || len(hook.ChannelId) == 0 { + if hook.ChannelId == post.ChannelId && len(hook.TriggerWords) == 0 { + relevantHooks = append(relevantHooks, hook) + } else if hook.TriggerWhen == TRIGGERWORDS_FULL && hook.HasTriggerWord(firstWord) { + relevantHooks = append(relevantHooks, hook) + } else if hook.TriggerWhen == TRIGGERWORDS_STARTSWITH && hook.TriggerWordStartsWith(firstWord) { + relevantHooks = append(relevantHooks, hook) + } + } + } + + for _, hook := range relevantHooks { + go func(hook *model.OutgoingWebhook) { + payload := &model.OutgoingWebhookPayload{ + Token: hook.Token, + TeamId: hook.TeamId, + TeamDomain: team.Name, + ChannelId: post.ChannelId, + ChannelName: channel.Name, + Timestamp: post.CreateAt, + UserId: post.UserId, + UserName: user.Username, + PostId: post.Id, + Text: post.Message, + TriggerWord: firstWord, + } + var body io.Reader + var contentType string + if hook.ContentType == "application/json" { + body = strings.NewReader(payload.ToJSON()) + contentType = "application/json" + } else { + body = strings.NewReader(payload.ToFormValues()) + contentType = "application/x-www-form-urlencoded" + } + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: *utils.Cfg.ServiceSettings.EnableInsecureOutgoingConnections}, + } + client := &http.Client{Transport: tr} + + for _, url := range hook.CallbackURLs { + go func(url string) { + req, _ := http.NewRequest("POST", url, body) + req.Header.Set("Content-Type", contentType) + req.Header.Set("Accept", "application/json") + if resp, err := client.Do(req); err != nil { + l4g.Error(utils.T("api.post.handle_webhook_events_and_forget.event_post.error"), err.Error()) + } else { + defer func() { + ioutil.ReadAll(resp.Body) + resp.Body.Close() + }() + respProps := model.MapFromJson(resp.Body) + + if text, ok := respProps["text"]; ok { + if _, err := CreateWebhookPost(hook.CreatorId, hook.TeamId, post.ChannelId, text, respProps["username"], respProps["icon_url"], post.Props, post.Type); err != nil { + l4g.Error(utils.T("api.post.handle_webhook_events_and_forget.create_post.error"), err) + } + } + } + }(url) + } + + }(hook) + } + + return nil +} + +func CreateWebhookPost(userId, teamId, channelId, text, overrideUsername, overrideIconUrl string, props model.StringInterface, postType string) (*model.Post, *model.AppError) { + post := &model.Post{UserId: userId, ChannelId: channelId, Message: text, Type: postType} + post.AddProp("from_webhook", "true") + + if utils.Cfg.ServiceSettings.EnablePostUsernameOverride { + if len(overrideUsername) != 0 { + post.AddProp("override_username", overrideUsername) + } else { + post.AddProp("override_username", model.DEFAULT_WEBHOOK_USERNAME) + } + } + + if utils.Cfg.ServiceSettings.EnablePostIconOverride { + if len(overrideIconUrl) != 0 { + post.AddProp("override_icon_url", overrideIconUrl) + } + } + + post.Message = parseSlackLinksToMarkdown(post.Message) + + if len(props) > 0 { + for key, val := range props { + if key == "attachments" { + parseSlackAttachment(post, val) + } else if key != "override_icon_url" && key != "override_username" && key != "from_webhook" { + post.AddProp(key, val) + } + } + } + + if _, err := CreatePost(post, teamId, false); err != nil { + return nil, model.NewLocAppError("CreateWebhookPost", "api.post.create_webhook_post.creating.app_error", nil, "err="+err.Message) + } + + return post, nil +} -- cgit v1.2.3-1-g7c22