Cont'd, getting there

This commit is contained in:
binwiederhier 2023-05-15 22:06:43 -04:00
parent 4b9e0c5c38
commit deb4f24856
3 changed files with 44 additions and 62 deletions

View File

@ -2698,19 +2698,25 @@ title `You've Got Mail` to topic `sometopic` (see [ntfy.sh/sometopic](https://nt
## Phone calls ## Phone calls
_Supported on:_ :material-android: :material-apple: :material-firefox: _Supported on:_ :material-android: :material-apple: :material-firefox:
You can use ntfy to call a phone and **read the message out loud using text-to-speech**, by specifying a phone number a header. You can use ntfy to call a phone and **read the message out loud using text-to-speech**.
Similar to email notifications, this can be useful to blast-notify yourself on all possible channels, or to notify people that do not have Similar to email notifications, this can be useful to blast-notify yourself on all possible channels, or to notify people that do not have
the ntfy app installed on their phone. the ntfy app installed on their phone.
Phone numbers have to be previously verified (via the web app). To forward a message as a voice call, pass a phone number **Phone numbers have to be previously verified** (via the web app), so this feature is **only available to authenticated users**.
in the `X-Call` header (or its alias: `Call`), prefixed with a plus sign and the country code, e.g. `+12223334444`. You may To forward a message as a voice call, pass a phone number in the `X-Call` header (or its alias: `Call`), prefixed with a
also simply pass `yes` as a value if you only have one verified phone number. plus sign and the country code, e.g. `+12223334444`. You may also simply pass `yes` as a value to pick the first of your
verified phone numbers.
!!! info
As of today, the text-to-speed voice used will only support English. If there is demand for other languages, we'll
be happy to add support for that. Please [open an issue on GitHub](https://github.com/binwiederhier/ntfy/issues).
On ntfy.sh, this feature is only supported to [ntfy Pro](https://ntfy.sh/app) plans. On ntfy.sh, this feature is only supported to [ntfy Pro](https://ntfy.sh/app) plans.
=== "Command line (curl)" === "Command line (curl)"
``` ```
curl \ curl \
-u :tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2 \
-H "Call: +12223334444" \ -H "Call: +12223334444" \
-d "Your garage seems to be on fire. You should probably check that out, and call 0118 999 881 999 119 725 3 for help." \ -d "Your garage seems to be on fire. You should probably check that out, and call 0118 999 881 999 119 725 3 for help." \
ntfy.sh/alerts ntfy.sh/alerts
@ -2719,6 +2725,7 @@ On ntfy.sh, this feature is only supported to [ntfy Pro](https://ntfy.sh/app) pl
=== "ntfy CLI" === "ntfy CLI"
``` ```
ntfy publish \ ntfy publish \
--token=tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2 \
--call=+12223334444 \ --call=+12223334444 \
alerts "Your garage seems to be on fire. You should probably check that out, and call 0118 999 881 999 119 725 3 for help." alerts "Your garage seems to be on fire. You should probably check that out, and call 0118 999 881 999 119 725 3 for help."
``` ```
@ -2727,6 +2734,7 @@ On ntfy.sh, this feature is only supported to [ntfy Pro](https://ntfy.sh/app) pl
``` http ``` http
POST /alerts HTTP/1.1 POST /alerts HTTP/1.1
Host: ntfy.sh Host: ntfy.sh
Authorization: Bearer tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2
Call: +12223334444 Call: +12223334444
Your garage seems to be on fire. You should probably check that out, and call 0118 999 881 999 119 725 3 for help. Your garage seems to be on fire. You should probably check that out, and call 0118 999 881 999 119 725 3 for help.
@ -2738,9 +2746,8 @@ On ntfy.sh, this feature is only supported to [ntfy Pro](https://ntfy.sh/app) pl
method: 'POST', method: 'POST',
body: "Your garage seems to be on fire. You should probably check that out, and call 0118 999 881 999 119 725 3 for help.", body: "Your garage seems to be on fire. You should probably check that out, and call 0118 999 881 999 119 725 3 for help.",
headers: { headers: {
'Email': 'phil@example.com', 'Authorization': 'Bearer tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2',
'Tags': 'warning,skull,backup-host,ssh-login', 'Call': '+12223334444'
'Priority': 'high'
} }
}) })
``` ```
@ -2748,10 +2755,9 @@ On ntfy.sh, this feature is only supported to [ntfy Pro](https://ntfy.sh/app) pl
=== "Go" === "Go"
``` go ``` go
req, _ := http.NewRequest("POST", "https://ntfy.sh/alerts", req, _ := http.NewRequest("POST", "https://ntfy.sh/alerts",
strings.NewReader("Unknown login from 5.31.23.83 to backups.example.com")) strings.NewReader("Your garage seems to be on fire. You should probably check that out, and call 0118 999 881 999 119 725 3 for help."))
req.Header.Set("Email", "phil@example.com") req.Header.Set("Call", "+12223334444")
req.Header.Set("Tags", "warning,skull,backup-host,ssh-login") req.Header.Set("Authorization", "Bearer tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2")
req.Header.Set("Priority", "high")
http.DefaultClient.Do(req) http.DefaultClient.Do(req)
``` ```
@ -2761,12 +2767,10 @@ On ntfy.sh, this feature is only supported to [ntfy Pro](https://ntfy.sh/app) pl
Method = "POST" Method = "POST"
URI = "https://ntfy.sh/alerts" URI = "https://ntfy.sh/alerts"
Headers = @{ Headers = @{
Title = "Low disk space alert" Authorization = "Bearer tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2"
Priority = "high" Call = "+12223334444"
Tags = "warning,skull,backup-host,ssh-login")
Email = "phil@example.com"
} }
Body = "Unknown login from 5.31.23.83 to backups.example.com" Body = "Your garage seems to be on fire. You should probably check that out, and call 0118 999 881 999 119 725 3 for help."
} }
Invoke-RestMethod @Request Invoke-RestMethod @Request
``` ```
@ -2774,11 +2778,10 @@ On ntfy.sh, this feature is only supported to [ntfy Pro](https://ntfy.sh/app) pl
=== "Python" === "Python"
``` python ``` python
requests.post("https://ntfy.sh/alerts", requests.post("https://ntfy.sh/alerts",
data="Unknown login from 5.31.23.83 to backups.example.com", data="Your garage seems to be on fire. You should probably check that out, and call 0118 999 881 999 119 725 3 for help.",
headers={ headers={
"Email": "phil@example.com", "Authorization": "Bearer tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2",
"Tags": "warning,skull,backup-host,ssh-login", "Call": "+12223334444"
"Priority": "high"
}) })
``` ```
@ -2789,21 +2792,13 @@ On ntfy.sh, this feature is only supported to [ntfy Pro](https://ntfy.sh/app) pl
'method' => 'POST', 'method' => 'POST',
'header' => 'header' =>
"Content-Type: text/plain\r\n" . "Content-Type: text/plain\r\n" .
"Email: phil@example.com\r\n" . "Authorization: Bearer tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2\r\n" .
"Tags: warning,skull,backup-host,ssh-login\r\n" . "Call: +12223334444",
"Priority: high", 'content' => 'Your garage seems to be on fire. You should probably check that out, and call 0118 999 881 999 119 725 3 for help.'
'content' => 'Unknown login from 5.31.23.83 to backups.example.com'
] ]
])); ]));
``` ```
Here's what that looks like in Google Mail:
<figure markdown>
![e-mail notification](static/img/screenshot-email.png){ width=600 }
<figcaption>E-mail notification</figcaption>
</figure>
## Authentication ## Authentication
Depending on whether the server is configured to support [access control](config.md#access-control), some topics Depending on whether the server is configured to support [access control](config.md#access-control), some topics
may be read/write protected so that only users with the correct credentials can subscribe or publish to them. may be read/write protected so that only users with the correct credentials can subscribe or publish to them.

View File

@ -15,8 +15,6 @@ var (
metricEmailsPublishedFailure prometheus.Counter metricEmailsPublishedFailure prometheus.Counter
metricEmailsReceivedSuccess prometheus.Counter metricEmailsReceivedSuccess prometheus.Counter
metricEmailsReceivedFailure prometheus.Counter metricEmailsReceivedFailure prometheus.Counter
metricSMSSentSuccess prometheus.Counter
metricSMSSentFailure prometheus.Counter
metricCallsMadeSuccess prometheus.Counter metricCallsMadeSuccess prometheus.Counter
metricCallsMadeFailure prometheus.Counter metricCallsMadeFailure prometheus.Counter
metricUnifiedPushPublishedSuccess prometheus.Counter metricUnifiedPushPublishedSuccess prometheus.Counter
@ -61,12 +59,6 @@ func initMetrics() {
metricEmailsReceivedFailure = prometheus.NewCounter(prometheus.CounterOpts{ metricEmailsReceivedFailure = prometheus.NewCounter(prometheus.CounterOpts{
Name: "ntfy_emails_received_failure", Name: "ntfy_emails_received_failure",
}) })
metricSMSSentSuccess = prometheus.NewCounter(prometheus.CounterOpts{
Name: "ntfy_sms_sent_success",
})
metricSMSSentFailure = prometheus.NewCounter(prometheus.CounterOpts{
Name: "ntfy_sms_sent_failure",
})
metricCallsMadeSuccess = prometheus.NewCounter(prometheus.CounterOpts{ metricCallsMadeSuccess = prometheus.NewCounter(prometheus.CounterOpts{
Name: "ntfy_calls_made_success", Name: "ntfy_calls_made_success",
}) })
@ -111,8 +103,6 @@ func initMetrics() {
metricEmailsPublishedFailure, metricEmailsPublishedFailure,
metricEmailsReceivedSuccess, metricEmailsReceivedSuccess,
metricEmailsReceivedFailure, metricEmailsReceivedFailure,
metricSMSSentSuccess,
metricSMSSentFailure,
metricCallsMadeSuccess, metricCallsMadeSuccess,
metricCallsMadeFailure, metricCallsMadeFailure,
metricUnifiedPushPublishedSuccess, metricUnifiedPushPublishedSuccess,

View File

@ -15,19 +15,21 @@ import (
) )
const ( const (
twilioMessageFooterFormat = "This message was sent by %s via %s" twilioCallEndpoint = "Calls.json"
twilioCallEndpoint = "Calls.json" twilioCallFormat = `
twilioCallFormat = `
<Response> <Response>
<Pause length="1"/> <Pause length="1"/>
<Say>You have a message from notify on topic %s. Message:</Say> <Say loop="5">
<Pause length="1"/> You have a notification from notify on topic %s. Message:
<Say>%s</Say> <break time="1s"/>
<Pause length="1"/> %s
<Say>End message.</Say> <break time="1s"/>
<Pause length="1"/> End message.
<Say>%s</Say> <break time="1s"/>
<Pause length="1"/> This message was sent by user %s. It will be repeated up to five times.
<break time="3s"/>
</Say>
<Say>Goodbye.</Say>
</Response>` </Response>`
) )
@ -55,7 +57,11 @@ func (s *Server) convertPhoneNumber(u *user.User, phoneNumber string) (string, *
} }
func (s *Server) callPhone(v *visitor, r *http.Request, m *message, to string) { func (s *Server) callPhone(v *visitor, r *http.Request, m *message, to string) {
body := fmt.Sprintf(twilioCallFormat, xmlEscapeText(m.Topic), xmlEscapeText(m.Message), xmlEscapeText(s.messageFooter(v.User(), m))) u, sender := v.User(), m.Sender.String()
if u != nil {
sender = u.Name
}
body := fmt.Sprintf(twilioCallFormat, xmlEscapeText(m.Topic), xmlEscapeText(m.Message), xmlEscapeText(sender))
data := url.Values{} data := url.Values{}
data.Set("From", s.config.TwilioFromNumber) data.Set("From", s.config.TwilioFromNumber)
data.Set("To", to) data.Set("To", to)
@ -186,15 +192,6 @@ func (s *Server) performTwilioMessagingRequestInternal(endpoint string, data url
return string(response), nil return string(response), nil
} }
func (s *Server) messageFooter(u *user.User, m *message) string { // u may be nil!
topicURL := s.config.BaseURL + "/" + m.Topic
sender := m.Sender.String()
if u != nil {
sender = fmt.Sprintf("%s (%s)", u.Name, m.Sender)
}
return fmt.Sprintf(twilioMessageFooterFormat, sender, util.ShortTopicURL(topicURL))
}
func xmlEscapeText(text string) string { func xmlEscapeText(text string) string {
var buf bytes.Buffer var buf bytes.Buffer
_ = xml.EscapeText(&buf, []byte(text)) _ = xml.EscapeText(&buf, []byte(text))