Implement outgoing webhooks.
Этот коммит содержится в:
@@ -145,7 +145,7 @@ func echoCommand(c *Context, command *model.Command) bool {
|
||||
|
||||
time.Sleep(time.Duration(delay) * time.Second)
|
||||
|
||||
if _, err := CreatePost(c, post, false); err != nil {
|
||||
if _, err := CreatePost(c, post, true); err != nil {
|
||||
l4g.Error("Unable to create /echo post, err=%v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
134
api/post.go
134
api/post.go
@@ -55,11 +55,15 @@ func createPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
return
|
||||
} else {
|
||||
if result := <-Srv.Store.Channel().UpdateLastViewedAt(post.ChannelId, c.Session.UserId); result.Err != nil {
|
||||
l4g.Error("Encountered error updating last viewed, channel_id=%s, user_id=%s, err=%v", post.ChannelId, c.Session.UserId, result.Err)
|
||||
}
|
||||
|
||||
w.Write([]byte(rp.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func CreatePost(c *Context, post *model.Post, doUpdateLastViewed bool) (*model.Post, *model.AppError) {
|
||||
func CreatePost(c *Context, post *model.Post, triggerWebhooks bool) (*model.Post, *model.AppError) {
|
||||
var pchan store.StoreChannel
|
||||
if len(post.RootId) > 0 {
|
||||
pchan = Srv.Store.Post().Get(post.RootId)
|
||||
@@ -130,18 +134,142 @@ func CreatePost(c *Context, post *model.Post, doUpdateLastViewed bool) (*model.P
|
||||
var rpost *model.Post
|
||||
if result := <-Srv.Store.Post().Save(post); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else if doUpdateLastViewed && (<-Srv.Store.Channel().UpdateLastViewedAt(post.ChannelId, c.Session.UserId)).Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
rpost = result.Data.(*model.Post)
|
||||
|
||||
fireAndForgetNotifications(rpost, c.Session.TeamId, c.GetSiteURL())
|
||||
|
||||
if triggerWebhooks {
|
||||
fireAndForgetWebhookEvent(c, rpost)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return rpost, nil
|
||||
}
|
||||
|
||||
func fireAndForgetWebhookEvent(c *Context, post *model.Post) {
|
||||
|
||||
go func() {
|
||||
|
||||
chchan := Srv.Store.Webhook().GetOutgoingByChannel(post.ChannelId)
|
||||
|
||||
firstWord := strings.Split(post.Message, " ")[0]
|
||||
thchan := Srv.Store.Webhook().GetOutgoingByTriggerWord(c.Session.TeamId, post.ChannelId, firstWord)
|
||||
|
||||
hooks := []*model.OutgoingWebhook{}
|
||||
|
||||
if result := <-chchan; result.Err != nil {
|
||||
l4g.Error("Encountered error getting webhook by channel, err=%v", result.Err)
|
||||
return
|
||||
} else {
|
||||
hooks = append(hooks, result.Data.([]*model.OutgoingWebhook)...)
|
||||
}
|
||||
|
||||
if result := <-thchan; result.Err != nil {
|
||||
l4g.Error("Encountered error getting webhook by trigger word, err=%v", result.Err)
|
||||
return
|
||||
} else {
|
||||
hooks = append(hooks, result.Data.([]*model.OutgoingWebhook)...)
|
||||
}
|
||||
|
||||
cchan := Srv.Store.Channel().Get(post.ChannelId)
|
||||
uchan := Srv.Store.User().Get(post.UserId)
|
||||
|
||||
tchan := make(chan *model.Team)
|
||||
isTeamBeingFetched := make(map[string]bool)
|
||||
for _, hook := range hooks {
|
||||
if !isTeamBeingFetched[hook.TeamId] {
|
||||
isTeamBeingFetched[hook.TeamId] = true
|
||||
go func() {
|
||||
if result := <-Srv.Store.Team().Get(hook.TeamId); result.Err != nil {
|
||||
l4g.Error("Encountered error getting team, team_id=%s, err=%v", hook.TeamId, result.Err)
|
||||
tchan <- nil
|
||||
} else {
|
||||
tchan <- result.Data.(*model.Team)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
teams := make(map[string]*model.Team)
|
||||
for _, _ = range isTeamBeingFetched {
|
||||
team := <-tchan
|
||||
if team != nil {
|
||||
teams[team.Id] = team
|
||||
}
|
||||
}
|
||||
|
||||
var channel *model.Channel
|
||||
if result := <-cchan; result.Err != nil {
|
||||
l4g.Error("Encountered error getting channel, channel_id=%s, err=%v", post.ChannelId, result.Err)
|
||||
} else {
|
||||
channel = result.Data.(*model.Channel)
|
||||
}
|
||||
|
||||
var user *model.User
|
||||
if result := <-uchan; result.Err != nil {
|
||||
l4g.Error("Encountered error getting user, user_id=%s, err=%v", post.UserId, result.Err)
|
||||
} else {
|
||||
user = result.Data.(*model.User)
|
||||
}
|
||||
|
||||
for _, hook := range hooks {
|
||||
go func() {
|
||||
p := url.Values{}
|
||||
p.Set("token", hook.Token)
|
||||
p.Set("team_id", hook.TeamId)
|
||||
|
||||
if team, ok := teams[hook.TeamId]; ok {
|
||||
p.Set("team_domain", team.Name)
|
||||
}
|
||||
|
||||
p.Set("channel_id", post.ChannelId)
|
||||
if channel != nil {
|
||||
p.Set("channel_name", channel.Name)
|
||||
}
|
||||
|
||||
p.Set("timestamp", strconv.FormatInt(post.CreateAt/1000, 10))
|
||||
|
||||
p.Set("user_id", post.UserId)
|
||||
if user != nil {
|
||||
p.Set("user_name", user.Username)
|
||||
}
|
||||
|
||||
p.Set("text", post.Message)
|
||||
if len(hook.TriggerWords) > 0 {
|
||||
p.Set("trigger_word", firstWord)
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
|
||||
for _, url := range hook.CallbackURLs {
|
||||
go func() {
|
||||
req, _ := http.NewRequest("POST", url, strings.NewReader(p.Encode()))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("Accept", "application/json")
|
||||
if resp, err := client.Do(req); err != nil {
|
||||
l4g.Error("Event POST failed, err=%s", err.Error())
|
||||
} else {
|
||||
respProps := model.MapFromJson(resp.Body)
|
||||
|
||||
if text, ok := respProps["text"]; ok {
|
||||
respPost := &model.Post{Message: text, ChannelId: post.ChannelId}
|
||||
if _, err := CreatePost(c, respPost, false); err != nil {
|
||||
l4g.Error("Failed to create response post, err=%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
}()
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
func fireAndForgetNotifications(post *model.Post, teamId, siteURL string) {
|
||||
|
||||
go func() {
|
||||
|
||||
169
api/webhook.go
169
api/webhook.go
@@ -18,6 +18,11 @@ func InitWebhook(r *mux.Router) {
|
||||
sr.Handle("/incoming/create", ApiUserRequired(createIncomingHook)).Methods("POST")
|
||||
sr.Handle("/incoming/delete", ApiUserRequired(deleteIncomingHook)).Methods("POST")
|
||||
sr.Handle("/incoming/list", ApiUserRequired(getIncomingHooks)).Methods("GET")
|
||||
|
||||
sr.Handle("/outgoing/create", ApiUserRequired(createOutgoingHook)).Methods("POST")
|
||||
sr.Handle("/outgoing/regen_token", ApiUserRequired(regenOutgoingHookToken)).Methods("POST")
|
||||
sr.Handle("/outgoing/delete", ApiUserRequired(deleteOutgoingHook)).Methods("POST")
|
||||
sr.Handle("/outgoing/list", ApiUserRequired(getOutgoingHooks)).Methods("GET")
|
||||
}
|
||||
|
||||
func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
@@ -50,9 +55,11 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
channel = result.Data.(*model.Channel)
|
||||
}
|
||||
|
||||
if !c.HasPermissionsToChannel(pchan, "createIncomingHook") && channel.Type != model.CHANNEL_OPEN {
|
||||
c.LogAudit("fail - bad channel permissions")
|
||||
return
|
||||
if !c.HasPermissionsToChannel(pchan, "createIncomingHook") {
|
||||
if channel.Type != model.CHANNEL_OPEN || channel.TeamId != c.Session.TeamId {
|
||||
c.LogAudit("fail - bad channel permissions")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Webhook().SaveIncoming(hook); result.Err != nil {
|
||||
@@ -67,7 +74,7 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !utils.Cfg.ServiceSettings.EnableIncomingWebhooks {
|
||||
c.Err = model.NewAppError("createIncomingHook", "Incoming webhooks have been disabled by the system admin.", "")
|
||||
c.Err = model.NewAppError("deleteIncomingHook", "Incoming webhooks have been disabled by the system admin.", "")
|
||||
c.Err.StatusCode = http.StatusNotImplemented
|
||||
return
|
||||
}
|
||||
@@ -87,7 +94,7 @@ func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
} else {
|
||||
if c.Session.UserId != result.Data.(*model.IncomingWebhook).UserId && !c.IsTeamAdmin() {
|
||||
c.LogAudit("fail - inappropriate conditions")
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.Err = model.NewAppError("deleteIncomingHook", "Inappropriate permissions to delete incoming webhook", "user_id="+c.Session.UserId)
|
||||
return
|
||||
}
|
||||
@@ -104,7 +111,7 @@ func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !utils.Cfg.ServiceSettings.EnableIncomingWebhooks {
|
||||
c.Err = model.NewAppError("createIncomingHook", "Incoming webhooks have been disabled by the system admin.", "")
|
||||
c.Err = model.NewAppError("getIncomingHooks", "Incoming webhooks have been disabled by the system admin.", "")
|
||||
c.Err.StatusCode = http.StatusNotImplemented
|
||||
return
|
||||
}
|
||||
@@ -117,3 +124,153 @@ func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(model.IncomingWebhookListToJson(hooks)))
|
||||
}
|
||||
}
|
||||
|
||||
func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !utils.Cfg.ServiceSettings.EnableOutgoingWebhooks {
|
||||
c.Err = model.NewAppError("createOutgoingHook", "Outgoing webhooks have been disabled by the system admin.", "")
|
||||
c.Err.StatusCode = http.StatusNotImplemented
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
hook := model.OutgoingWebhookFromJson(r.Body)
|
||||
|
||||
if hook == nil {
|
||||
c.SetInvalidParam("createOutgoingHook", "webhook")
|
||||
return
|
||||
}
|
||||
|
||||
hook.CreatorId = c.Session.UserId
|
||||
hook.TeamId = c.Session.TeamId
|
||||
|
||||
if len(hook.ChannelId) != 0 {
|
||||
cchan := Srv.Store.Channel().Get(hook.ChannelId)
|
||||
pchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, hook.ChannelId, c.Session.UserId)
|
||||
|
||||
var channel *model.Channel
|
||||
if result := <-cchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
channel = result.Data.(*model.Channel)
|
||||
}
|
||||
|
||||
if channel.Type != model.CHANNEL_OPEN {
|
||||
c.LogAudit("fail - not open channel")
|
||||
}
|
||||
|
||||
if !c.HasPermissionsToChannel(pchan, "createOutgoingHook") {
|
||||
if channel.Type != model.CHANNEL_OPEN || channel.TeamId != c.Session.TeamId {
|
||||
c.LogAudit("fail - bad channel permissions")
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if len(hook.TriggerWords) == 0 {
|
||||
c.Err = model.NewAppError("createOutgoingHook", "Either trigger_words or channel_id must be set", "")
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Webhook().SaveOutgoing(hook); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
c.LogAudit("success")
|
||||
rhook := result.Data.(*model.OutgoingWebhook)
|
||||
w.Write([]byte(rhook.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getOutgoingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !utils.Cfg.ServiceSettings.EnableOutgoingWebhooks {
|
||||
c.Err = model.NewAppError("getOutgoingHooks", "Outgoing webhooks have been disabled by the system admin.", "")
|
||||
c.Err.StatusCode = http.StatusNotImplemented
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Webhook().GetOutgoingByCreator(c.Session.UserId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
hooks := result.Data.([]*model.OutgoingWebhook)
|
||||
w.Write([]byte(model.OutgoingWebhookListToJson(hooks)))
|
||||
}
|
||||
}
|
||||
|
||||
func deleteOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !utils.Cfg.ServiceSettings.EnableIncomingWebhooks {
|
||||
c.Err = model.NewAppError("deleteOutgoingHook", "Outgoing webhooks have been disabled by the system admin.", "")
|
||||
c.Err.StatusCode = http.StatusNotImplemented
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
id := props["id"]
|
||||
if len(id) == 0 {
|
||||
c.SetInvalidParam("deleteIncomingHook", "id")
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Webhook().GetOutgoing(id); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
if c.Session.UserId != result.Data.(*model.OutgoingWebhook).CreatorId && !c.IsTeamAdmin() {
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.Err = model.NewAppError("deleteOutgoingHook", "Inappropriate permissions to delete outcoming webhook", "user_id="+c.Session.UserId)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := (<-Srv.Store.Webhook().DeleteOutgoing(id, model.GetMillis())).Err; err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("success")
|
||||
w.Write([]byte(model.MapToJson(props)))
|
||||
}
|
||||
|
||||
func regenOutgoingHookToken(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !utils.Cfg.ServiceSettings.EnableIncomingWebhooks {
|
||||
c.Err = model.NewAppError("regenOutgoingHookToken", "Outgoing webhooks have been disabled by the system admin.", "")
|
||||
c.Err.StatusCode = http.StatusNotImplemented
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
id := props["id"]
|
||||
if len(id) == 0 {
|
||||
c.SetInvalidParam("regenOutgoingHookToken", "id")
|
||||
return
|
||||
}
|
||||
|
||||
var hook *model.OutgoingWebhook
|
||||
if result := <-Srv.Store.Webhook().GetOutgoing(id); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
hook = result.Data.(*model.OutgoingWebhook)
|
||||
|
||||
if c.Session.UserId != hook.CreatorId && !c.IsTeamAdmin() {
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.Err = model.NewAppError("regenOutgoingHookToken", "Inappropriate permissions to regenerate outcoming webhook token", "user_id="+c.Session.UserId)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
hook.Token = model.NewId()
|
||||
|
||||
if result := <-Srv.Store.Webhook().UpdateOutgoing(hook); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(result.Data.(*model.OutgoingWebhook).ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user