mirror of
https://github.com/jwetzell/showbridge-go.git
synced 2026-04-26 21:05:30 +00:00
move sip server under own namespace
This commit is contained in:
152
internal/module/sip-dtmf-server.go
Normal file
152
internal/module/sip-dtmf-server.go
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
package module
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/emiago/diago"
|
||||||
|
"github.com/emiago/sipgo"
|
||||||
|
"github.com/emiago/sipgo/sip"
|
||||||
|
"github.com/jwetzell/showbridge-go/internal/config"
|
||||||
|
"github.com/jwetzell/showbridge-go/internal/route"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SIPDTMFServer struct {
|
||||||
|
config config.ModuleConfig
|
||||||
|
ctx context.Context
|
||||||
|
router route.RouteIO
|
||||||
|
IP string
|
||||||
|
Port int
|
||||||
|
Transport string
|
||||||
|
Separator string
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RegisterModule(ModuleRegistration{
|
||||||
|
Type: "sip.dtmf.server",
|
||||||
|
New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) {
|
||||||
|
params := config.Params
|
||||||
|
portNum := 5060
|
||||||
|
|
||||||
|
port, ok := params["port"]
|
||||||
|
if ok {
|
||||||
|
specificPortNum, ok := port.(float64)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("sip.dtmf.server port must be a number")
|
||||||
|
}
|
||||||
|
portNum = int(specificPortNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipString := "0.0.0.0"
|
||||||
|
|
||||||
|
ip, ok := params["ip"]
|
||||||
|
if ok {
|
||||||
|
|
||||||
|
specificIpString, ok := ip.(string)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("sip.dtmf.server ip must be a string")
|
||||||
|
}
|
||||||
|
ipString = specificIpString
|
||||||
|
}
|
||||||
|
|
||||||
|
transportString := "udp"
|
||||||
|
|
||||||
|
transport, ok := params["transport"]
|
||||||
|
if ok {
|
||||||
|
|
||||||
|
specificTransportString, ok := transport.(string)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("sip.dtmf.server transport must be a string")
|
||||||
|
}
|
||||||
|
transportString = specificTransportString
|
||||||
|
}
|
||||||
|
|
||||||
|
separator, ok := params["separator"]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("sip.dtmf.server requires a separator parameter")
|
||||||
|
}
|
||||||
|
separatorString, ok := separator.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("sip.dtmf.server separator must be a string")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(separatorString) != 1 {
|
||||||
|
return nil, fmt.Errorf("sip.dtmf.server separator must be a single character")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.ContainsRune("0123456789*#ABCD", rune(separatorString[0])) {
|
||||||
|
return nil, fmt.Errorf("sip.dtmf.server separator must be a valid DTMF character")
|
||||||
|
}
|
||||||
|
return &SIPDTMFServer{config: config, ctx: ctx, router: router, IP: ipString, Port: int(portNum), Transport: transportString, Separator: separatorString}, nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sds *SIPDTMFServer) Id() string {
|
||||||
|
return sds.config.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sds *SIPDTMFServer) Type() string {
|
||||||
|
return sds.config.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sds *SIPDTMFServer) Run() error {
|
||||||
|
diagoLogger := slog.New(slog.NewJSONHandler(io.Discard, nil))
|
||||||
|
|
||||||
|
ua, _ := sipgo.NewUA(
|
||||||
|
sipgo.WithUserAgentTransportLayerOptions(sip.WithTransportLayerLogger(diagoLogger)),
|
||||||
|
)
|
||||||
|
defer ua.Close()
|
||||||
|
|
||||||
|
sip.SetDefaultLogger(diagoLogger)
|
||||||
|
dg := diago.NewDiago(ua, diago.WithLogger(diagoLogger), diago.WithTransport(
|
||||||
|
diago.Transport{
|
||||||
|
Transport: sds.Transport,
|
||||||
|
BindHost: sds.IP,
|
||||||
|
BindPort: sds.Port,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
|
||||||
|
err := dg.Serve(sds.ctx, func(inDialog *diago.DialogServerSession) {
|
||||||
|
sds.HandleCall(inDialog)
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
<-sds.ctx.Done()
|
||||||
|
slog.Debug("router context done in module", "id", sds.Id())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sds *SIPDTMFServer) HandleCall(inDialog *diago.DialogServerSession) error {
|
||||||
|
inDialog.Trying()
|
||||||
|
inDialog.Ringing()
|
||||||
|
inDialog.Answer()
|
||||||
|
|
||||||
|
reader := inDialog.AudioReaderDTMF()
|
||||||
|
userString := ""
|
||||||
|
return reader.Listen(func(dtmf rune) error {
|
||||||
|
if dtmf == rune(sds.Separator[0]) {
|
||||||
|
if sds.router != nil {
|
||||||
|
sds.router.HandleInput(sds.Id(), userString)
|
||||||
|
}
|
||||||
|
userString = ""
|
||||||
|
} else {
|
||||||
|
userString += string(dtmf)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}, 5*time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sds *SIPDTMFServer) Output(payload any) error {
|
||||||
|
return fmt.Errorf("sip.dtmf.server output is not implemented")
|
||||||
|
}
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
package module
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log/slog"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/emiago/diago"
|
|
||||||
"github.com/emiago/sipgo"
|
|
||||||
"github.com/emiago/sipgo/sip"
|
|
||||||
"github.com/jwetzell/showbridge-go/internal/config"
|
|
||||||
"github.com/jwetzell/showbridge-go/internal/route"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SIPServer struct {
|
|
||||||
config config.ModuleConfig
|
|
||||||
ctx context.Context
|
|
||||||
router route.RouteIO
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
RegisterModule(ModuleRegistration{
|
|
||||||
Type: "net.sip.server",
|
|
||||||
New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) {
|
|
||||||
|
|
||||||
return &SIPServer{config: config, ctx: ctx, router: router}, nil
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *SIPServer) Id() string {
|
|
||||||
return ss.config.Id
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *SIPServer) Type() string {
|
|
||||||
return ss.config.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *SIPServer) Run() error {
|
|
||||||
diagoLogger := slog.New(slog.NewJSONHandler(io.Discard, nil))
|
|
||||||
|
|
||||||
ua, _ := sipgo.NewUA(
|
|
||||||
sipgo.WithUserAgentTransportLayerOptions(sip.WithTransportLayerLogger(diagoLogger)),
|
|
||||||
)
|
|
||||||
|
|
||||||
sip.SetDefaultLogger(diagoLogger)
|
|
||||||
dg := diago.NewDiago(ua, diago.WithLogger(diagoLogger), diago.WithTransport(
|
|
||||||
diago.Transport{
|
|
||||||
Transport: "udp",
|
|
||||||
BindHost: "0.0.0.0",
|
|
||||||
BindPort: 5060,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
|
|
||||||
err := dg.Serve(ss.ctx, func(inDialog *diago.DialogServerSession) {
|
|
||||||
ss.HandleCall(inDialog)
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
<-ss.ctx.Done()
|
|
||||||
slog.Debug("router context done in module", "id", ss.Id())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *SIPServer) HandleCall(inDialog *diago.DialogServerSession) error {
|
|
||||||
inDialog.Trying()
|
|
||||||
inDialog.Ringing()
|
|
||||||
inDialog.Answer()
|
|
||||||
|
|
||||||
reader := inDialog.AudioReaderDTMF()
|
|
||||||
userString := ""
|
|
||||||
return reader.Listen(func(dtmf rune) error {
|
|
||||||
if dtmf == '#' {
|
|
||||||
if ss.router != nil {
|
|
||||||
ss.router.HandleInput(ss.Id(), userString)
|
|
||||||
}
|
|
||||||
userString = ""
|
|
||||||
} else {
|
|
||||||
userString += string(dtmf)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}, 5*time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *SIPServer) Output(payload any) error {
|
|
||||||
return fmt.Errorf("net.sip.server output is not implemented")
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user