mirror of
https://github.com/cwinfo/matterbridge.git
synced 2025-09-19 01:22:30 +00:00
.github
bridge
contrib
docker
gateway
hook
img
internal
matterclient
matterhook
vendor
github.com
42wim
Baozisoftware
Benau
Jeffail
Philipp15b
Rhymen
SevereCloud
apex
av-elier
blang
d5
davecgh
dustin
dyatlov
francoispqt
fsnotify
go-asn1-ber
go-telegram-bot-api
golang
golang-jwt
gomarkdown
google
gopackage
gorilla
harmony-development
hashicorp
jpillora
json-iterator
kettek
keybase
klauspost
kyokomi
labstack
lrstanley
magiconair
matrix-org
matterbridge
mattermost
go-i18n
ldap
logr
mattermost-server
v5
v6
model
access.go
analytics_row.go
audit.go
auditconv.go
audits.go
authorize.go
bot.go
builtin.go
bundle_info.go
channel.go
channel_count.go
channel_data.go
channel_list.go
channel_member.go
channel_member_history.go
channel_member_history_result.go
channel_mentions.go
channel_search.go
channel_sidebar.go
channel_stats.go
channel_view.go
client4.go
cloud.go
cluster_discovery.go
cluster_info.go
cluster_message.go
cluster_stats.go
command.go
command_args.go
command_autocomplete.go
command_request.go
command_response.go
command_webhook.go
compliance.go
compliance_post.go
config.go
custom_status.go
data_retention_policy.go
emoji.go
emoji_data.go
emoji_search.go
feature_flags.go
file.go
file_info.go
file_info_list.go
file_info_search_results.go
gitlab.go
group.go
group_member.go
group_syncable.go
guest_invite.go
incoming_webhook.go
initial_load.go
integration_action.go
integrity.go
job.go
ldap.go
license.go
link_metadata.go
manifest.go
marketplace_plugin.go
mention_map.go
message_export.go
mfa_secret.go
migration.go
oauth.go
outgoing_webhook.go
permalink.go
permission.go
plugin_cluster_event.go
plugin_event_data.go
plugin_key_value.go
plugin_kvset_options.go
plugin_status.go
plugin_valid.go
plugins_response.go
post.go
post_embed.go
post_list.go
post_metadata.go
post_search_results.go
preference.go
product_notices.go
push_notification.go
push_response.go
reaction.go
remote_cluster.go
role.go
saml.go
scheduled_task.go
scheme.go
search_params.go
security_bulletin.go
session.go
session_serial_gen.go
shared_channel.go
slack_attachment.go
slack_compatibility.go
status.go
suggest_command.go
switch_request.go
system.go
team.go
team_member.go
team_member_serial_gen.go
team_search.go
team_stats.go
terms_of_service.go
thread.go
token.go
typing_request.go
upload_session.go
user.go
user_access_token.go
user_access_token_search.go
user_autocomplete.go
user_count.go
user_get.go
user_search.go
user_serial_gen.go
user_terms_of_service.go
users_stats.go
utils.go
version.go
websocket_client.go
websocket_message.go
websocket_request.go
services
shared
utils
LICENSE.txt
NOTICE.txt
mattn
mgutz
minio
missdeer
mitchellh
modern-go
monaco-io
mreiferson
mrexodia
nelsonken
paulrosania
pborman
pelletier
philhofer
pkg
pmezard
rickb777
rivo
rs
russross
saintfish
shazow
sirupsen
sizeofint
skip2
slack-go
spf13
stretchr
subosito
tinylib
valyala
vincent-petithory
wiggin77
writeas
yaegashi
zfjagann
go.uber.org
golang.org
gomod.garykim.dev
google.golang.org
gopkg.in
layeh.com
modules.txt
version
.dockerignore
.fixmie.yml
.gitignore
.golangci.yaml
.goreleaser.yml
Dockerfile
LICENSE
README.md
changelog.md
go.mod
go.sum
matterbridge.go
matterbridge.toml.sample
matterbridge.toml.simple
tgs.Dockerfile
185 lines
5.8 KiB
Go
185 lines
5.8 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package model
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"regexp"
|
|
)
|
|
|
|
const (
|
|
DefaultWebhookUsername = "webhook"
|
|
)
|
|
|
|
type IncomingWebhook struct {
|
|
Id string `json:"id"`
|
|
CreateAt int64 `json:"create_at"`
|
|
UpdateAt int64 `json:"update_at"`
|
|
DeleteAt int64 `json:"delete_at"`
|
|
UserId string `json:"user_id"`
|
|
ChannelId string `json:"channel_id"`
|
|
TeamId string `json:"team_id"`
|
|
DisplayName string `json:"display_name"`
|
|
Description string `json:"description"`
|
|
Username string `json:"username"`
|
|
IconURL string `json:"icon_url"`
|
|
ChannelLocked bool `json:"channel_locked"`
|
|
}
|
|
|
|
type IncomingWebhookRequest struct {
|
|
Text string `json:"text"`
|
|
Username string `json:"username"`
|
|
IconURL string `json:"icon_url"`
|
|
ChannelName string `json:"channel"`
|
|
Props StringInterface `json:"props"`
|
|
Attachments []*SlackAttachment `json:"attachments"`
|
|
Type string `json:"type"`
|
|
IconEmoji string `json:"icon_emoji"`
|
|
}
|
|
|
|
func (o *IncomingWebhook) IsValid() *AppError {
|
|
if !IsValidId(o.Id) {
|
|
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.id.app_error", nil, "", http.StatusBadRequest)
|
|
|
|
}
|
|
|
|
if o.CreateAt == 0 {
|
|
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
|
}
|
|
|
|
if o.UpdateAt == 0 {
|
|
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
|
}
|
|
|
|
if !IsValidId(o.UserId) {
|
|
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.user_id.app_error", nil, "", http.StatusBadRequest)
|
|
}
|
|
|
|
if !IsValidId(o.ChannelId) {
|
|
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.channel_id.app_error", nil, "", http.StatusBadRequest)
|
|
}
|
|
|
|
if !IsValidId(o.TeamId) {
|
|
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.team_id.app_error", nil, "", http.StatusBadRequest)
|
|
}
|
|
|
|
if len(o.DisplayName) > 64 {
|
|
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.display_name.app_error", nil, "", http.StatusBadRequest)
|
|
}
|
|
|
|
if len(o.Description) > 500 {
|
|
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.description.app_error", nil, "", http.StatusBadRequest)
|
|
}
|
|
|
|
if len(o.Username) > 64 {
|
|
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.username.app_error", nil, "", http.StatusBadRequest)
|
|
}
|
|
|
|
if len(o.IconURL) > 1024 {
|
|
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.icon_url.app_error", nil, "", http.StatusBadRequest)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (o *IncomingWebhook) PreSave() {
|
|
if o.Id == "" {
|
|
o.Id = NewId()
|
|
}
|
|
|
|
o.CreateAt = GetMillis()
|
|
o.UpdateAt = o.CreateAt
|
|
}
|
|
|
|
func (o *IncomingWebhook) PreUpdate() {
|
|
o.UpdateAt = GetMillis()
|
|
}
|
|
|
|
// escapeControlCharsFromPayload escapes control chars (\n, \t) from a byte slice.
|
|
// Context:
|
|
// JSON strings are not supposed to contain control characters such as \n, \t,
|
|
// ... but some incoming webhooks might still send invalid JSON and we want to
|
|
// try to handle that. An example invalid JSON string from an incoming webhook
|
|
// might look like this (strings for both "text" and "fallback" attributes are
|
|
// invalid JSON strings because they contain unescaped newlines and tabs):
|
|
// `{
|
|
// "text": "this is a test
|
|
// that contains a newline and tabs",
|
|
// "attachments": [
|
|
// {
|
|
// "fallback": "Required plain-text summary of the attachment
|
|
// that contains a newline and tabs",
|
|
// "color": "#36a64f",
|
|
// ...
|
|
// "text": "Optional text that appears within the attachment
|
|
// that contains a newline and tabs",
|
|
// ...
|
|
// "thumb_url": "http://example.com/path/to/thumb.png"
|
|
// }
|
|
// ]
|
|
// }`
|
|
// This function will search for `"key": "value"` pairs, and escape \n, \t
|
|
// from the value.
|
|
func escapeControlCharsFromPayload(by []byte) []byte {
|
|
// we'll search for `"text": "..."` or `"fallback": "..."`, ...
|
|
keys := "text|fallback|pretext|author_name|title|value"
|
|
|
|
// the regexp reads like this:
|
|
// (?s): this flag let . match \n (default is false)
|
|
// "(keys)": we search for the keys defined above
|
|
// \s*:\s*: followed by 0..n spaces/tabs, a colon then 0..n spaces/tabs
|
|
// ": a double-quote
|
|
// (\\"|[^"])*: any number of times the `\"` string or any char but a double-quote
|
|
// ": a double-quote
|
|
r := `(?s)"(` + keys + `)"\s*:\s*"(\\"|[^"])*"`
|
|
re := regexp.MustCompile(r)
|
|
|
|
// the function that will escape \n and \t on the regexp matches
|
|
repl := func(b []byte) []byte {
|
|
if bytes.Contains(b, []byte("\n")) {
|
|
b = bytes.Replace(b, []byte("\n"), []byte("\\n"), -1)
|
|
}
|
|
if bytes.Contains(b, []byte("\t")) {
|
|
b = bytes.Replace(b, []byte("\t"), []byte("\\t"), -1)
|
|
}
|
|
|
|
return b
|
|
}
|
|
|
|
return re.ReplaceAllFunc(by, repl)
|
|
}
|
|
|
|
func decodeIncomingWebhookRequest(by []byte) (*IncomingWebhookRequest, error) {
|
|
decoder := json.NewDecoder(bytes.NewReader(by))
|
|
var o IncomingWebhookRequest
|
|
err := decoder.Decode(&o)
|
|
if err == nil {
|
|
return &o, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
func IncomingWebhookRequestFromJSON(data io.Reader) (*IncomingWebhookRequest, *AppError) {
|
|
buf := new(bytes.Buffer)
|
|
buf.ReadFrom(data)
|
|
by := buf.Bytes()
|
|
|
|
// Try to decode the JSON data. Only if it fails, try to escape control
|
|
// characters from the strings contained in the JSON data.
|
|
o, err := decodeIncomingWebhookRequest(by)
|
|
if err != nil {
|
|
o, err = decodeIncomingWebhookRequest(escapeControlCharsFromPayload(by))
|
|
if err != nil {
|
|
return nil, NewAppError("IncomingWebhookRequestFromJSON", "model.incoming_hook.parse_data.app_error", nil, err.Error(), http.StatusBadRequest)
|
|
}
|
|
}
|
|
|
|
o.Attachments = StringifySlackFieldValue(o.Attachments)
|
|
|
|
return o, nil
|
|
}
|