mirror of
https://github.com/binwiederhier/ntfy.git
synced 2025-12-01 04:40:32 +01:00
Merge 16900d2c10 into b531bc95ea
This commit is contained in:
commit
543070db62
6 changed files with 98 additions and 1 deletions
|
|
@ -187,6 +187,7 @@ func execServe(c *cli.Context) error {
|
|||
twilioAuthToken := c.String("twilio-auth-token")
|
||||
twilioPhoneNumber := c.String("twilio-phone-number")
|
||||
twilioVerifyService := c.String("twilio-verify-service")
|
||||
twilioCallFormat := c.String("twilio-call-format")
|
||||
messageSizeLimitStr := c.String("message-size-limit")
|
||||
messageDelayLimitStr := c.String("message-delay-limit")
|
||||
totalTopicLimit := c.Int("global-topic-limit")
|
||||
|
|
@ -456,6 +457,7 @@ func execServe(c *cli.Context) error {
|
|||
conf.TwilioAuthToken = twilioAuthToken
|
||||
conf.TwilioPhoneNumber = twilioPhoneNumber
|
||||
conf.TwilioVerifyService = twilioVerifyService
|
||||
conf.TwilioCallFormat = twilioCallFormat
|
||||
conf.MessageSizeLimit = int(messageSizeLimit)
|
||||
conf.MessageDelayMax = messageDelayLimit
|
||||
conf.TotalTopicLimit = totalTopicLimit
|
||||
|
|
|
|||
|
|
@ -1235,6 +1235,32 @@ are the easiest), and then configure the following options:
|
|||
After you have configured phone calls, create a [tier](#tiers) with a call limit (e.g. `ntfy tier create --call-limit=10 ...`),
|
||||
and then assign it to a user. Users may then use the `X-Call` header to receive a phone call when publishing a message.
|
||||
|
||||
To customize your message send to Twilio's Call API, set the `twilio-call-format` option with [TwiML](https://www.twilio.com/docs/voice/twiml).
|
||||
This is the default TwiML:
|
||||
|
||||
``` xml
|
||||
<Response>
|
||||
<Pause length="1"/>
|
||||
<Say loop="3">
|
||||
You have a message from notify on topic %s. Message:
|
||||
<break time="1s"/>
|
||||
%s
|
||||
<break time="1s"/>
|
||||
End of message.
|
||||
<break time="1s"/>
|
||||
This message was sent by user %s. It will be repeated three times.
|
||||
To unsubscribe from calls like this, remove your phone number in the notify web app.
|
||||
<break time="3s"/>
|
||||
</Say>
|
||||
<Say>Goodbye.</Say>
|
||||
</Response>`
|
||||
```
|
||||
|
||||
The TwiML is internaly used as a format string:
|
||||
1. The first `%s` will be replaced with the topic.
|
||||
1. The second `%s` will be replaced with the message.
|
||||
1. The third `%s` will be replaced with the message`s sender name.
|
||||
|
||||
## Message limits
|
||||
There are a few message limits that you can configure:
|
||||
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ type Config struct {
|
|||
TwilioCallsBaseURL string
|
||||
TwilioVerifyBaseURL string
|
||||
TwilioVerifyService string
|
||||
TwilioCallFormat string
|
||||
MetricsEnable bool
|
||||
MetricsListenHTTP string
|
||||
ProfileListenHTTP string
|
||||
|
|
@ -226,6 +227,7 @@ func NewConfig() *Config {
|
|||
TwilioPhoneNumber: "",
|
||||
TwilioVerifyBaseURL: "https://verify.twilio.com", // Override for tests
|
||||
TwilioVerifyService: "",
|
||||
TwilioCallFormat: "",
|
||||
MessageSizeLimit: DefaultMessageSizeLimit,
|
||||
MessageDelayMin: DefaultMessageDelayMin,
|
||||
MessageDelayMax: DefaultMessageDelayMax,
|
||||
|
|
|
|||
|
|
@ -216,11 +216,13 @@
|
|||
# - twilio-auth-token is the Twilio auth token, e.g. affebeef258625862586258625862586
|
||||
# - twilio-phone-number is the outgoing phone number you purchased, e.g. +18775132586
|
||||
# - twilio-verify-service is the Twilio Verify service SID, e.g. VA12345beefbeef67890beefbeef122586
|
||||
# - twilio-call-format (optional) is the custom TwiML send to the Call API. See: https://www.twilio.com/docs/voice/twiml
|
||||
#
|
||||
# twilio-account:
|
||||
# twilio-auth-token:
|
||||
# twilio-phone-number:
|
||||
# twilio-verify-service:
|
||||
# twilio-call-format:
|
||||
|
||||
# Interval in which keepalive messages are sent to the client. This is to prevent
|
||||
# intermediaries closing the connection for inactivity.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
twilioCallFormat = `
|
||||
defaultTwilioCallFormat = `
|
||||
<Response>
|
||||
<Pause length="1"/>
|
||||
<Say loop="3">
|
||||
|
|
@ -65,6 +65,10 @@ func (s *Server) callPhone(v *visitor, r *http.Request, m *message, to string) {
|
|||
if u != nil {
|
||||
sender = u.Name
|
||||
}
|
||||
twilioCallFormat := defaultTwilioCallFormat
|
||||
if len(s.config.TwilioCallFormat) > 0 {
|
||||
twilioCallFormat = s.config.TwilioCallFormat
|
||||
}
|
||||
body := fmt.Sprintf(twilioCallFormat, xmlEscapeText(m.Topic), xmlEscapeText(m.Message), xmlEscapeText(sender))
|
||||
data := url.Values{}
|
||||
data.Set("From", s.config.TwilioPhoneNumber)
|
||||
|
|
|
|||
|
|
@ -202,6 +202,67 @@ func TestServer_Twilio_Call_Success_With_Yes(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestServer_Twilio_Call_Success_with_custom_twiml(t *testing.T) {
|
||||
var called atomic.Bool
|
||||
twilioServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if called.Load() {
|
||||
t.Fatal("Should be only called once")
|
||||
}
|
||||
body, err := io.ReadAll(r.Body)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "/2010-04-01/Accounts/AC1234567890/Calls.json", r.URL.Path)
|
||||
require.Equal(t, "Basic QUMxMjM0NTY3ODkwOkFBRUFBMTIzNDU2Nzg5MA==", r.Header.Get("Authorization"))
|
||||
require.Equal(t, "From=%2B1234567890&To=%2B11122233344&Twiml=%0A%3CResponse%3E%0A%09%3CPause+length%3D%221%22%2F%3E%0A%09%3CSay+language%3D%22de-DE%22+loop%3D%223%22%3E%0A%09%09Du+hast+eine+Nachricht+von+notify+im+Thema+mytopic.+Nachricht%3A%0A%09%09%3Cbreak+time%3D%221s%22%2F%3E%0A%09%09hi+there%0A%09%09%3Cbreak+time%3D%221s%22%2F%3E%0A%09%09Ende+der+Nachricht.%0A%09%09%3Cbreak+time%3D%221s%22%2F%3E%0A%09%09Diese+Nachricht+wurde+von+Benutzer+phil+gesendet.+Sie+wird+drei+Mal+wiederholt.%0A%09%09Um+dich+von+Anrufen+wie+diesen+abzumelden%2C+entferne+deine+Telefonnummer+in+der+notify+web+app.%0A%09%09%3Cbreak+time%3D%223s%22%2F%3E%0A%09%3C%2FSay%3E%0A%09%3CSay+language%3D%22de-DE%22%3EAuf+Wiederh%C3%B6ren.%3C%2FSay%3E%0A%3C%2FResponse%3E", string(body))
|
||||
called.Store(true)
|
||||
}))
|
||||
defer twilioServer.Close()
|
||||
|
||||
c := newTestConfigWithAuthFile(t)
|
||||
c.TwilioCallsBaseURL = twilioServer.URL
|
||||
c.TwilioAccount = "AC1234567890"
|
||||
c.TwilioAuthToken = "AAEAA1234567890"
|
||||
c.TwilioPhoneNumber = "+1234567890"
|
||||
c.TwilioCallFormat = `
|
||||
<Response>
|
||||
<Pause length="1"/>
|
||||
<Say language="de-DE" loop="3">
|
||||
Du hast eine Nachricht von notify im Thema %s. Nachricht:
|
||||
<break time="1s"/>
|
||||
%s
|
||||
<break time="1s"/>
|
||||
Ende der Nachricht.
|
||||
<break time="1s"/>
|
||||
Diese Nachricht wurde von Benutzer %s gesendet. Sie wird drei Mal wiederholt.
|
||||
Um dich von Anrufen wie diesen abzumelden, entferne deine Telefonnummer in der notify web app.
|
||||
<break time="3s"/>
|
||||
</Say>
|
||||
<Say language="de-DE">Auf Wiederhören.</Say>
|
||||
</Response>`
|
||||
s := newTestServer(t, c)
|
||||
|
||||
// Add tier and user
|
||||
require.Nil(t, s.userManager.AddTier(&user.Tier{
|
||||
Code: "pro",
|
||||
MessageLimit: 10,
|
||||
CallLimit: 1,
|
||||
}))
|
||||
require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
|
||||
require.Nil(t, s.userManager.ChangeTier("phil", "pro"))
|
||||
u, err := s.userManager.User("phil")
|
||||
require.Nil(t, err)
|
||||
require.Nil(t, s.userManager.AddPhoneNumber(u.ID, "+11122233344"))
|
||||
|
||||
// Do the thing
|
||||
response := request(t, s, "POST", "/mytopic", "hi there", map[string]string{
|
||||
"authorization": util.BasicAuth("phil", "phil"),
|
||||
"x-call": "+11122233344",
|
||||
})
|
||||
require.Equal(t, "hi there", toMessage(t, response.Body.String()).Message)
|
||||
waitFor(t, func() bool {
|
||||
return called.Load()
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_Twilio_Call_UnverifiedNumber(t *testing.T) {
|
||||
c := newTestConfigWithAuthFile(t)
|
||||
c.TwilioCallsBaseURL = "http://dummy.invalid"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue