Fix Slack webhook payload title generation to work with Mattermost (#9378)
* Fix Slack webhook payload title generation to work with Mattermost by using a markdown link instead of html * Fix Slack webhook attachment title and title link handling
This commit is contained in:
parent
669791be99
commit
04ab1138f6
1 changed files with 86 additions and 73 deletions
|
@ -47,10 +47,11 @@ type SlackPayload struct {
|
||||||
|
|
||||||
// SlackAttachment contains the slack message
|
// SlackAttachment contains the slack message
|
||||||
type SlackAttachment struct {
|
type SlackAttachment struct {
|
||||||
Fallback string `json:"fallback"`
|
Fallback string `json:"fallback"`
|
||||||
Color string `json:"color"`
|
Color string `json:"color"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Text string `json:"text"`
|
TitleLink string `json:"title_link"`
|
||||||
|
Text string `json:"text"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSecret sets the slack secret
|
// SetSecret sets the slack secret
|
||||||
|
@ -144,72 +145,78 @@ func getSlackForkPayload(p *api.ForkPayload, slack *SlackMeta) (*SlackPayload, e
|
||||||
|
|
||||||
func getSlackIssuesPayload(p *api.IssuePayload, slack *SlackMeta) (*SlackPayload, error) {
|
func getSlackIssuesPayload(p *api.IssuePayload, slack *SlackMeta) (*SlackPayload, error) {
|
||||||
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
||||||
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
|
title := SlackTextFormatter(fmt.Sprintf("#%d %s", p.Index, p.Issue.Title))
|
||||||
fmt.Sprintf("#%d %s", p.Index, p.Issue.Title))
|
titleLink := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index)
|
||||||
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
||||||
var text, title, attachmentText string
|
var text, attachmentText string
|
||||||
|
|
||||||
switch p.Action {
|
switch p.Action {
|
||||||
case api.HookIssueOpened:
|
case api.HookIssueOpened:
|
||||||
text = fmt.Sprintf("[%s] Issue submitted by %s", repoLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue opened by %s", repoLink, senderLink)
|
||||||
title = titleLink
|
|
||||||
attachmentText = SlackTextFormatter(p.Issue.Body)
|
attachmentText = SlackTextFormatter(p.Issue.Body)
|
||||||
case api.HookIssueClosed:
|
case api.HookIssueClosed:
|
||||||
text = fmt.Sprintf("[%s] Issue closed: %s by %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue closed: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueReOpened:
|
case api.HookIssueReOpened:
|
||||||
text = fmt.Sprintf("[%s] Issue re-opened: %s by %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue re-opened: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueEdited:
|
case api.HookIssueEdited:
|
||||||
text = fmt.Sprintf("[%s] Issue edited: %s by %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue edited: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
attachmentText = SlackTextFormatter(p.Issue.Body)
|
attachmentText = SlackTextFormatter(p.Issue.Body)
|
||||||
case api.HookIssueAssigned:
|
case api.HookIssueAssigned:
|
||||||
text = fmt.Sprintf("[%s] Issue assigned to %s: %s by %s", repoLink,
|
text = fmt.Sprintf("[%s] Issue assigned to %s: [%s](%s) by %s", repoLink,
|
||||||
SlackLinkFormatter(setting.AppURL+p.Issue.Assignee.UserName, p.Issue.Assignee.UserName),
|
SlackLinkFormatter(setting.AppURL+p.Issue.Assignee.UserName, p.Issue.Assignee.UserName),
|
||||||
titleLink, senderLink)
|
title, titleLink, senderLink)
|
||||||
case api.HookIssueUnassigned:
|
case api.HookIssueUnassigned:
|
||||||
text = fmt.Sprintf("[%s] Issue unassigned: %s by %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue unassigned: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueLabelUpdated:
|
case api.HookIssueLabelUpdated:
|
||||||
text = fmt.Sprintf("[%s] Issue labels updated: %s by %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue labels updated: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueLabelCleared:
|
case api.HookIssueLabelCleared:
|
||||||
text = fmt.Sprintf("[%s] Issue labels cleared: %s by %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue labels cleared: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueSynchronized:
|
case api.HookIssueSynchronized:
|
||||||
text = fmt.Sprintf("[%s] Issue synchronized: %s by %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue synchronized: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueMilestoned:
|
case api.HookIssueMilestoned:
|
||||||
text = fmt.Sprintf("[%s] Issue milestoned: #%s %s", repoLink, titleLink, senderLink)
|
mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.Issue.Milestone.ID)
|
||||||
|
text = fmt.Sprintf("[%s] Issue milestoned to [%s](%s): [%s](%s) by %s", repoLink,
|
||||||
|
p.Issue.Milestone.Title, mileStoneLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueDemilestoned:
|
case api.HookIssueDemilestoned:
|
||||||
text = fmt.Sprintf("[%s] Issue milestone cleared: #%s %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue milestone cleared: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SlackPayload{
|
pl := &SlackPayload{
|
||||||
Channel: slack.Channel,
|
Channel: slack.Channel,
|
||||||
Text: text,
|
Text: text,
|
||||||
Username: slack.Username,
|
Username: slack.Username,
|
||||||
IconURL: slack.IconURL,
|
IconURL: slack.IconURL,
|
||||||
Attachments: []SlackAttachment{{
|
}
|
||||||
Color: slack.Color,
|
if attachmentText != "" {
|
||||||
Title: title,
|
pl.Attachments = []SlackAttachment{{
|
||||||
Text: attachmentText,
|
Color: slack.Color,
|
||||||
}},
|
Title: title,
|
||||||
}, nil
|
TitleLink: titleLink,
|
||||||
|
Text: attachmentText,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (*SlackPayload, error) {
|
func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (*SlackPayload, error) {
|
||||||
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
||||||
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, models.CommentHashTag(p.Comment.ID)),
|
title := SlackTextFormatter(fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
|
||||||
fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
|
|
||||||
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
||||||
var text, title, attachmentText string
|
var text, titleLink, attachmentText string
|
||||||
|
|
||||||
switch p.Action {
|
switch p.Action {
|
||||||
case api.HookIssueCommentCreated:
|
case api.HookIssueCommentCreated:
|
||||||
text = fmt.Sprintf("[%s] New comment created by %s", repoLink, senderLink)
|
text = fmt.Sprintf("[%s] New comment created by %s", repoLink, senderLink)
|
||||||
title = titleLink
|
titleLink = fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, models.CommentHashTag(p.Comment.ID))
|
||||||
attachmentText = SlackTextFormatter(p.Comment.Body)
|
attachmentText = SlackTextFormatter(p.Comment.Body)
|
||||||
case api.HookIssueCommentEdited:
|
case api.HookIssueCommentEdited:
|
||||||
text = fmt.Sprintf("[%s] Comment edited by %s", repoLink, senderLink)
|
text = fmt.Sprintf("[%s] Comment edited by %s", repoLink, senderLink)
|
||||||
title = titleLink
|
titleLink = fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, models.CommentHashTag(p.Comment.ID))
|
||||||
attachmentText = SlackTextFormatter(p.Comment.Body)
|
attachmentText = SlackTextFormatter(p.Comment.Body)
|
||||||
case api.HookIssueCommentDeleted:
|
case api.HookIssueCommentDeleted:
|
||||||
text = fmt.Sprintf("[%s] Comment deleted by %s", repoLink, senderLink)
|
text = fmt.Sprintf("[%s] Comment deleted by %s", repoLink, senderLink)
|
||||||
title = SlackLinkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index),
|
titleLink = 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)
|
attachmentText = SlackTextFormatter(p.Comment.Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,9 +226,10 @@ func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (
|
||||||
Username: slack.Username,
|
Username: slack.Username,
|
||||||
IconURL: slack.IconURL,
|
IconURL: slack.IconURL,
|
||||||
Attachments: []SlackAttachment{{
|
Attachments: []SlackAttachment{{
|
||||||
Color: slack.Color,
|
Color: slack.Color,
|
||||||
Title: title,
|
Title: title,
|
||||||
Text: attachmentText,
|
TitleLink: titleLink,
|
||||||
|
Text: attachmentText,
|
||||||
}},
|
}},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -294,67 +302,75 @@ func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, e
|
||||||
|
|
||||||
func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) {
|
func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) {
|
||||||
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
||||||
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
|
title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
|
||||||
fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
|
titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
|
||||||
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
||||||
var text, title, attachmentText string
|
var text, attachmentText string
|
||||||
|
|
||||||
switch p.Action {
|
switch p.Action {
|
||||||
case api.HookIssueOpened:
|
case api.HookIssueOpened:
|
||||||
text = fmt.Sprintf("[%s] Pull request submitted by %s", repoLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request opened by %s", repoLink, senderLink)
|
||||||
title = titleLink
|
|
||||||
attachmentText = SlackTextFormatter(p.PullRequest.Body)
|
attachmentText = SlackTextFormatter(p.PullRequest.Body)
|
||||||
case api.HookIssueClosed:
|
case api.HookIssueClosed:
|
||||||
if p.PullRequest.HasMerged {
|
if p.PullRequest.HasMerged {
|
||||||
text = fmt.Sprintf("[%s] Pull request merged: %s by %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request merged: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
} else {
|
} else {
|
||||||
text = fmt.Sprintf("[%s] Pull request closed: %s by %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request closed: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
}
|
}
|
||||||
case api.HookIssueReOpened:
|
case api.HookIssueReOpened:
|
||||||
text = fmt.Sprintf("[%s] Pull request re-opened: %s by %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request re-opened: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueEdited:
|
case api.HookIssueEdited:
|
||||||
text = fmt.Sprintf("[%s] Pull request edited: %s by %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request edited: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
attachmentText = SlackTextFormatter(p.PullRequest.Body)
|
attachmentText = SlackTextFormatter(p.PullRequest.Body)
|
||||||
case api.HookIssueAssigned:
|
case api.HookIssueAssigned:
|
||||||
list := make([]string, len(p.PullRequest.Assignees))
|
list := make([]string, len(p.PullRequest.Assignees))
|
||||||
for i, user := range p.PullRequest.Assignees {
|
for i, user := range p.PullRequest.Assignees {
|
||||||
list[i] = SlackLinkFormatter(setting.AppURL+user.UserName, user.UserName)
|
list[i] = SlackLinkFormatter(setting.AppURL+user.UserName, user.UserName)
|
||||||
}
|
}
|
||||||
text = fmt.Sprintf("[%s] Pull request assigned to %s: %s by %s", repoLink,
|
text = fmt.Sprintf("[%s] Pull request assigned to %s: [%s](%s) by %s", repoLink,
|
||||||
strings.Join(list, ", "),
|
strings.Join(list, ", "),
|
||||||
titleLink, senderLink)
|
title, titleLink, senderLink)
|
||||||
case api.HookIssueUnassigned:
|
case api.HookIssueUnassigned:
|
||||||
text = fmt.Sprintf("[%s] Pull request unassigned: %s by %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request unassigned: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueLabelUpdated:
|
case api.HookIssueLabelUpdated:
|
||||||
text = fmt.Sprintf("[%s] Pull request labels updated: %s by %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request labels updated: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueLabelCleared:
|
case api.HookIssueLabelCleared:
|
||||||
text = fmt.Sprintf("[%s] Pull request labels cleared: %s by %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request labels cleared: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueSynchronized:
|
case api.HookIssueSynchronized:
|
||||||
text = fmt.Sprintf("[%s] Pull request synchronized: %s by %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request synchronized: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueMilestoned:
|
case api.HookIssueMilestoned:
|
||||||
text = fmt.Sprintf("[%s] Pull request milestoned: #%s %s", repoLink, titleLink, senderLink)
|
mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.PullRequest.Milestone.ID)
|
||||||
|
text = fmt.Sprintf("[%s] Pull request milestoned to [%s](%s): [%s](%s) %s", repoLink,
|
||||||
|
p.PullRequest.Milestone.Title, mileStoneLink, title, titleLink, senderLink)
|
||||||
case api.HookIssueDemilestoned:
|
case api.HookIssueDemilestoned:
|
||||||
text = fmt.Sprintf("[%s] Pull request milestone cleared: #%s %s", repoLink, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request milestone cleared: [%s](%s) %s", repoLink, title, titleLink, senderLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SlackPayload{
|
pl := &SlackPayload{
|
||||||
Channel: slack.Channel,
|
Channel: slack.Channel,
|
||||||
Text: text,
|
Text: text,
|
||||||
Username: slack.Username,
|
Username: slack.Username,
|
||||||
IconURL: slack.IconURL,
|
IconURL: slack.IconURL,
|
||||||
Attachments: []SlackAttachment{{
|
}
|
||||||
Color: slack.Color,
|
if attachmentText != "" {
|
||||||
Title: title,
|
pl.Attachments = []SlackAttachment{{
|
||||||
Text: attachmentText,
|
Color: slack.Color,
|
||||||
}},
|
Title: title,
|
||||||
}, nil
|
TitleLink: titleLink,
|
||||||
|
Text: attachmentText,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackMeta, event models.HookEventType) (*SlackPayload, error) {
|
func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackMeta, event models.HookEventType) (*SlackPayload, error) {
|
||||||
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
||||||
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
|
title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
|
||||||
fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
|
titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
|
||||||
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
||||||
var text, title, attachmentText string
|
var text string
|
||||||
|
|
||||||
switch p.Action {
|
switch p.Action {
|
||||||
case api.HookIssueSynchronized:
|
case api.HookIssueSynchronized:
|
||||||
action, err := parseHookPullRequestEventType(event)
|
action, err := parseHookPullRequestEventType(event)
|
||||||
|
@ -362,7 +378,7 @@ func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackM
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
text = fmt.Sprintf("[%s] Pull request review %s : %s by %s", repoLink, action, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request review %s: [%s](%s) by %s", repoLink, action, title, titleLink, senderLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SlackPayload{
|
return &SlackPayload{
|
||||||
|
@ -370,11 +386,6 @@ func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackM
|
||||||
Text: text,
|
Text: text,
|
||||||
Username: slack.Username,
|
Username: slack.Username,
|
||||||
IconURL: slack.IconURL,
|
IconURL: slack.IconURL,
|
||||||
Attachments: []SlackAttachment{{
|
|
||||||
Color: slack.Color,
|
|
||||||
Title: title,
|
|
||||||
Text: attachmentText,
|
|
||||||
}},
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,6 +393,7 @@ func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*Sla
|
||||||
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
||||||
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
||||||
var text, title, attachmentText string
|
var text, title, attachmentText string
|
||||||
|
|
||||||
switch p.Action {
|
switch p.Action {
|
||||||
case api.HookRepoCreated:
|
case api.HookRepoCreated:
|
||||||
text = fmt.Sprintf("[%s] Repository created by %s", repoLink, senderLink)
|
text = fmt.Sprintf("[%s] Repository created by %s", repoLink, senderLink)
|
||||||
|
@ -396,9 +408,10 @@ func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*Sla
|
||||||
Username: slack.Username,
|
Username: slack.Username,
|
||||||
IconURL: slack.IconURL,
|
IconURL: slack.IconURL,
|
||||||
Attachments: []SlackAttachment{{
|
Attachments: []SlackAttachment{{
|
||||||
Color: slack.Color,
|
Color: slack.Color,
|
||||||
Title: title,
|
Title: title,
|
||||||
Text: attachmentText,
|
TitleLink: title,
|
||||||
|
Text: attachmentText,
|
||||||
}},
|
}},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue