Update web app with SMS and calls stuff
This commit is contained in:
parent
7677c50b0e
commit
eb0805a470
14 changed files with 274 additions and 39 deletions
|
@ -529,6 +529,8 @@ func (s *Server) handleWebConfig(w http.ResponseWriter, _ *http.Request, _ *visi
|
|||
EnableLogin: s.config.EnableLogin,
|
||||
EnableSignup: s.config.EnableSignup,
|
||||
EnablePayments: s.config.StripeSecretKey != "",
|
||||
EnableSMS: s.config.TwilioAccount != "",
|
||||
EnableCalls: s.config.TwilioAccount != "",
|
||||
EnableReservations: s.config.EnableReservations,
|
||||
BillingContact: s.config.BillingContact,
|
||||
DisallowedTopics: s.config.DisallowedTopics,
|
||||
|
|
|
@ -68,6 +68,8 @@ func (s *Server) handleBillingTiersGet(w http.ResponseWriter, _ *http.Request, _
|
|||
Messages: freeTier.MessageLimit,
|
||||
MessagesExpiryDuration: int64(freeTier.MessageExpiryDuration.Seconds()),
|
||||
Emails: freeTier.EmailLimit,
|
||||
SMS: freeTier.SMSLimit,
|
||||
Calls: freeTier.CallLimit,
|
||||
Reservations: freeTier.ReservationsLimit,
|
||||
AttachmentTotalSize: freeTier.AttachmentTotalSizeLimit,
|
||||
AttachmentFileSize: freeTier.AttachmentFileSizeLimit,
|
||||
|
@ -96,6 +98,8 @@ func (s *Server) handleBillingTiersGet(w http.ResponseWriter, _ *http.Request, _
|
|||
Messages: tier.MessageLimit,
|
||||
MessagesExpiryDuration: int64(tier.MessageExpiryDuration.Seconds()),
|
||||
Emails: tier.EmailLimit,
|
||||
SMS: tier.SMSLimit,
|
||||
Calls: tier.CallLimit,
|
||||
Reservations: tier.ReservationLimit,
|
||||
AttachmentTotalSize: tier.AttachmentTotalSizeLimit,
|
||||
AttachmentFileSize: tier.AttachmentFileSizeLimit,
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"heckel.io/ntfy/log"
|
||||
"heckel.io/ntfy/user"
|
||||
"heckel.io/ntfy/util"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -32,7 +33,7 @@ const (
|
|||
)
|
||||
|
||||
func (s *Server) sendSMS(v *visitor, r *http.Request, m *message, to string) {
|
||||
body := fmt.Sprintf("%s\n\n--\n%s", m.Message, s.messageFooter(m))
|
||||
body := fmt.Sprintf("%s\n\n--\n%s", m.Message, s.messageFooter(v.User(), m))
|
||||
data := url.Values{}
|
||||
data.Set("From", s.config.TwilioFromNumber)
|
||||
data.Set("To", to)
|
||||
|
@ -41,7 +42,7 @@ func (s *Server) sendSMS(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(m)))
|
||||
body := fmt.Sprintf(twilioCallFormat, xmlEscapeText(m.Topic), xmlEscapeText(m.Message), xmlEscapeText(s.messageFooter(v.User(), m)))
|
||||
data := url.Values{}
|
||||
data.Set("From", s.config.TwilioFromNumber)
|
||||
data.Set("To", to)
|
||||
|
@ -97,11 +98,11 @@ func (s *Server) performTwilioRequestInternal(endpoint string, data url.Values)
|
|||
return string(response), nil
|
||||
}
|
||||
|
||||
func (s *Server) messageFooter(m *message) string {
|
||||
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 m.User != "" {
|
||||
sender = fmt.Sprintf("%s (%s)", m.User, m.Sender)
|
||||
if u != nil {
|
||||
sender = fmt.Sprintf("%s (%s)", u.Name, m.Sender)
|
||||
}
|
||||
return fmt.Sprintf(twilioMessageFooterFormat, sender, util.ShortTopicURL(topicURL))
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package server
|
|||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"heckel.io/ntfy/user"
|
||||
"heckel.io/ntfy/util"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
@ -27,6 +29,7 @@ func TestServer_Twilio_SMS(t *testing.T) {
|
|||
c.TwilioAccount = "AC1234567890"
|
||||
c.TwilioAuthToken = "AAEAA1234567890"
|
||||
c.TwilioFromNumber = "+1234567890"
|
||||
c.VisitorSMSDailyLimit = 1
|
||||
s := newTestServer(t, c)
|
||||
|
||||
response := request(t, s, "POST", "/mytopic", "test", map[string]string{
|
||||
|
@ -38,6 +41,56 @@ func TestServer_Twilio_SMS(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestServer_Twilio_SMS_With_User(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/Messages.json", r.URL.Path)
|
||||
require.Equal(t, "Basic QUMxMjM0NTY3ODkwOkFBRUFBMTIzNDU2Nzg5MA==", r.Header.Get("Authorization"))
|
||||
require.Equal(t, "Body=test%0A%0A--%0AThis+message+was+sent+by+phil+%289.9.9.9%29+via+ntfy.sh%2Fmytopic&From=%2B1234567890&To=%2B11122233344", string(body))
|
||||
called.Store(true)
|
||||
}))
|
||||
defer twilioServer.Close()
|
||||
|
||||
c := newTestConfigWithAuthFile(t)
|
||||
c.BaseURL = "https://ntfy.sh"
|
||||
c.TwilioBaseURL = twilioServer.URL
|
||||
c.TwilioAccount = "AC1234567890"
|
||||
c.TwilioAuthToken = "AAEAA1234567890"
|
||||
c.TwilioFromNumber = "+1234567890"
|
||||
s := newTestServer(t, c)
|
||||
|
||||
// Add tier and user
|
||||
require.Nil(t, s.userManager.AddTier(&user.Tier{
|
||||
Code: "pro",
|
||||
MessageLimit: 10,
|
||||
SMSLimit: 1,
|
||||
}))
|
||||
require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
|
||||
require.Nil(t, s.userManager.ChangeTier("phil", "pro"))
|
||||
|
||||
// Do request with user
|
||||
response := request(t, s, "POST", "/mytopic", "test", map[string]string{
|
||||
"Authorization": util.BasicAuth("phil", "phil"),
|
||||
"SMS": "+11122233344",
|
||||
})
|
||||
require.Equal(t, "test", toMessage(t, response.Body.String()).Message)
|
||||
waitFor(t, func() bool {
|
||||
return called.Load()
|
||||
})
|
||||
|
||||
// Second one should fail due to rate limits
|
||||
response = request(t, s, "POST", "/mytopic", "test", map[string]string{
|
||||
"Authorization": util.BasicAuth("phil", "phil"),
|
||||
"SMS": "+11122233344",
|
||||
})
|
||||
require.Equal(t, 42910, toHTTPError(t, response.Body.String()).Code)
|
||||
}
|
||||
|
||||
func TestServer_Twilio_Call(t *testing.T) {
|
||||
var called atomic.Bool
|
||||
twilioServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -55,6 +108,7 @@ func TestServer_Twilio_Call(t *testing.T) {
|
|||
c.TwilioAccount = "AC1234567890"
|
||||
c.TwilioAuthToken = "AAEAA1234567890"
|
||||
c.TwilioFromNumber = "+1234567890"
|
||||
c.VisitorCallDailyLimit = 1
|
||||
s := newTestServer(t, c)
|
||||
|
||||
body := `this message has
|
||||
|
@ -69,6 +123,48 @@ and "quotes and other 'quotes`
|
|||
})
|
||||
}
|
||||
|
||||
func TestServer_Twilio_Call_With_User(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%3EYou+have+a+message+from+notify+on+topic+mytopic.+Message%3A%3C%2FSay%3E%0A%09%3CPause+length%3D%221%22%2F%3E%0A%09%3CSay%3Ehi+there%3C%2FSay%3E%0A%09%3CPause+length%3D%221%22%2F%3E%0A%09%3CSay%3EEnd+message.%3C%2FSay%3E%0A%09%3CPause+length%3D%221%22%2F%3E%0A%09%3CSay%3EThis+message+was+sent+by+phil+%289.9.9.9%29+via+127.0.0.1%3A12345%2Fmytopic%3C%2FSay%3E%0A%09%3CPause+length%3D%221%22%2F%3E%0A%3C%2FResponse%3E", string(body))
|
||||
called.Store(true)
|
||||
}))
|
||||
defer twilioServer.Close()
|
||||
|
||||
c := newTestConfigWithAuthFile(t)
|
||||
c.TwilioBaseURL = twilioServer.URL
|
||||
c.TwilioAccount = "AC1234567890"
|
||||
c.TwilioAuthToken = "AAEAA1234567890"
|
||||
c.TwilioFromNumber = "+1234567890"
|
||||
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"))
|
||||
|
||||
// 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_InvalidNumber(t *testing.T) {
|
||||
c := newTestConfig(t)
|
||||
c.TwilioBaseURL = "https://127.0.0.1"
|
||||
|
|
|
@ -351,6 +351,8 @@ type apiConfigResponse struct {
|
|||
EnableLogin bool `json:"enable_login"`
|
||||
EnableSignup bool `json:"enable_signup"`
|
||||
EnablePayments bool `json:"enable_payments"`
|
||||
EnableSMS bool `json:"enable_sms"`
|
||||
EnableCalls bool `json:"enable_calls"`
|
||||
EnableReservations bool `json:"enable_reservations"`
|
||||
BillingContact string `json:"billing_contact"`
|
||||
DisallowedTopics []string `json:"disallowed_topics"`
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue