mirror of
https://github.com/binwiederhier/ntfy.git
synced 2026-03-18 21:30:44 +01:00
Merge branch 'feature/twilio-call-format-file'
This commit is contained in:
10
cmd/serve.go
10
cmd/serve.go
@@ -14,6 +14,7 @@ import (
|
|||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
@@ -77,6 +78,7 @@ var flagsServe = append(
|
|||||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "twilio-auth-token", Aliases: []string{"twilio_auth_token"}, EnvVars: []string{"NTFY_TWILIO_AUTH_TOKEN"}, Usage: "Twilio auth token"}),
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "twilio-auth-token", Aliases: []string{"twilio_auth_token"}, EnvVars: []string{"NTFY_TWILIO_AUTH_TOKEN"}, Usage: "Twilio auth token"}),
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "twilio-phone-number", Aliases: []string{"twilio_phone_number"}, EnvVars: []string{"NTFY_TWILIO_PHONE_NUMBER"}, Usage: "Twilio number to use for outgoing calls"}),
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "twilio-phone-number", Aliases: []string{"twilio_phone_number"}, EnvVars: []string{"NTFY_TWILIO_PHONE_NUMBER"}, Usage: "Twilio number to use for outgoing calls"}),
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "twilio-verify-service", Aliases: []string{"twilio_verify_service"}, EnvVars: []string{"NTFY_TWILIO_VERIFY_SERVICE"}, Usage: "Twilio Verify service ID, used for phone number verification"}),
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "twilio-verify-service", Aliases: []string{"twilio_verify_service"}, EnvVars: []string{"NTFY_TWILIO_VERIFY_SERVICE"}, Usage: "Twilio Verify service ID, used for phone number verification"}),
|
||||||
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "twilio-call-format", Aliases: []string{"twilio_call_format"}, EnvVars: []string{"NTFY_TWILIO_CALL_FORMAT"}, Usage: "Twilio/TwiML format string for phone calls"}),
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "message-size-limit", Aliases: []string{"message_size_limit"}, EnvVars: []string{"NTFY_MESSAGE_SIZE_LIMIT"}, Value: util.FormatSize(server.DefaultMessageSizeLimit), Usage: "size limit for the message (see docs for limitations)"}),
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "message-size-limit", Aliases: []string{"message_size_limit"}, EnvVars: []string{"NTFY_MESSAGE_SIZE_LIMIT"}, Value: util.FormatSize(server.DefaultMessageSizeLimit), Usage: "size limit for the message (see docs for limitations)"}),
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "message-delay-limit", Aliases: []string{"message_delay_limit"}, EnvVars: []string{"NTFY_MESSAGE_DELAY_LIMIT"}, Value: util.FormatDuration(server.DefaultMessageDelayMax), Usage: "max duration a message can be scheduled into the future"}),
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "message-delay-limit", Aliases: []string{"message_delay_limit"}, EnvVars: []string{"NTFY_MESSAGE_DELAY_LIMIT"}, Value: util.FormatDuration(server.DefaultMessageDelayMax), Usage: "max duration a message can be scheduled into the future"}),
|
||||||
altsrc.NewIntFlag(&cli.IntFlag{Name: "global-topic-limit", Aliases: []string{"global_topic_limit", "T"}, EnvVars: []string{"NTFY_GLOBAL_TOPIC_LIMIT"}, Value: server.DefaultTotalTopicLimit, Usage: "total number of topics allowed"}),
|
altsrc.NewIntFlag(&cli.IntFlag{Name: "global-topic-limit", Aliases: []string{"global_topic_limit", "T"}, EnvVars: []string{"NTFY_GLOBAL_TOPIC_LIMIT"}, Value: server.DefaultTotalTopicLimit, Usage: "total number of topics allowed"}),
|
||||||
@@ -187,6 +189,7 @@ func execServe(c *cli.Context) error {
|
|||||||
twilioAuthToken := c.String("twilio-auth-token")
|
twilioAuthToken := c.String("twilio-auth-token")
|
||||||
twilioPhoneNumber := c.String("twilio-phone-number")
|
twilioPhoneNumber := c.String("twilio-phone-number")
|
||||||
twilioVerifyService := c.String("twilio-verify-service")
|
twilioVerifyService := c.String("twilio-verify-service")
|
||||||
|
twilioCallFormat := c.String("twilio-call-format")
|
||||||
messageSizeLimitStr := c.String("message-size-limit")
|
messageSizeLimitStr := c.String("message-size-limit")
|
||||||
messageDelayLimitStr := c.String("message-delay-limit")
|
messageDelayLimitStr := c.String("message-delay-limit")
|
||||||
totalTopicLimit := c.Int("global-topic-limit")
|
totalTopicLimit := c.Int("global-topic-limit")
|
||||||
@@ -456,6 +459,13 @@ func execServe(c *cli.Context) error {
|
|||||||
conf.TwilioAuthToken = twilioAuthToken
|
conf.TwilioAuthToken = twilioAuthToken
|
||||||
conf.TwilioPhoneNumber = twilioPhoneNumber
|
conf.TwilioPhoneNumber = twilioPhoneNumber
|
||||||
conf.TwilioVerifyService = twilioVerifyService
|
conf.TwilioVerifyService = twilioVerifyService
|
||||||
|
if twilioCallFormat != "" {
|
||||||
|
tmpl, err := template.New("twiml").Parse(twilioCallFormat)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse twilio-call-format template: %w", err)
|
||||||
|
}
|
||||||
|
conf.TwilioCallFormat = tmpl
|
||||||
|
}
|
||||||
conf.MessageSizeLimit = int(messageSizeLimit)
|
conf.MessageSizeLimit = int(messageSizeLimit)
|
||||||
conf.MessageDelayMax = messageDelayLimit
|
conf.MessageDelayMax = messageDelayLimit
|
||||||
conf.TotalTopicLimit = totalTopicLimit
|
conf.TotalTopicLimit = totalTopicLimit
|
||||||
|
|||||||
@@ -1261,10 +1261,85 @@ are the easiest), and then configure the following options:
|
|||||||
* `twilio-auth-token` is the Twilio auth token, e.g. affebeef258625862586258625862586
|
* `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-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-verify-service` is the Twilio Verify service SID, e.g. VA12345beefbeef67890beefbeef122586
|
||||||
|
* `twilio-call-format` is the custom Twilio markup ([TwiML](https://www.twilio.com/docs/voice/twiml)) to use for phone calls (optional)
|
||||||
|
|
||||||
After you have configured phone calls, create a [tier](#tiers) with a call limit (e.g. `ntfy tier create --call-limit=10 ...`),
|
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.
|
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 the message that is spoken out loud, set the `twilio-call-format` option with [TwiML](https://www.twilio.com/docs/voice/twiml). The format is
|
||||||
|
rendered as a [Go template](https://pkg.go.dev/text/template), so you can use the following fields from the message:
|
||||||
|
|
||||||
|
* `{{.Topic}}` is the topic name
|
||||||
|
* `{{.Message}}` is the message body
|
||||||
|
* `{{.Title}}` is the message title
|
||||||
|
* `{{.Tags}}` is a list of tags
|
||||||
|
* `{{.Priority}}` is the message priority
|
||||||
|
* `{{.Sender}}` is the IP address or username of the sender
|
||||||
|
|
||||||
|
Here's an example:
|
||||||
|
|
||||||
|
=== "Custom TwiML (English)"
|
||||||
|
``` yaml
|
||||||
|
twilio-account: "AC12345beefbeef67890beefbeef122586"
|
||||||
|
twilio-auth-token: "affebeef258625862586258625862586"
|
||||||
|
twilio-phone-number: "+18775132586"
|
||||||
|
twilio-verify-service: "VA12345beefbeef67890beefbeef122586"
|
||||||
|
twilio-call-format: |
|
||||||
|
<Response>
|
||||||
|
<Pause length="1"/>
|
||||||
|
<Say loop="3">
|
||||||
|
Yo yo yo, you should totally check out this message for {{.Topic}}.
|
||||||
|
{{ if eq .Priority 5 }}
|
||||||
|
It's really really important, dude. So listen up!
|
||||||
|
{{ end }}
|
||||||
|
<break time="1s"/>
|
||||||
|
{{ if neq .Title "" }}
|
||||||
|
Bro, it's titled: {{.Title}}.
|
||||||
|
{{ end }}
|
||||||
|
<break time="1s"/>
|
||||||
|
{{.Message}}
|
||||||
|
<break time="1s"/>
|
||||||
|
That is all.
|
||||||
|
<break time="1s"/>
|
||||||
|
You know who this message is from? It is from {{.Sender}}.
|
||||||
|
<break time="3s"/>
|
||||||
|
</Say>
|
||||||
|
<Say>See ya!</Say>
|
||||||
|
</Response>
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Custom TwiML (German)"
|
||||||
|
``` yaml
|
||||||
|
twilio-account: "AC12345beefbeef67890beefbeef122586"
|
||||||
|
twilio-auth-token: "affebeef258625862586258625862586"
|
||||||
|
twilio-phone-number: "+18775132586"
|
||||||
|
twilio-verify-service: "VA12345beefbeef67890beefbeef122586"
|
||||||
|
twilio-call-format: |
|
||||||
|
<Response>
|
||||||
|
<Pause length="1"/>
|
||||||
|
<Say loop="3" voice="alice" language="de-DE">
|
||||||
|
Du hast eine Nachricht zum Thema {{.Topic}}.
|
||||||
|
{{ if eq .Priority 5 }}
|
||||||
|
Achtung. Die Nachricht ist sehr wichtig.
|
||||||
|
{{ end }}
|
||||||
|
<break time="1s"/>
|
||||||
|
{{ if neq .Title "" }}
|
||||||
|
Titel der Nachricht: {{.Title}}.
|
||||||
|
{{ end }}
|
||||||
|
<break time="1s"/>
|
||||||
|
Nachricht:
|
||||||
|
<break time="1s"/>
|
||||||
|
{{.Message}}
|
||||||
|
<break time="1s"/>
|
||||||
|
Ende der Nachricht.
|
||||||
|
<break time="1s"/>
|
||||||
|
Diese Nachricht wurde vom Benutzer {{.Sender}} gesendet. Sie wird drei Mal wiederholt.
|
||||||
|
<break time="3s"/>
|
||||||
|
</Say>
|
||||||
|
<Say voice="alice" language="de-DE">Alla mol!</Say>
|
||||||
|
</Response>
|
||||||
|
```
|
||||||
|
|
||||||
## Message limits
|
## Message limits
|
||||||
There are a few message limits that you can configure:
|
There are a few message limits that you can configure:
|
||||||
|
|
||||||
|
|||||||
@@ -1603,10 +1603,9 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
|
|||||||
|
|
||||||
**Features:**
|
**Features:**
|
||||||
|
|
||||||
* Support for [updating and deleting notifications](publish.md#updating-deleting-notifications)
|
* Support for [updating and deleting notifications](publish.md#updating-deleting-notifications) ([#303](https://github.com/binwiederhier/ntfy/issues/303), [#1536](https://github.com/binwiederhier/ntfy/pull/1536),
|
||||||
([#303](https://github.com/binwiederhier/ntfy/issues/303), [#1536](https://github.com/binwiederhier/ntfy/pull/1536),
|
[ntfy-android#151](https://github.com/binwiederhier/ntfy-android/pull/151), thanks to [@wunter8](https://github.com/wunter8) for the initial implementation)
|
||||||
[ntfy-android#151](https://github.com/binwiederhier/ntfy-android/pull/151), thanks to [@wunter8](https://github.com/wunter8)
|
* Support for a [custom Twilio call format](config.md#phone-calls) ([#1289](https://github.com/binwiederhier/ntfy/pull/1289), thanks to [@mmichaa](https://github.com/mmichaa) for the initial implementation)
|
||||||
for the initial implementation)
|
|
||||||
|
|
||||||
### ntfy Android app v1.22.x (UNRELEASED)
|
### ntfy Android app v1.22.x (UNRELEASED)
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package server
|
|||||||
import (
|
import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"heckel.io/ntfy/v2/user"
|
"heckel.io/ntfy/v2/user"
|
||||||
@@ -128,6 +129,7 @@ type Config struct {
|
|||||||
TwilioCallsBaseURL string
|
TwilioCallsBaseURL string
|
||||||
TwilioVerifyBaseURL string
|
TwilioVerifyBaseURL string
|
||||||
TwilioVerifyService string
|
TwilioVerifyService string
|
||||||
|
TwilioCallFormat *template.Template
|
||||||
MetricsEnable bool
|
MetricsEnable bool
|
||||||
MetricsListenHTTP string
|
MetricsListenHTTP string
|
||||||
ProfileListenHTTP string
|
ProfileListenHTTP string
|
||||||
@@ -226,6 +228,7 @@ func NewConfig() *Config {
|
|||||||
TwilioPhoneNumber: "",
|
TwilioPhoneNumber: "",
|
||||||
TwilioVerifyBaseURL: "https://verify.twilio.com", // Override for tests
|
TwilioVerifyBaseURL: "https://verify.twilio.com", // Override for tests
|
||||||
TwilioVerifyService: "",
|
TwilioVerifyService: "",
|
||||||
|
TwilioCallFormat: nil,
|
||||||
MessageSizeLimit: DefaultMessageSizeLimit,
|
MessageSizeLimit: DefaultMessageSizeLimit,
|
||||||
MessageDelayMin: DefaultMessageDelayMin,
|
MessageDelayMin: DefaultMessageDelayMin,
|
||||||
MessageDelayMax: DefaultMessageDelayMax,
|
MessageDelayMax: DefaultMessageDelayMax,
|
||||||
|
|||||||
@@ -216,11 +216,13 @@
|
|||||||
# - twilio-auth-token is the Twilio auth token, e.g. affebeef258625862586258625862586
|
# - 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-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-verify-service is the Twilio Verify service SID, e.g. VA12345beefbeef67890beefbeef122586
|
||||||
|
# - twilio-call-format is the custom TwiML send to the Call API (optional, see https://www.twilio.com/docs/voice/twiml)
|
||||||
#
|
#
|
||||||
# twilio-account:
|
# twilio-account:
|
||||||
# twilio-auth-token:
|
# twilio-auth-token:
|
||||||
# twilio-phone-number:
|
# twilio-phone-number:
|
||||||
# twilio-verify-service:
|
# twilio-verify-service:
|
||||||
|
# twilio-call-format:
|
||||||
|
|
||||||
# Interval in which keepalive messages are sent to the client. This is to prevent
|
# Interval in which keepalive messages are sent to the client. This is to prevent
|
||||||
# intermediaries closing the connection for inactivity.
|
# intermediaries closing the connection for inactivity.
|
||||||
|
|||||||
@@ -4,33 +4,49 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"heckel.io/ntfy/v2/log"
|
|
||||||
"heckel.io/ntfy/v2/user"
|
|
||||||
"heckel.io/ntfy/v2/util"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"heckel.io/ntfy/v2/log"
|
||||||
|
"heckel.io/ntfy/v2/user"
|
||||||
|
"heckel.io/ntfy/v2/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// defaultTwilioCallFormatTemplate is the default TwiML template used for Twilio calls.
|
||||||
twilioCallFormat = `
|
// It can be overridden in the server configuration's twilio-call-format field.
|
||||||
|
//
|
||||||
|
// The format uses Go template syntax with the following fields:
|
||||||
|
// {{.Topic}}, {{.Title}}, {{.Message}}, {{.Priority}}, {{.Tags}}, {{.Sender}}
|
||||||
|
// String fields are automatically XML-escaped.
|
||||||
|
var defaultTwilioCallFormatTemplate = template.Must(template.New("twiml").Parse(`
|
||||||
<Response>
|
<Response>
|
||||||
<Pause length="1"/>
|
<Pause length="1"/>
|
||||||
<Say loop="3">
|
<Say loop="3">
|
||||||
You have a message from notify on topic %s. Message:
|
You have a message from notify on topic {{.Topic}}. Message:
|
||||||
<break time="1s"/>
|
<break time="1s"/>
|
||||||
%s
|
{{.Message}}
|
||||||
<break time="1s"/>
|
<break time="1s"/>
|
||||||
End of message.
|
End of message.
|
||||||
<break time="1s"/>
|
<break time="1s"/>
|
||||||
This message was sent by user %s. It will be repeated three times.
|
This message was sent by user {{.Sender}}. It will be repeated three times.
|
||||||
To unsubscribe from calls like this, remove your phone number in the notify web app.
|
To unsubscribe from calls like this, remove your phone number in the notify web app.
|
||||||
<break time="3s"/>
|
<break time="3s"/>
|
||||||
</Say>
|
</Say>
|
||||||
<Say>Goodbye.</Say>
|
<Say>Goodbye.</Say>
|
||||||
</Response>`
|
</Response>`))
|
||||||
)
|
|
||||||
|
// twilioCallData holds the data passed to the Twilio call format template
|
||||||
|
type twilioCallData struct {
|
||||||
|
Topic string
|
||||||
|
Title string
|
||||||
|
Message string
|
||||||
|
Priority int
|
||||||
|
Tags []string
|
||||||
|
Sender string
|
||||||
|
}
|
||||||
|
|
||||||
// convertPhoneNumber checks if the given phone number is verified for the given user, and if so, returns the verified
|
// convertPhoneNumber checks if the given phone number is verified for the given user, and if so, returns the verified
|
||||||
// phone number. It also converts a boolean string ("yes", "1", "true") to the first verified phone number.
|
// phone number. It also converts a boolean string ("yes", "1", "true") to the first verified phone number.
|
||||||
@@ -65,7 +81,29 @@ func (s *Server) callPhone(v *visitor, r *http.Request, m *message, to string) {
|
|||||||
if u != nil {
|
if u != nil {
|
||||||
sender = u.Name
|
sender = u.Name
|
||||||
}
|
}
|
||||||
body := fmt.Sprintf(twilioCallFormat, xmlEscapeText(m.Topic), xmlEscapeText(m.Message), xmlEscapeText(sender))
|
tmpl := defaultTwilioCallFormatTemplate
|
||||||
|
if s.config.TwilioCallFormat != nil {
|
||||||
|
tmpl = s.config.TwilioCallFormat
|
||||||
|
}
|
||||||
|
tags := make([]string, len(m.Tags))
|
||||||
|
for i, tag := range m.Tags {
|
||||||
|
tags[i] = xmlEscapeText(tag)
|
||||||
|
}
|
||||||
|
templateData := &twilioCallData{
|
||||||
|
Topic: xmlEscapeText(m.Topic),
|
||||||
|
Title: xmlEscapeText(m.Title),
|
||||||
|
Message: xmlEscapeText(m.Message),
|
||||||
|
Priority: m.Priority,
|
||||||
|
Tags: tags,
|
||||||
|
Sender: xmlEscapeText(sender),
|
||||||
|
}
|
||||||
|
var bodyBuf bytes.Buffer
|
||||||
|
if err := tmpl.Execute(&bodyBuf, templateData); err != nil {
|
||||||
|
logvrm(v, r, m).Tag(tagTwilio).Err(err).Warn("Error executing Twilio call format template")
|
||||||
|
minc(metricCallsMadeFailure)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
body := bodyBuf.String()
|
||||||
data := url.Values{}
|
data := url.Values{}
|
||||||
data.Set("From", s.config.TwilioPhoneNumber)
|
data.Set("From", s.config.TwilioPhoneNumber)
|
||||||
data.Set("To", to)
|
data.Set("To", to)
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"heckel.io/ntfy/v2/user"
|
|
||||||
"heckel.io/ntfy/v2/util"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"heckel.io/ntfy/v2/user"
|
||||||
|
"heckel.io/ntfy/v2/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestServer_Twilio_Call_Add_Verify_Call_Delete_Success(t *testing.T) {
|
func TestServer_Twilio_Call_Add_Verify_Call_Delete_Success(t *testing.T) {
|
||||||
@@ -202,6 +204,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 = template.Must(template.New("twiml").Parse(`
|
||||||
|
<Response>
|
||||||
|
<Pause length="1"/>
|
||||||
|
<Say language="de-DE" loop="3">
|
||||||
|
Du hast eine Nachricht von notify im Thema {{.Topic}}. Nachricht:
|
||||||
|
<break time="1s"/>
|
||||||
|
{{.Message}}
|
||||||
|
<break time="1s"/>
|
||||||
|
Ende der Nachricht.
|
||||||
|
<break time="1s"/>
|
||||||
|
Diese Nachricht wurde von Benutzer {{.Sender}} 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, false))
|
||||||
|
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) {
|
func TestServer_Twilio_Call_UnverifiedNumber(t *testing.T) {
|
||||||
c := newTestConfigWithAuthFile(t)
|
c := newTestConfigWithAuthFile(t)
|
||||||
c.TwilioCallsBaseURL = "http://dummy.invalid"
|
c.TwilioCallsBaseURL = "http://dummy.invalid"
|
||||||
|
|||||||
Reference in New Issue
Block a user