Docs, tests
parent
b7679c7826
commit
499b2fb0d6
|
@ -623,6 +623,35 @@ them with a comma, e.g. `tag1,tag2,tag3`.
|
|||
as [RFC 2047](https://datatracker.ietf.org/doc/html/rfc2047#section-2), e.g. `tag1,=?UTF-8?B?8J+HqfCfh6o=?=` ([base64](https://en.wikipedia.org/wiki/Base64)),
|
||||
or `=?UTF-8?Q?=C3=84pfel?=,tag2` ([quoted-printable](https://en.wikipedia.org/wiki/Quoted-printable)).
|
||||
|
||||
## Markdown
|
||||
_Supported on:_ :material-firefox:
|
||||
|
||||
You can format messages using [Markdown](https://www.markdownguide.org/basic-syntax/). 🤩
|
||||
|
||||
By default, messages sent to ntfy are rendered as plain text. To enable Markdown, set the `X-Markdown` header (or any of
|
||||
its aliases: `Markdown`, or `md`) to `true` (or `1` or `yes`), or set the `Content-Type` header to `text/markdown`.
|
||||
|
||||
Supported Markdown features:
|
||||
|
||||
- **bold** (`**bold**`)
|
||||
- *italic* (`*italic*`)
|
||||
- [links](https://www.markdownguide.org/basic-syntax/#links) (`[links](https://www.markdownguide.org/basic-syntax/#links)`)
|
||||
- [images](https://www.markdownguide.org/basic-syntax/#images) (`![images](https://www.markdownguide.org/basic-syntax/#images)`)
|
||||
- [code blocks](https://www.markdownguide.org/basic-syntax/#code-blocks) (`` `code blocks` ``)
|
||||
- [inline code](https://www.markdownguide.org/basic-syntax/#inline-code) (`` `inline code` ``)
|
||||
- [headings](https://www.markdownguide.org/basic-syntax/#headings) (`# headings`)
|
||||
- [lists](https://www.markdownguide.org/basic-syntax/#lists) (`- lists`)
|
||||
- [blockquotes](https://www.markdownguide.org/basic-syntax/#blockquotes) (`> blockquotes`)
|
||||
- [horizontal rules](https://www.markdownguide.org/basic-syntax/#horizontal-rules) (`---`)
|
||||
|
||||
XXXXXXXXXXXXXXXXXXXXXx
|
||||
- examples
|
||||
- supported only on Web for now
|
||||
|
||||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXxx
|
||||
|
||||
|
||||
|
||||
## Scheduled delivery
|
||||
_Supported on:_ :material-android: :material-apple: :material-firefox:
|
||||
|
||||
|
@ -1004,6 +1033,7 @@ all the supported fields:
|
|||
| `actions` | - | *JSON array* | *(see [action buttons](#action-buttons))* | Custom [user action buttons](#action-buttons) for notifications |
|
||||
| `click` | - | *URL* | `https://example.com` | Website opened when notification is [clicked](#click-action) |
|
||||
| `attach` | - | *URL* | `https://example.com/file.jpg` | URL of an attachment, see [attach via URL](#attach-file-from-url) |
|
||||
| `markdown` | - | *bool* | `true` | Set to true if the `message` is Markdown-formatted |
|
||||
| `icon` | - | *string* | `https://example.com/icon.png` | URL to use as notification [icon](#icons) |
|
||||
| `filename` | - | *string* | `file.jpg` | File name of the attachment |
|
||||
| `delay` | - | *string* | `30min`, `9am` | Timestamp or duration for delayed delivery |
|
||||
|
|
|
@ -1010,7 +1010,7 @@ func (s *Server) parsePublishParams(r *http.Request, m *message) (cache bool, fi
|
|||
return false, false, "", "", false, errHTTPBadRequestActionsInvalid.Wrap(e.Error())
|
||||
}
|
||||
}
|
||||
contentType, markdown := readParam(r, "content-type"), readBoolParam(r, false, "x-markdown", "markdown", "md")
|
||||
contentType, markdown := readParam(r, "content-type", "content_type"), readBoolParam(r, false, "x-markdown", "markdown", "md")
|
||||
if markdown || strings.ToLower(contentType) == "text/markdown" {
|
||||
m.ContentType = "text/markdown"
|
||||
}
|
||||
|
@ -1789,6 +1789,9 @@ func (s *Server) transformBodyJSON(next handleFunc) handleFunc {
|
|||
if m.Icon != "" {
|
||||
r.Header.Set("X-Icon", m.Icon)
|
||||
}
|
||||
if m.Markdown {
|
||||
r.Header.Set("X-Markdown", "yes")
|
||||
}
|
||||
if len(m.Actions) > 0 {
|
||||
actionsStr, err := json.Marshal(m.Actions)
|
||||
if err != nil {
|
||||
|
|
|
@ -1518,6 +1518,39 @@ func TestServer_PublishActions_AndPoll(t *testing.T) {
|
|||
require.Equal(t, "target_temp_f=65", m.Actions[1].Body)
|
||||
}
|
||||
|
||||
func TestServer_PublishMarkdown(t *testing.T) {
|
||||
s := newTestServer(t, newTestConfig(t))
|
||||
response := request(t, s, "PUT", "/mytopic", "_underline this_", map[string]string{
|
||||
"Content-Type": "text/markdown",
|
||||
})
|
||||
require.Equal(t, 200, response.Code)
|
||||
|
||||
m := toMessage(t, response.Body.String())
|
||||
require.Equal(t, "_underline this_", m.Message)
|
||||
require.Equal(t, "text/markdown", m.ContentType)
|
||||
}
|
||||
|
||||
func TestServer_PublishMarkdown_QueryParam(t *testing.T) {
|
||||
s := newTestServer(t, newTestConfig(t))
|
||||
response := request(t, s, "PUT", "/mytopic?md=1", "_underline this_", nil)
|
||||
require.Equal(t, 200, response.Code)
|
||||
|
||||
m := toMessage(t, response.Body.String())
|
||||
require.Equal(t, "_underline this_", m.Message)
|
||||
require.Equal(t, "text/markdown", m.ContentType)
|
||||
}
|
||||
|
||||
func TestServer_PublishMarkdown_NotMarkdown(t *testing.T) {
|
||||
s := newTestServer(t, newTestConfig(t))
|
||||
response := request(t, s, "PUT", "/mytopic", "_underline this_", map[string]string{
|
||||
"Content-Type": "not-markdown",
|
||||
})
|
||||
require.Equal(t, 200, response.Code)
|
||||
|
||||
m := toMessage(t, response.Body.String())
|
||||
require.Equal(t, "", m.ContentType)
|
||||
}
|
||||
|
||||
func TestServer_PublishAsJSON(t *testing.T) {
|
||||
s := newTestServer(t, newTestConfig(t))
|
||||
body := `{"topic":"mytopic","message":"A message","title":"a title\nwith lines","tags":["tag1","tag 2"],` +
|
||||
|
@ -1535,12 +1568,25 @@ func TestServer_PublishAsJSON(t *testing.T) {
|
|||
require.Equal(t, "google.pdf", m.Attachment.Name)
|
||||
require.Equal(t, "http://ntfy.sh", m.Click)
|
||||
require.Equal(t, "https://ntfy.sh/static/img/ntfy.png", m.Icon)
|
||||
require.Equal(t, "", m.ContentType)
|
||||
|
||||
require.Equal(t, 4, m.Priority)
|
||||
require.True(t, m.Time > time.Now().Unix()+29*60)
|
||||
require.True(t, m.Time < time.Now().Unix()+31*60)
|
||||
}
|
||||
|
||||
func TestServer_PublishAsJSON_Markdown(t *testing.T) {
|
||||
s := newTestServer(t, newTestConfig(t))
|
||||
body := `{"topic":"mytopic","message":"**This is bold**","markdown":true}`
|
||||
response := request(t, s, "PUT", "/", body, nil)
|
||||
require.Equal(t, 200, response.Code)
|
||||
|
||||
m := toMessage(t, response.Body.String())
|
||||
require.Equal(t, "mytopic", m.Topic)
|
||||
require.Equal(t, "**This is bold**", m.Message)
|
||||
require.Equal(t, "text/markdown", m.ContentType)
|
||||
}
|
||||
|
||||
func TestServer_PublishAsJSON_RateLimit_MessageDailyLimit(t *testing.T) {
|
||||
// Publishing as JSON follows a different path. This ensures that rate
|
||||
// limiting works for this endpoint as well
|
||||
|
|
|
@ -101,6 +101,7 @@ type publishMessage struct {
|
|||
Icon string `json:"icon"`
|
||||
Actions []action `json:"actions"`
|
||||
Attach string `json:"attach"`
|
||||
Markdown bool `json:"markdown"`
|
||||
Filename string `json:"filename"`
|
||||
Email string `json:"email"`
|
||||
Call string `json:"call"`
|
||||
|
|
|
@ -192,7 +192,7 @@ const MarkdownContainer = styled("div")`
|
|||
ol {
|
||||
padding-inline: 1rem;
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue