├── codecov.yml ├── agi ├── README.md ├── agi_demo_test.go ├── agi_test.go ├── agimodels │ ├── command_test.go │ ├── iface.go │ └── commands.go ├── agi_bin.go └── agi.go ├── pkg └── tools │ └── xmlgen │ ├── template │ ├── field.tmpl │ ├── ami │ │ ├── events.tmpl │ │ ├── actions.tmpl │ │ ├── event.tmpl │ │ └── action.tmpl │ └── agi │ │ ├── commands.tmpl │ │ └── command.tmpl │ ├── parse.go │ ├── _ami_parse_test.go │ ├── _doc_test.go │ ├── gen_model_build.go │ ├── gen_model.go │ ├── build.go │ ├── tmpl.go │ ├── doc_model.go │ └── model.go ├── ami ├── amimodels │ ├── evt.go │ └── iface.go ├── conn.go ├── connect_demo_test.go ├── handler.go ├── subscribe.go ├── msg_test.go ├── request.go ├── conn_internal.go ├── msg.go └── connect.go ├── .editorconfig ├── Makefile ├── .github └── workflows │ └── ci.yml ├── go.mod ├── astdb ├── ps_model.go ├── model.go ├── _enum_gen_test.go └── enums.go ├── .golangci.yml ├── .gitignore ├── README.md ├── LICENSE └── go.sum /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - pkg/tools 3 | - astdb # not ready yet 4 | # generated 5 | - ami/amimodels 6 | - agi/agimodels 7 | -------------------------------------------------------------------------------- /agi/README.md: -------------------------------------------------------------------------------- 1 | # AGI 2 | - [handle_connection](https://github.com/asterisk/asterisk/blob/master/res/res_agi.c#L2012) 3 | - FastAGI defaults port 4573 4 | # See also 5 | 6 | - [CyCoreSystems/agi](https://github.com/CyCoreSystems/agi) 7 | -------------------------------------------------------------------------------- /pkg/tools/xmlgen/template/field.tmpl: -------------------------------------------------------------------------------- 1 | {{/* gotype: github.com/wenerme/astgo/pkg/tools/xmlgen.Field */}} 2 | {{- define "field"}} 3 | {{- with .Doc}} 4 | {{template "comment" . | nindent 2}} 5 | {{- end}} 6 | {{.Name}} {{.Type}} 7 | {{- end}} 8 | -------------------------------------------------------------------------------- /ami/amimodels/evt.go: -------------------------------------------------------------------------------- 1 | package amimodels 2 | 3 | import "github.com/pkg/errors" 4 | 5 | func (evt OriginateResponseEvent) Err() error { 6 | if evt.Response == "Failure" { 7 | return errors.Errorf("Originate failed: exten %v reason %v", evt.Exten, evt.Reason) 8 | } 9 | return nil 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [Makefile] 12 | indent_style = tab 13 | indent_size = 4 14 | 15 | [*.go] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /pkg/tools/xmlgen/template/ami/events.tmpl: -------------------------------------------------------------------------------- 1 | {{/* gotype: github.com/wenerme/astgo/pkg/tools/xmlgen.Model */}} 2 | {{- define "ami/events"}} 3 | // Code generated by xmlgen. DO NOT EDIT. 4 | 5 | package amimodels 6 | 7 | {{range $_,$v := $.Events}} 8 | {{template "ami/event" $v}} 9 | {{end}} 10 | {{- end}} 11 | -------------------------------------------------------------------------------- /pkg/tools/xmlgen/template/ami/actions.tmpl: -------------------------------------------------------------------------------- 1 | {{/* gotype: github.com/wenerme/astgo/pkg/tools/xmlgen.Model */}} 2 | {{- define "ami/actions"}} 3 | // Code generated by xmlgen. DO NOT EDIT. 4 | 5 | package amimodels 6 | 7 | {{range $_,$v := $.Actions}} 8 | {{template "ami/action" $v}} 9 | {{end}} 10 | {{- end}} 11 | -------------------------------------------------------------------------------- /pkg/tools/xmlgen/template/agi/commands.tmpl: -------------------------------------------------------------------------------- 1 | {{/* gotype: github.com/wenerme/astgo/pkg/tools/xmlgen.Model*/}} 2 | 3 | {{- define "agi/commands"}} 4 | // Code generated by xmlgen. DO NOT EDIT. 5 | 6 | package agimodels 7 | 8 | {{range $_,$v := $.AGICommands}} 9 | {{template "agi/command" $v}} 10 | {{end}} 11 | {{- end}} 12 | -------------------------------------------------------------------------------- /agi/agi_demo_test.go: -------------------------------------------------------------------------------- 1 | package agi_test 2 | 3 | import "github.com/wenerme/astgo/agi" 4 | 5 | func ExampleRun() { 6 | agi.Run(func(session *agi.Session) { 7 | client := session.Client() 8 | client.Answer() 9 | client.StreamFile("activated", "#") 10 | client.SetVariable("AGISTATUS", "SUCCESS") 11 | client.Hangup() 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /pkg/tools/xmlgen/template/ami/event.tmpl: -------------------------------------------------------------------------------- 1 | {{/* gotype: github.com/wenerme/astgo/pkg/tools/xmlgen.ManagerEvent*/}} 2 | {{- define "ami/event"}} 3 | {{with .Synopsis}} {{template "comment" (printf "%v %v" $.StructName .)}} {{end}} 4 | type {{.StructName}} struct { 5 | {{- template "ami/fields" .}} 6 | } 7 | func({{.StructName}})EventTypeName() string{ 8 | return "{{.Name}}" 9 | } 10 | {{- end}} 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | love: 3 | 4 | fmt: 5 | gofmt -w `find . -type f -name '*.go' -not -path "./vendor/*"` 6 | goimports -w `find . -type f -name '*.go' -not -path "./vendor/*"` 7 | 8 | lint: 9 | golangci-lint run 10 | 11 | ci: cover 12 | [ -z "$(CODECOV_TOKEN)" ] || bash -c 'bash <(curl -s https://codecov.io/bash)' 13 | 14 | .PHONY: cover 15 | cover: 16 | go test -race -coverprofile=cover.out -coverpkg=./... ./... 17 | go tool cover -html=cover.out -o cover.html 18 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Test and coverage 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | with: 11 | fetch-depth: 2 12 | - uses: actions/setup-go@v2 13 | with: 14 | go-version: '1.16' 15 | - name: Run CI 16 | run: make ci 17 | - name: Upload coverage to codecov.io 18 | uses: codecov/codecov-action@v1 19 | -------------------------------------------------------------------------------- /agi/agi_test.go: -------------------------------------------------------------------------------- 1 | package agi 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestParseResponse(t *testing.T) { 10 | for _, test := range []struct { 11 | L string 12 | R Response 13 | }{ 14 | {L: "200 result=1", R: Response{Status: 200, Result: 1, ResultString: "1"}}, 15 | {L: "200 result=0 endpos=8640", R: Response{Status: 200, Result: 0, ResultString: "0", Value: "endpos=8640"}}, 16 | } { 17 | assert.Equal(t, &test.R, ParseResponse(test.L)) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /pkg/tools/xmlgen/parse.go: -------------------------------------------------------------------------------- 1 | package xmlgen 2 | 3 | type Type struct { 4 | Name string 5 | Doc string 6 | Fields []*Field 7 | } 8 | type Field struct { 9 | Name string 10 | Doc string 11 | Default string 12 | Required bool 13 | Type *TypeInfo 14 | StructTag string 15 | Annotations interface{} 16 | } 17 | type Annotation interface { 18 | Name() string 19 | } 20 | type TypeInfo struct { 21 | Type string 22 | } 23 | 24 | func (ti *TypeInfo) String() string { 25 | return ti.Type 26 | } 27 | -------------------------------------------------------------------------------- /ami/conn.go: -------------------------------------------------------------------------------- 1 | package ami 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "net" 7 | "sync" 8 | 9 | "go.uber.org/zap" 10 | "golang.org/x/sync/errgroup" 11 | ) 12 | 13 | type Conn struct { 14 | g *errgroup.Group 15 | conn net.Conn 16 | reader *bufio.Reader 17 | nextID func() string 18 | pending chan *asyncMsg 19 | recv chan *Message 20 | ctx context.Context 21 | closer context.CancelFunc 22 | closed bool 23 | 24 | version string 25 | logger *zap.Logger 26 | conf *ConnectOptions 27 | subs []*subscribe 28 | subLoc sync.Mutex 29 | } 30 | -------------------------------------------------------------------------------- /agi/agimodels/command_test.go: -------------------------------------------------------------------------------- 1 | package agimodels 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestBuild(t *testing.T) { 10 | for _, test := range []struct { 11 | S string 12 | C Command 13 | }{ 14 | {S: "HANGUP 16", C: HangupCommand{}.SetChannelName("16")}, 15 | {S: "CONTROL STREAM FILE fn #", C: ControlStreamFileCommand{FileName: "fn", EscapeDigits: "#"}}, 16 | {S: "CONTROL STREAM FILE fn #", C: ControlStreamFileCommand{FileName: "fn", EscapeDigits: "#"}.SetPausechr("Abc")}, 17 | } { 18 | s, err := test.C.Command() 19 | assert.NoError(t, err) 20 | assert.Equal(t, test.S, s) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pkg/tools/xmlgen/_ami_parse_test.go: -------------------------------------------------------------------------------- 1 | package xmlgen 2 | 3 | import ( 4 | "context" 5 | "encoding/xml" 6 | "log" 7 | "os" 8 | "testing" 9 | ) 10 | 11 | func TestParse(t *testing.T) { 12 | f, _ := os.Open("out.xml") 13 | defer f.Close() 14 | dec := xml.NewDecoder(f) 15 | var doc Docs 16 | if err := dec.Decode(&doc); err != nil { 17 | log.Fatal(err) 18 | } 19 | 20 | ge := NewAMIGenerator() 21 | ge.Debug = true 22 | d := &AstDoc{} 23 | 24 | BuildAstDoc(&doc, d) 25 | 26 | err := ge.Generate(context.Background(), d) 27 | if err != nil { 28 | panic(err) 29 | } 30 | err = ge.Write(WriteConfig{ 31 | Target: "./../../../ami/models", 32 | //DryRun: true, 33 | }) 34 | if err != nil { 35 | panic(err) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ami/connect_demo_test.go: -------------------------------------------------------------------------------- 1 | package ami_test 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/wenerme/astgo/ami" 7 | ) 8 | 9 | func ExampleConnect() { 10 | boot := make(chan *ami.Message, 1) 11 | 12 | conn, err := ami.Connect( 13 | "192.168.1.1:5038", 14 | ami.WithAuth("admin", "admin"), // AMI auth 15 | // add predefined subscriber 16 | ami.WithSubscribe(ami.SubscribeFullyBootedChanOnce(boot)), 17 | ami.WithSubscribe(func(ctx context.Context, msg *ami.Message) bool { 18 | fmt.Println(msg.Format()) // log everything 19 | return true // keep subscribe 20 | }, ami.SubscribeSend(), // subscribe send message - default recv only 21 | )) 22 | if err != nil { 23 | panic(err) 24 | } 25 | <-boot 26 | // AMI now FullyBooted 27 | _ = conn 28 | } 29 | -------------------------------------------------------------------------------- /ami/handler.go: -------------------------------------------------------------------------------- 1 | package ami 2 | 3 | import "context" 4 | 5 | func SubscribeChan(c chan *Message, names ...string) SubscribeFunc { 6 | m := make(map[string]struct{}, len(names)) 7 | for _, v := range names { 8 | m[v] = struct{}{} 9 | } 10 | filter := func(ctx context.Context, msg *Message) bool { 11 | _, ok := m[msg.Name] 12 | return ok 13 | } 14 | if len(names) == 0 { 15 | filter = func(ctx context.Context, msg *Message) bool { 16 | return true 17 | } 18 | } 19 | return func(ctx context.Context, msg *Message) bool { 20 | if filter(ctx, msg) { 21 | c <- msg 22 | } 23 | 24 | return true 25 | } 26 | } 27 | 28 | func SubscribeFullyBootedChanOnce(c chan *Message) SubscribeFunc { 29 | return func(ctx context.Context, msg *Message) bool { 30 | if msg.Name == "FullyBooted" { 31 | c <- msg 32 | return false 33 | } 34 | return true 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wenerme/astgo 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/Masterminds/goutils v1.1.1 // indirect 7 | github.com/Masterminds/semver v1.5.0 // indirect 8 | github.com/Masterminds/sprig v2.22.0+incompatible 9 | github.com/go-pg/pg v8.0.7+incompatible 10 | github.com/google/uuid v1.2.0 // indirect 11 | github.com/hashicorp/go-multierror v1.1.1 12 | github.com/huandu/xstrings v1.3.2 13 | github.com/iancoleman/strcase v0.1.3 14 | github.com/imdario/mergo v0.3.12 // indirect 15 | github.com/jinzhu/gorm v1.9.16 16 | github.com/mitchellh/copystructure v1.1.2 // indirect 17 | github.com/mitchellh/mapstructure v1.4.1 18 | github.com/onsi/ginkgo v1.15.2 // indirect 19 | github.com/onsi/gomega v1.11.0 // indirect 20 | github.com/pkg/errors v0.9.1 21 | github.com/sirupsen/logrus v1.8.1 22 | github.com/stretchr/testify v1.7.0 23 | go.uber.org/zap v1.16.0 24 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 25 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e 26 | mellium.im/sasl v0.2.1 // indirect 27 | ) 28 | -------------------------------------------------------------------------------- /agi/agimodels/iface.go: -------------------------------------------------------------------------------- 1 | package agimodels 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | type Command interface { 10 | Command() (string, error) 11 | } 12 | type Response interface { 13 | Err() error 14 | Val() (string, error) 15 | } 16 | 17 | type Handler interface { 18 | Command(cmd Command) Response 19 | } 20 | type HandlerFunc func(cmd Command) Response 21 | 22 | func (f HandlerFunc) Command(cmd Command) Response { 23 | return f(cmd) 24 | } 25 | 26 | type Client struct { 27 | Handler 28 | } 29 | 30 | func joinCommand(s []interface{}) string { 31 | sb := strings.Builder{} 32 | for _, param := range s { 33 | switch v := param.(type) { 34 | case string: 35 | sb.WriteString(v) 36 | case int: 37 | sb.WriteString(strconv.Itoa(v)) 38 | case float64: 39 | sb.WriteString(fmt.Sprint(v)) 40 | case *string: 41 | if v == nil { 42 | goto DONE 43 | } 44 | sb.WriteString(*v) 45 | case *int: 46 | if v == nil { 47 | goto DONE 48 | } 49 | sb.WriteString(strconv.Itoa(*v)) 50 | case *float64: 51 | if v == nil { 52 | goto DONE 53 | } 54 | sb.WriteString(fmt.Sprint(*v)) 55 | } 56 | sb.WriteRune(' ') 57 | } 58 | DONE: 59 | return strings.TrimSpace(sb.String()) 60 | } 61 | -------------------------------------------------------------------------------- /agi/agi_bin.go: -------------------------------------------------------------------------------- 1 | package agi 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "go.uber.org/zap" 8 | ) 9 | 10 | type ConfEnv struct { 11 | ConfigDir string 12 | ConfigFile string 13 | ModuleDir string 14 | SpoolDir string 15 | MonitorDir string 16 | VarDir string 17 | DataDir string 18 | LogDir string 19 | AGIDir string 20 | KeyDir string 21 | RunDir string 22 | } 23 | 24 | func LoadConfEnv() ConfEnv { 25 | env := func(s string) string { 26 | v, _ := os.LookupEnv(s) 27 | return v 28 | } 29 | return ConfEnv{ 30 | ConfigDir: env("AST_CONFIG_DIR"), 31 | ConfigFile: env("AST_CONFIG_FILE"), 32 | ModuleDir: env("AST_MODULE_DIR"), 33 | SpoolDir: env("AST_SPOOL_DIR"), 34 | MonitorDir: env("AST_MONITOR_DIR"), 35 | VarDir: env("AST_VAR_DIR"), 36 | DataDir: env("AST_DATA_DIR"), 37 | LogDir: env("AST_LOG_DIR"), 38 | AGIDir: env("AST_AGI_DIR"), 39 | KeyDir: env("AST_KEY_DIR"), 40 | RunDir: env("AST_RUN_DIR"), 41 | } 42 | } 43 | 44 | func Run(handler HandlerFunc) { 45 | ctx := context.Background() 46 | session, err := NewSession(ctx, os.Stdin, os.Stdout, nil) 47 | if err != nil { 48 | zap.S().With("err", err).Error("init session failed") 49 | os.Exit(1) 50 | } 51 | handler(session) 52 | } 53 | -------------------------------------------------------------------------------- /ami/subscribe.go: -------------------------------------------------------------------------------- 1 | package ami 2 | 3 | import "context" 4 | 5 | type subscribe struct { 6 | f SubscribeFunc 7 | ctx context.Context 8 | unsub bool 9 | onSent bool 10 | onRecv bool 11 | } 12 | type SubscribeFunc func(ctx context.Context, message *Message) bool 13 | 14 | type SubscribeOption func(o *subscribe) error 15 | 16 | func SubscribeSend() SubscribeOption { 17 | return func(o *subscribe) error { 18 | o.onSent = true 19 | return nil 20 | } 21 | } 22 | 23 | func SubscribeSetContext(ctx context.Context) SubscribeOption { 24 | return func(o *subscribe) error { 25 | o.ctx = ctx 26 | return nil 27 | } 28 | } 29 | 30 | func (c *Conn) Subscribe(cb SubscribeFunc, opts ...SubscribeOption) (func(), error) { 31 | c.subLoc.Lock() 32 | defer c.subLoc.Unlock() 33 | 34 | sub := &subscribe{ 35 | f: cb, 36 | onRecv: true, 37 | } 38 | for _, v := range opts { 39 | err := v(sub) 40 | if err != nil { 41 | return nil, err 42 | } 43 | } 44 | 45 | c.subs = append(c.subs, sub) 46 | 47 | return func() { 48 | sub.unsub = true 49 | }, nil 50 | } 51 | func (c *Conn) cleanUnsub() { 52 | c.subLoc.Lock() 53 | defer c.subLoc.Unlock() 54 | var neo []*subscribe 55 | subs := c.subs 56 | for _, sub := range subs { 57 | if !sub.unsub { 58 | neo = append(neo, sub) 59 | } 60 | } 61 | c.subs = neo 62 | } 63 | -------------------------------------------------------------------------------- /astdb/ps_model.go: -------------------------------------------------------------------------------- 1 | package astdb 2 | 3 | import "database/sql" 4 | 5 | type PsAor struct { 6 | ID string `gorm:"primary_key"` 7 | Contact sql.NullString 8 | // 3600 9 | DefaultExpiration int 10 | MinimumExpiration int 11 | MaximumExpiration int 12 | Mailboxes sql.NullString 13 | MaxContacts int 14 | QualifyFrequency int 15 | QualifyTimeout float64 16 | AuthenticateQualify YesNo 17 | OutboundProxy sql.NullString 18 | SupportPath YesNo 19 | VoicemailExtension sql.NullString 20 | } 21 | 22 | type PsContact struct { 23 | ID string `gorm:"primary_key"` 24 | URI string 25 | ExpirationTime string 26 | QualifyFrequency int 27 | } 28 | type PsDomainAlias struct { 29 | ID string `gorm:"primary_key"` 30 | Domain string 31 | } 32 | type PsEndpointIdIp struct { 33 | ID string `gorm:"primary_key"` 34 | Endpoint string 35 | Match string 36 | } 37 | type PsAuth struct { 38 | ID string `gorm:"primary_key"` 39 | // userpass 40 | AuthType string 41 | NonceLifetime sql.NullString 42 | Md5Cred sql.NullString 43 | Username string 44 | Password string 45 | Realm sql.NullString 46 | } 47 | type PsGlobal struct { 48 | ID string `gorm:"primary_key"` 49 | MaxForwards *int 50 | UserAgent *string 51 | DefaultOutboundEndpoint *string 52 | } 53 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | skip-dirs-use-default: true 3 | skip-dirs: 4 | - vendor 5 | - ent 6 | - graph 7 | skip-files: 8 | - ".*\\.peg\\.go$" 9 | - ".*ignored_.*.go$" 10 | - "generated.go$" 11 | - model_gen.go 12 | 13 | linters: 14 | disable-all: true 15 | enable: 16 | - bodyclose 17 | - depguard 18 | - dogsled 19 | - dupl 20 | - funlen 21 | - goconst 22 | - gocritic 23 | - gocyclo 24 | - gofmt 25 | - goimports 26 | - golint 27 | - gomnd 28 | - gosec 29 | # - interfacer 30 | - misspell 31 | - unconvert 32 | 33 | # default 34 | - deadcode 35 | - errcheck 36 | - gosimple 37 | - govet 38 | - ineffassign 39 | - staticcheck 40 | - structcheck 41 | - typecheck 42 | - unused 43 | - varcheck 44 | 45 | linters-settings: 46 | dupl: 47 | threshold: 100 48 | funlen: 49 | lines: 100 50 | statements: 60 51 | depguard: 52 | list-type: blacklist 53 | packages: 54 | - golang.org/x/net/context 55 | - github.com/sirupsen/logrus 56 | - github.com/prometheus/common/log 57 | 58 | issues: 59 | # Excluding configuration per-path, per-linter, per-text and per-source 60 | exclude-rules: 61 | - path: _test\.go 62 | linters: 63 | - gomnd 64 | # https://github.com/go-critic/go-critic/issues/926 65 | - linters: 66 | - gocritic 67 | text: "unnecessaryDefer:" 68 | -------------------------------------------------------------------------------- /astdb/model.go: -------------------------------------------------------------------------------- 1 | package astdb 2 | 3 | import "time" 4 | 5 | // https://wiki.asterisk.org/wiki/display/AST/Asterisk+12+CEL+Specification 6 | type CallEventLog struct { 7 | ID int64 `gorm:"primary_key"` 8 | EventType string `gorm:"column:eventtype"` 9 | EventTime *time.Time `gorm:"column:eventtime"` 10 | UserDefType string `gorm:"column:userdeftype"` 11 | CidName string 12 | CidNum string 13 | CidAni string 14 | CidRdnis string 15 | CidDnid string 16 | Exten string 17 | Context string 18 | ChanName string `gorm:"column:channame"` 19 | AppName string `gorm:"column:appname"` 20 | AppData string `gorm:"column:appdata"` 21 | AmaFlags int `gorm:"column:amaflags"` 22 | AccountCode string `gorm:"column:accountcode"` 23 | PeerAccount string `gorm:"column:peeraccount"` 24 | UniqueId string `gorm:"column:uniqueid"` 25 | LinkedId string `gorm:"column:linkedid"` 26 | UserField string `gorm:"column:userfield"` 27 | Peer string `gorm:"column:peer"` 28 | } 29 | 30 | func (CallEventLog) TableName() string { 31 | return "cel" 32 | } 33 | 34 | // https://wiki.asterisk.org/wiki/display/AST/Asterisk+12+CDR+Specification 35 | type CallDetailRecord struct { 36 | } 37 | 38 | func (CallDetailRecord) TableName() string { 39 | return "cdr" 40 | } 41 | 42 | type Extension struct { 43 | ID int `gorm:"primary_key"` 44 | Context string 45 | Exten string 46 | Priority int 47 | App string 48 | AppData string 49 | } 50 | -------------------------------------------------------------------------------- /pkg/tools/xmlgen/_doc_test.go: -------------------------------------------------------------------------------- 1 | package xmlgen 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "encoding/xml" 7 | "os" 8 | "testing" 9 | 10 | log "github.com/sirupsen/logrus" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestDocParse(t *testing.T) { 15 | f, _ := os.Open("out.xml") 16 | defer f.Close() 17 | dec := xml.NewDecoder(f) 18 | var doc DocModel 19 | if err := dec.Decode(&doc); err != nil { 20 | log.Fatal(err) 21 | } 22 | //for _, v := range doc.Agi { 23 | // if v.Name == "control stream file" { 24 | // indent, err := json.MarshalIndent(v, "", " ") 25 | // if err != nil { 26 | // log.Fatal(err) 27 | // } 28 | // fmt.Println(string(indent)) 29 | // return 30 | // } 31 | //} 32 | 33 | ge := NewAGIGenerator() 34 | ge.Debug = true 35 | d := &Model{} 36 | BuildModel(&doc, d) 37 | 38 | { 39 | b, err := json.MarshalIndent(doc, "", " ") 40 | assert.NoError(t, err) 41 | err = os.WriteFile("doc.json", b, 0644) 42 | assert.NoError(t, err) 43 | } 44 | { 45 | b, err := json.MarshalIndent(d, "", " ") 46 | assert.NoError(t, err) 47 | err = os.WriteFile("gen.json", b, 0644) 48 | assert.NoError(t, err) 49 | } 50 | 51 | err := ge.Generate(context.Background(), d) 52 | if err != nil { 53 | panic(err) 54 | } 55 | err = ge.Write(WriteConfig{ 56 | Target: "./../../../", 57 | //DryRun: true, 58 | }) 59 | if err != nil { 60 | panic(err) 61 | } 62 | log.Println("AGI Commands", len(d.AGICommands)) 63 | } 64 | 65 | //func TestName(t *testing.T) { 66 | // println(fieldName("Variablename")) 67 | // println(fieldName("Keytree")) 68 | //} 69 | -------------------------------------------------------------------------------- /ami/amimodels/iface.go: -------------------------------------------------------------------------------- 1 | package amimodels 2 | 3 | import "errors" 4 | 5 | type Event interface { 6 | EventTypeName() string 7 | } 8 | type Action interface { 9 | ActionTypeName() string 10 | } 11 | type HasActionID interface { 12 | GetActionID() string 13 | SetActionID(actionID string) 14 | } 15 | type Handler interface { 16 | Request(req *Request) *Response 17 | } 18 | type HandlerFunc func(req *Request) *Response 19 | 20 | func (f HandlerFunc) Request(req *Request) *Response { 21 | return f(req) 22 | } 23 | 24 | type Client struct { 25 | Handler 26 | } 27 | 28 | func (cli *Client) Action(act Action, res interface{}, opts ...RequestOption) error { 29 | r, err := BuildRequest(act, res, opts...) 30 | if err != nil { 31 | return err 32 | } 33 | return cli.Request(r).Err() 34 | } 35 | 36 | type Response struct { 37 | Response string // Success, Error 38 | ActionID string 39 | Message string 40 | Timestamp string // 1621260071.803488 41 | Headers map[string]string 42 | Error error 43 | } 44 | 45 | func (res *Response) Err() error { 46 | if res.Error != nil { 47 | return res.Error 48 | } 49 | if res.Response == "Error" { 50 | return errors.New(res.Message) 51 | } 52 | return nil 53 | } 54 | 55 | type Request struct { 56 | Action Action 57 | Response *Response 58 | } 59 | type RequestOption func(r *Request) error 60 | 61 | func BuildRequest(act Action, res interface{}, opts ...RequestOption) (r *Request, err error) { 62 | r = &Request{ 63 | Action: act, 64 | } 65 | for _, v := range opts { 66 | if err = v(r); err != nil { 67 | return nil, err 68 | } 69 | } 70 | return 71 | } 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff: 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/dictionaries 10 | 11 | # Sensitive or high-churn files: 12 | .idea/**/dataSources/ 13 | .idea/**/dataSources.ids 14 | .idea/**/dataSources.xml 15 | .idea/**/dataSources.local.xml 16 | .idea/**/sqlDataSources.xml 17 | .idea/**/dynamic.xml 18 | .idea/**/uiDesigner.xml 19 | 20 | # Gradle: 21 | .idea/**/gradle.xml 22 | .idea/**/libraries 23 | 24 | # CMake 25 | cmake-build-debug/ 26 | 27 | # Mongo Explorer plugin: 28 | .idea/**/mongoSettings.xml 29 | 30 | ## File-based project format: 31 | *.iws 32 | 33 | ## Plugin-specific files: 34 | 35 | # IntelliJ 36 | out/ 37 | 38 | # mpeltonen/sbt-idea plugin 39 | .idea_modules/ 40 | 41 | # JIRA plugin 42 | atlassian-ide-plugin.xml 43 | 44 | # Cursive Clojure plugin 45 | .idea/replstate.xml 46 | 47 | # Crashlytics plugin (for Android Studio and IntelliJ) 48 | com_crashlytics_export_strings.xml 49 | crashlytics.properties 50 | crashlytics-build.properties 51 | fabric.properties 52 | ### Go template 53 | # Binaries for programs and plugins 54 | *.exe 55 | *.dll 56 | *.so 57 | *.dylib 58 | 59 | # Test binary, build with `go test -c` 60 | *.test 61 | 62 | # Output of the go coverage tool, specifically when used with LiteIDE 63 | *.out 64 | 65 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 66 | .glide/ 67 | 68 | 69 | *.log 70 | ignored 71 | .idea 72 | .cache 73 | dist 74 | .DS_Store 75 | ignored* 76 | *ignored_test.go 77 | bin/ 78 | /sparkle.yaml 79 | *.local 80 | *.sqlite3 81 | *.sqlite 82 | *.db 83 | -------------------------------------------------------------------------------- /ami/msg_test.go: -------------------------------------------------------------------------------- 1 | package ami 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "io" 7 | "sort" 8 | "strings" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | "github.com/wenerme/astgo/ami/amimodels" 13 | ) 14 | 15 | func TestMsgIO(t *testing.T) { 16 | for _, test := range []struct { 17 | msg *Message 18 | out string 19 | }{ 20 | { 21 | msg: &Message{ 22 | Type: MessageTypeEvent, 23 | Name: "Name", 24 | Attributes: map[string]interface{}{ 25 | "Text": "12\r\n34", 26 | "More": "Yes", 27 | }, 28 | }, 29 | // more after multi line 30 | out: "Event: Name\r\nText: 12\r\n34\r\nMore: Yes\r\n\r\n", 31 | }, 32 | { 33 | msg: &Message{ 34 | Type: MessageTypeEvent, 35 | Name: "Name", 36 | Attributes: map[string]interface{}{ 37 | "Text": "12\r\n34", 38 | }, 39 | }, 40 | // simple multi line 41 | out: "Event: Name\r\nText: 12\r\n34\r\n\r\n", 42 | }, 43 | 44 | { 45 | msg: &Message{ 46 | Type: MessageTypeEvent, 47 | Name: "FullyBooted", 48 | Attributes: map[string]interface{}{ 49 | "Uptime": "1234", 50 | }, 51 | }, 52 | out: "Event: FullyBooted\r\nUptime: 1234\r\n\r\n", 53 | }, 54 | {msg: MustConvertToMessage(amimodels.FullyBootedEvent{ 55 | Uptime: "1234", 56 | }), 57 | out: "Event: FullyBooted\r\nUptime: 1234\r\n\r\n"}, 58 | } { 59 | msg := test.msg 60 | 61 | // ignore order 62 | exp := strings.Split(test.out, "\r\n") 63 | act := strings.Split(msg.Format(), "\r\n") 64 | sort.Strings(exp) 65 | sort.Strings(act) 66 | assert.Equal(t, exp, act) 67 | 68 | r := bufio.NewReader(bytes.NewReader([]byte(test.out))) 69 | rm := &Message{} 70 | assert.NoError(t, rm.Read(r)) 71 | assert.Equal(t, msg.Type, rm.Type) 72 | assert.Equal(t, msg.Name, rm.Name) 73 | assert.Equal(t, msg.Attributes, rm.Attributes) 74 | _, err := r.ReadByte() 75 | assert.Equal(t, io.EOF, err) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /pkg/tools/xmlgen/template/agi/command.tmpl: -------------------------------------------------------------------------------- 1 | {{/* gotype: github.com/wenerme/astgo/pkg/tools/xmlgen.AGICommand*/}} 2 | {{- define "agi/command"}} 3 | // {{.StructName}}Command {{.Synopsis}} 4 | {{- with .Description}} 5 | // {{end}} 6 | {{- with .Description}}{{template "comment" .}} {{end}} 7 | type {{.StructName}}Command struct { 8 | {{- range $_,$v:=.Syntax.Params}} 9 | {{- with $v.Description}} {{template "comment" (printf "%v %v" $v.Name .)}} {{end}} 10 | {{$v.Name}} {{with not $v.Required}}*{{end}}{{$v.Type}} {{with $v.Default}} // default to {{$v.Default}} {{end}} 11 | {{- end}} 12 | {{with .Syntax.Missing}} // has missing params {{- end}} 13 | } 14 | func(cmd {{.StructName}}Command)Command() (string, error) { 15 | s := []interface{}{cmd.CommandString(),{{- range $_,$v:=.Syntax.Params}} cmd.{{.Name}}, {{- end}}} 16 | return joinCommand(s), nil 17 | } 18 | func(cmd {{.StructName}}Command)CommandString()string { 19 | return "{{.Name | upper}}" 20 | } 21 | 22 | {{$s:=.}} 23 | {{- range $_,$v:=.Syntax.Params}} 24 | {{- if not $v.Required}} 25 | func (cmd {{$s.StructName}}Command)Set{{$v.Name}}(v {{$v.Type}}) {{$s.StructName}}Command { 26 | cmd.{{$v.Name}} = &v 27 | return cmd 28 | } 29 | {{- end}} 30 | {{- end}} 31 | 32 | func (c *Client) {{$s.StructName}}( 33 | {{- range $_,$v:=.Syntax.Params -}} 34 | {{- if $v.Required}} {{$v.Name | untitle}} {{$v.Type}}, {{- end -}} 35 | {{- end -}} 36 | ) Response { 37 | return c.Handler.Command({{$s.StructName}}Command{ 38 | {{- range $_,$v:=.Syntax.Params}} 39 | {{- if $v.Required}} 40 | {{$v.Name}}: {{$v.Name | untitle}}, 41 | {{- end}} 42 | {{- end}} 43 | }) 44 | } 45 | 46 | {{- end}} 47 | 48 | {{/* gotype: github.com/wenerme/astgo/pkg/tools/xmlgen.SyntaxParamDoc*/}} 49 | {{- define "agi/command/field"}} 50 | {{- with .Description}} {{template "comment" (printf "%v %v" $.Name .)}} {{end}} 51 | {{$.Name}} {{with not $.Required}}*{{end}}{{$.Type}} {{with $.Default}} // default to {{$.Default}} {{end}} 52 | {{- end}} 53 | -------------------------------------------------------------------------------- /pkg/tools/xmlgen/template/ami/action.tmpl: -------------------------------------------------------------------------------- 1 | {{/* gotype: github.com/wenerme/astgo/pkg/tools/xmlgen.ManagerAction*/}} 2 | {{- define "ami/action"}} 3 | {{with .Synopsis}} {{template "comment" (printf "%v %v" $.StructName .)}} {{end}} 4 | type {{.StructName}} struct { 5 | {{- template "ami/fields" .}} 6 | } 7 | func({{.StructName}})ActionTypeName() string{ 8 | return "{{.Name}}" 9 | } 10 | func(a {{.StructName}})GetActionID() string{ 11 | return a.ActionID 12 | } 13 | func(a*{{.StructName}})SetActionID(actionID string) { 14 | a.ActionID = actionID 15 | } 16 | {{- if eq (len .Responses) 1}} 17 | func(cli*Client){{.Name}}(req {{.StructName}},opts ...RequestOption)(res *{{(index .Responses 0).StructName}}, err error) { 18 | req := &{{.StructName}}{ 19 | {{- range $_,$v:=.Syntax.Params}} 20 | {{- if $v.Required}} 21 | {{$v.Name}}: {{$v.Name | untitle}}, 22 | {{- end}} 23 | {{- end}} 24 | } 25 | res = &{{(index .Responses 0).StructName}}{} 26 | return res, cli.Action(req,res) 27 | } 28 | {{- end}} 29 | {{- if eq (len .Responses) 0}} 30 | func(cli*Client){{.Name}}({{- range $_,$v:=.Syntax.Params -}} 31 | {{- if $v.Required}} {{$v.Name | ParamName}} {{$v.Type}}, {{- end -}} 32 | {{- end -}} opts ...RequestOption)(res *Response, err error) { 33 | req := &{{.StructName}}{ 34 | {{- range $_,$v:=.Syntax.Params}} 35 | {{- if $v.Required}} 36 | {{$v.Name}}: {{$v.Name | ParamName}}, 37 | {{- end}} 38 | {{- end}} 39 | } 40 | res = &Response{} 41 | return res, cli.Action(req,res,opts...) 42 | } 43 | {{- end}} 44 | {{- end}} 45 | 46 | {{- define "comment"}} 47 | {{regexReplaceAllLiteral "(?m)^" (. | trim) "// "}} 48 | {{- end}} 49 | 50 | {{- define "ami/fields" -}} 51 | {{- range $_,$v:=.Syntax.Params -}} 52 | {{- with $v.Description}} {{template "comment" (printf "%v %v" $v.Name .)}} {{end}} 53 | {{$v.Name}} {{$v.Type}} {{with $v.Default}} // default to {{$v.Default}} {{end}} 54 | {{- end}} 55 | {{with .Syntax.Missing}} // has missing params {{- end}} 56 | {{- end}} 57 | -------------------------------------------------------------------------------- /ami/request.go: -------------------------------------------------------------------------------- 1 | package ami 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/pkg/errors" 8 | ) 9 | 10 | const attrActionID = "ActionID" 11 | 12 | type requestOptions struct { 13 | Timeout time.Duration 14 | OnComplete func(ctx context.Context, msg *Message, err error) 15 | } 16 | type RequestOption func(o *requestOptions) error 17 | 18 | func RequestTimeout(d time.Duration) RequestOption { 19 | return func(o *requestOptions) error { 20 | o.Timeout = d 21 | return nil 22 | } 23 | } 24 | 25 | // RequestResponseCallback will case Conn.Request run async, will not timeout 26 | func RequestResponseCallback(cb func(ctx context.Context, msg *Message, err error)) RequestOption { 27 | return func(o *requestOptions) error { 28 | o.OnComplete = cb 29 | return nil 30 | } 31 | } 32 | 33 | func (c *Conn) Request(r interface{}, opts ...RequestOption) (resp *Message, err error) { 34 | var msg *Message 35 | msg, err = ConvertToMessage(r) 36 | if err != nil { 37 | return nil, err 38 | } 39 | if msg.Type != MessageTypeAction { 40 | return nil, errors.Errorf("can only request action: %v", msg.Type) 41 | } 42 | 43 | async := &asyncMsg{ 44 | id: c.nextID(), 45 | msg: msg, 46 | result: make(chan *asyncMsg, 1), 47 | ctx: context.Background(), 48 | } 49 | o := requestOptions{ 50 | Timeout: time.Second * 30, 51 | } 52 | for _, opt := range opts { 53 | if err = opt(&o); err != nil { 54 | return 55 | } 56 | } 57 | 58 | onComplete := o.OnComplete 59 | if onComplete != nil { 60 | async.cb = func(v *asyncMsg) { 61 | onComplete(v.ctx, v.resp, v.err) 62 | } 63 | } 64 | 65 | msg.SetAttr(attrActionID, async.id) 66 | 67 | if async.cb == nil { 68 | var cancel context.CancelFunc 69 | // todo allowed custom timeout 70 | async.ctx, cancel = context.WithTimeout(async.ctx, o.Timeout) 71 | defer cancel() 72 | } 73 | 74 | // enqueue 75 | c.pending <- async 76 | 77 | if async.cb != nil { 78 | return nil, errors.New("No response yet") 79 | } 80 | 81 | select { 82 | case <-async.ctx.Done(): 83 | return nil, async.ctx.Err() 84 | case <-async.result: 85 | err = async.err 86 | if err == nil && async.resp != nil { 87 | err = async.resp.Error() 88 | } 89 | return async.resp, err 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /pkg/tools/xmlgen/gen_model_build.go: -------------------------------------------------------------------------------- 1 | package xmlgen 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func BuildModel(in *DocModel, out *Model) { 9 | for _, v := range in.Agi { 10 | out.AGICommands = append(out.AGICommands, in.AGIModel(v)) 11 | } 12 | for _, v := range in.Manager { 13 | out.Actions = append(out.Actions, in.ManagerModel(v)) 14 | } 15 | for _, v := range in.ManagerEvent { 16 | out.Events = append(out.Events, in.ManagerEventModel(v)) 17 | } 18 | // 19 | Dedup(&out.Actions, func(i int) string { 20 | return out.Actions[i].Name 21 | }) 22 | Dedup(&out.Events, func(i int) string { 23 | return out.Events[i].Name 24 | }) 25 | for _, v := range out.Events { 26 | Dedup(&v.Syntax.Params, func(i int) string { 27 | return v.Syntax.Params[i].Name 28 | }) 29 | } 30 | 31 | // add missing event, unify event 32 | events := map[string]*ManagerEvent{} 33 | for _, v := range out.Events { 34 | events[v.Name] = v 35 | } 36 | for _, action := range out.Actions { 37 | for k, v := range action.Responses { 38 | if e, ok := events[v.Name]; ok { 39 | action.Responses[k] = e 40 | } else { 41 | out.Events = append(out.Events, v) 42 | fmt.Println("Append Response Event", v.Name) 43 | } 44 | } 45 | } 46 | 47 | // add missing ActionID 48 | for _, v := range out.Actions { 49 | found := false 50 | for _, v := range v.Syntax.Params { 51 | if found = v.Name == "ActionID"; found { 52 | break 53 | } 54 | } 55 | if !found { 56 | o := []*Parameter{{ 57 | Name: "ActionID", 58 | Type: "string", 59 | Description: "ActionID for this transaction. Will be returned.", 60 | }} 61 | v.Syntax.Params = append(o, v.Syntax.Params...) 62 | } 63 | } 64 | } 65 | 66 | func validGoTypeName(s string) string { 67 | s = invalid.ReplaceAllLiteralString(s, "") 68 | return s 69 | } 70 | func Dedup(arr interface{}, cond func(int) string) { 71 | m := map[string]bool{} 72 | Filter(arr, func(i int) bool { 73 | k := cond(i) 74 | if _, ok := m[k]; ok { 75 | return false 76 | } 77 | m[k] = true 78 | return true 79 | }) 80 | } 81 | 82 | func Filter(arr interface{}, cond func(int) bool) { 83 | rv := reflect.ValueOf(arr).Elem() 84 | rt := rv.Type() 85 | 86 | neo := reflect.MakeSlice(rt, 0, 0) 87 | for i := 0; i < rv.Len(); i++ { 88 | if v := rv.Index(i); cond(i) { 89 | neo = reflect.Append(neo, v) 90 | } 91 | } 92 | rv.Set(neo) 93 | } 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Asterisk to Go [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![Go Report Card][report-card-img]][report-card] 2 | 3 | Libs for Golang to work with Asterisk 4 | 5 | * AMI 6 | * AGI 7 | 8 | [doc-img]: http://img.shields.io/badge/GoDoc-Reference-blue.svg 9 | [doc]: https://pkg.go.dev/github.com/wenerme/astgo 10 | 11 | [ci-img]: https://github.com/wenerme/astgo/actions/workflows/ci.yml/badge.svg 12 | [ci]: https://github.com/wenerme/astgo/actions/workflows/ci.yml 13 | 14 | [cov-img]: https://codecov.io/gh/wenerme/astgo/branch/master/graph/badge.svg 15 | [cov]: https://codecov.io/gh/wenerme/astgo/branch/master 16 | 17 | [report-card-img]: https://goreportcard.com/badge/github.com/wenerme/astgo 18 | [report-card]: https://goreportcard.com/report/github.com/wenerme/astgo 19 | 20 | ## AMI 21 | 22 | * Async Action 23 | * Sync Action 24 | * Event Subscribe 25 | * Auto Reconnect 26 | * Generated Action with document 27 | * Generated Event with document 28 | * Generated Client - Action -> Response 29 | 30 | ```go 31 | package main 32 | 33 | import ( 34 | "context" 35 | "fmt" 36 | "github.com/wenerme/astgo/ami" 37 | ) 38 | 39 | func main() { 40 | boot := make(chan *ami.Message, 1) 41 | 42 | conn, err := ami.Connect( 43 | "192.168.1.1:5038", 44 | ami.WithAuth("admin", "admin"), // AMI auth 45 | // add predefined subscriber 46 | ami.WithSubscribe(ami.SubscribeFullyBootedChanOnce(boot)), 47 | ami.WithSubscribe(func(ctx context.Context, msg *ami.Message) bool { 48 | fmt.Println(msg.Format()) // log everything 49 | return true // keep subscribe 50 | }, ami.SubscribeSend(), // subscribe send message - default recv only 51 | )) 52 | if err != nil { 53 | panic(err) 54 | } 55 | <-boot 56 | // AMI now FullyBooted 57 | _ = conn 58 | } 59 | ``` 60 | 61 | ## AGI 62 | 63 | * FastAGI 64 | * AGI Bin 65 | * Generated Command 66 | * Generated Client 67 | 68 | ```go 69 | package main 70 | 71 | import ( 72 | "github.com/wenerme/astgo/agi" 73 | ) 74 | 75 | func main() { 76 | // agi.Listen for FastAGI 77 | agi.Run(func(session *agi.Session) { 78 | client := session.Client() 79 | client.Answer() 80 | client.StreamFile("activated", "#") 81 | client.SetVariable("AGISTATUS", "SUCCESS") 82 | client.Hangup() 83 | }) 84 | } 85 | ``` 86 | 87 | ## Asterisk Database Model 88 | * GORM Based Model 89 | 90 | ## Roadmap 91 | * [-] Asterisk Database Model 92 | * [ ] Stasis 93 | * [ ] ARI 94 | -------------------------------------------------------------------------------- /pkg/tools/xmlgen/gen_model.go: -------------------------------------------------------------------------------- 1 | package xmlgen 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/huandu/xstrings" 7 | ) 8 | 9 | type AstDoc struct { 10 | Actions []*Type 11 | Events []*Type 12 | } 13 | type Model struct { 14 | AGICommands []*AGICommand 15 | Actions []*ManagerAction 16 | Events []*ManagerEvent 17 | } 18 | type AGICommand struct { 19 | Name string 20 | Module string 21 | Syntax *Syntax 22 | Synopsis string 23 | Description string 24 | SeeAlso []*SeeAlso 25 | } 26 | type ManagerAction struct { 27 | Name string 28 | Synopsis string 29 | Syntax *Syntax 30 | Description string 31 | Response *ManagerEvent 32 | SeeAlso []*SeeAlso 33 | Responses []*ManagerEvent 34 | } 35 | 36 | func (m ManagerAction) StructName() string { 37 | return validGoTypeName(m.Name) + "Action" 38 | } 39 | 40 | type ManagerEvent struct { 41 | Name string 42 | Synopsis string 43 | Syntax *Syntax 44 | SeeAlso []*SeeAlso 45 | } 46 | 47 | func (m ManagerEvent) StructName() string { 48 | return validGoTypeName(m.Name) + "Event" 49 | } 50 | 51 | type SeeAlso struct { 52 | Type string 53 | Name string 54 | } 55 | 56 | var nameFixReplace = strings.NewReplacer("Callerid", "CallerID") 57 | 58 | func (a AGICommand) StructName() string { 59 | switch a.Name { 60 | case "set autohangup": 61 | return "SetAutoHangup" 62 | case "asyncagi break": 63 | return "AsyncAGIBreak" 64 | case "database deltree": 65 | return "DatabaseDelTree" 66 | } 67 | name := xstrings.ToCamelCase(a.Name) 68 | name = nameFixReplace.Replace(name) 69 | return name 70 | } 71 | 72 | type Syntax struct { 73 | Params []*Parameter 74 | Missing bool 75 | } 76 | 77 | func (d Syntax) HasRequiredParam() bool { 78 | for _, v := range d.Params { 79 | if v.Required { 80 | return true 81 | } 82 | } 83 | return false 84 | } 85 | 86 | type Parameter struct { 87 | RawName string 88 | Name string 89 | Type string 90 | Default string 91 | Required bool 92 | Description string 93 | /* 94 | enumlist 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | */ 106 | } 107 | -------------------------------------------------------------------------------- /ami/conn_internal.go: -------------------------------------------------------------------------------- 1 | package ami 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "os" 7 | "time" 8 | ) 9 | 10 | // promise like 11 | type asyncMsg struct { 12 | id string 13 | msg *Message 14 | resp *Message 15 | err error 16 | result chan *asyncMsg 17 | cb func(v *asyncMsg) 18 | ctx context.Context 19 | done bool 20 | } 21 | 22 | func (async *asyncMsg) complete() { 23 | if async.done { 24 | return 25 | } 26 | if async.result != nil { 27 | async.result <- async 28 | close(async.result) 29 | } 30 | if async.cb != nil { 31 | go func() { 32 | async.cb(async) 33 | }() 34 | } 35 | async.done = true 36 | } 37 | 38 | func (c *Conn) read(ctx context.Context) (err error) { 39 | log := c.logger 40 | log.Debug("start read loop") 41 | defer func() { 42 | log.Debug("done read loop") 43 | }() 44 | for { 45 | msg := &Message{} 46 | 47 | if err = msg.Read(c.reader); err != nil && !errors.Is(err, os.ErrDeadlineExceeded) { 48 | return 49 | } 50 | 51 | if msg.Type != "" { 52 | c.recv <- msg 53 | } 54 | if ctx.Err() != nil { 55 | return ctx.Err() 56 | } 57 | } 58 | } 59 | 60 | func (c *Conn) loop(ctx context.Context) (err error) { 61 | ids := map[string]*asyncMsg{} 62 | defer func() { 63 | for _, v := range ids { 64 | v.err = err 65 | v.complete() 66 | } 67 | }() 68 | 69 | log := c.logger 70 | cleanTicker := time.NewTicker(5 * time.Second) 71 | 72 | c.logger.Debug("start event loop") 73 | defer func() { 74 | log.Debug("done event loop") 75 | }() 76 | for { 77 | select { 78 | case <-ctx.Done(): 79 | return ctx.Err() 80 | case <-cleanTicker.C: 81 | c.cleanUnsub() 82 | case async := <-c.pending: 83 | //err = c.conn.SetDeadline(time.Now().Add(1 * time.Second)) 84 | //if err != nil { 85 | // return 86 | //} 87 | 88 | // send pending message 89 | msg := async.msg 90 | //log.Sugar().With("id", async.id, "type", msg.Type, "name", msg.Name).Debug("send message") 91 | 92 | err = msg.Write(c.conn) 93 | 94 | if err != nil { 95 | async.err = err 96 | async.complete() 97 | } else if async.id != "" { 98 | ids[async.id] = async 99 | } 100 | c.onSend(async) 101 | case msg := <-c.recv: 102 | // received 103 | if msg.Type == MessageTypeResponse { 104 | id := msg.AttrString(attrActionID) 105 | if id != "" { 106 | async := ids[id] 107 | if async == nil { 108 | log.Sugar().With("id", id).Warn("response untracked") 109 | } else { 110 | async.resp = msg 111 | async.complete() 112 | } 113 | } 114 | } 115 | c.onRecv(msg) 116 | } 117 | } 118 | } 119 | 120 | func (c *Conn) onSend(msg *asyncMsg) { 121 | subs := c.subs 122 | 123 | for _, v := range subs { 124 | if v.unsub || !v.onSent { 125 | continue 126 | } 127 | ctx := v.ctx 128 | if ctx == nil { 129 | ctx = c.ctx 130 | } 131 | v.unsub = !v.f(ctx, msg.msg) 132 | } 133 | } 134 | 135 | func (c *Conn) onRecv(msg *Message) { 136 | subs := c.subs 137 | for _, v := range subs { 138 | if v.unsub || !v.onRecv { 139 | continue 140 | } 141 | ctx := v.ctx 142 | if ctx == nil { 143 | ctx = c.ctx 144 | } 145 | // NOTE blocked 146 | v.unsub = !v.f(ctx, msg) 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /pkg/tools/xmlgen/build.go: -------------------------------------------------------------------------------- 1 | package xmlgen 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | func BuildAstDoc(doc *Docs, d *AstDoc) { 11 | byName := map[string]*Type{} 12 | byFieldName := map[string]map[string]*Field{} 13 | for _, e := range doc.Manager { 14 | typ, dup := byName[e.Name] 15 | if !dup { 16 | log.Println("duplicate action", e.Name) 17 | typ = &Type{ 18 | Name: e.Name, 19 | Doc: strings.TrimSpace(e.Synopsis), 20 | } 21 | } 22 | 23 | if byFieldName[typ.Name] == nil { 24 | byFieldName[typ.Name] = map[string]*Field{} 25 | } 26 | 27 | for _, p := range e.Syntax.Parameter { 28 | name := p.Name 29 | fieldName := goName(name) 30 | 31 | field, dup := byFieldName[typ.Name][fieldName] 32 | if dup { 33 | log.Println("duplicate field", typ.Name, fieldName) 34 | continue 35 | } 36 | 37 | field = &Field{ 38 | Name: fieldName, 39 | Required: p.Required != "", 40 | Default: p.Default, 41 | Type: &TypeInfo{Type: "string"}, 42 | } 43 | 44 | var s []string 45 | for _, v := range p.Para { 46 | text := strings.TrimSpace(v.Text) 47 | if text != "" { 48 | s = append(s, text) 49 | } else { 50 | log.Println(v) 51 | } 52 | } 53 | field.Doc = strings.Join(s, "\n") 54 | 55 | if field.Name != name { 56 | field.StructTag = fmt.Sprintf(`json:"%s,omitempty"`, name) 57 | } 58 | 59 | byFieldName[typ.Name][fieldName] = field 60 | typ.Fields = append(typ.Fields, field) 61 | } 62 | 63 | if !dup { 64 | byName[typ.Name] = typ 65 | d.Actions = append(d.Actions, typ) 66 | } 67 | } 68 | { 69 | byName := map[string]*Type{} 70 | byFieldName := map[string]map[string]*Field{} 71 | for _, e := range doc.ManagerEvent { 72 | typeName := e.Name 73 | typ, dup := byName[typeName] 74 | e := e.ManagerEventInstance 75 | if !dup { 76 | log.Println("duplicate action", typeName) 77 | typ = &Type{ 78 | Name: goName(typeName), 79 | Doc: strings.TrimSpace(e.Synopsis), 80 | } 81 | } 82 | 83 | if byFieldName[typ.Name] == nil { 84 | byFieldName[typ.Name] = map[string]*Field{} 85 | } 86 | 87 | for _, p := range e.Syntax.Parameter { 88 | name := p.Name 89 | fieldName := goName(name) 90 | 91 | _, dup := byFieldName[typ.Name][fieldName] 92 | if dup { 93 | log.Println("duplicate field", typ.Name, fieldName) 94 | continue 95 | } 96 | 97 | field := &Field{ 98 | Name: fieldName, 99 | Required: p.Required != "", 100 | // Default: p.Default, 101 | Type: &TypeInfo{Type: "string"}, 102 | } 103 | 104 | var s []string 105 | for _, v := range p.Para { 106 | text := strings.TrimSpace(v.Text) 107 | if text != "" { 108 | s = append(s, text) 109 | } else { 110 | log.Println(v) 111 | } 112 | } 113 | field.Doc = strings.Join(s, "\n") 114 | 115 | if field.Name != name { 116 | field.StructTag = fmt.Sprintf(`json:"%s,omitempty"`, name) 117 | } 118 | 119 | byFieldName[typ.Name][fieldName] = field 120 | typ.Fields = append(typ.Fields, field) 121 | } 122 | 123 | if !dup { 124 | byName[typ.Name] = typ 125 | d.Events = append(d.Events, typ) 126 | } 127 | } 128 | } 129 | for _, v := range d.Actions { 130 | _, ok := byFieldName[v.Name]["ActionID"] 131 | if !ok { 132 | v.Fields = append(v.Fields, &Field{ 133 | Name: "ActionID", 134 | Doc: "ActionID for tx - compensate", 135 | Type: &TypeInfo{ 136 | Type: "string", 137 | }, 138 | }) 139 | } 140 | } 141 | 142 | } 143 | 144 | var invalid = regexp.MustCompile(`[- ]|\(.*?\)`) 145 | var valid = regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9]*$") 146 | 147 | func goName(s string) string { 148 | s = invalid.ReplaceAllLiteralString(s, "") 149 | if s[0] >= '0' && s[0] <= '9' { 150 | s = "Field" + s 151 | } 152 | // UnitAmount(0) 153 | if !valid.MatchString(s) { 154 | panic("invalid id: " + s) 155 | } 156 | return s 157 | } 158 | -------------------------------------------------------------------------------- /ami/msg.go: -------------------------------------------------------------------------------- 1 | package ami 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "io" 8 | "strings" 9 | 10 | "github.com/mitchellh/mapstructure" 11 | "github.com/pkg/errors" 12 | "github.com/wenerme/astgo/ami/amimodels" 13 | ) 14 | 15 | type MessageType string 16 | 17 | const ( 18 | MessageTypeAction MessageType = "Action" 19 | MessageTypeEvent MessageType = "Event" 20 | MessageTypeResponse MessageType = "Response" 21 | ) 22 | 23 | type Message struct { 24 | Type MessageType // type of message 25 | Name string // name of message 26 | Attributes map[string]interface{} 27 | } 28 | 29 | func ReadMessage(m *Message, rd io.Reader) (err error) { 30 | r := bufio.NewReader(rd) 31 | 32 | m.Attributes = map[string]interface{}{} 33 | var line string 34 | line, err = r.ReadString('\n') 35 | if err != nil { 36 | return err 37 | } 38 | RETRY: 39 | line = strings.TrimSuffix(line, "\r\n") 40 | if line == "" { 41 | goto RETRY 42 | } 43 | sp := strings.SplitN(line, ":", 2) 44 | if len(sp) != 2 { 45 | return errors.Errorf("invalid type line read: %q", line) 46 | } 47 | m.Type = MessageType(sp[0]) 48 | m.Name = strings.TrimSpace(sp[1]) 49 | switch m.Type { 50 | case MessageTypeAction: 51 | case MessageTypeEvent: 52 | case MessageTypeResponse: 53 | default: 54 | return errors.Errorf("invalid message type: %q", sp[0]) 55 | } 56 | 57 | var stack [][]string 58 | for { 59 | // may contain BOM 60 | line, err = r.ReadString('\n') 61 | if err != nil { 62 | return err 63 | } 64 | if line == "\r\n" { 65 | break 66 | } 67 | sp = strings.SplitN(line, ":", 2) 68 | 69 | switch { 70 | case len(sp) == 2 && strings.HasSuffix(line, "\r\n"): 71 | // valid line 72 | stack = append(stack, sp) 73 | case len(stack) == 0 && len(sp) == 2: 74 | // first line 75 | stack = append(stack, sp) 76 | case len(stack) != 0: 77 | // continue line 78 | stack = append(stack, []string{"", line}) 79 | } 80 | } 81 | 82 | var k, v string 83 | for _, pair := range stack { 84 | switch { 85 | case pair[0] != "" && k != "": 86 | m.Attributes[k] = strings.TrimSuffix(v, "\r\n") 87 | k = "" 88 | v = "" 89 | fallthrough 90 | case pair[0] != "": 91 | k = pair[0] 92 | v = strings.TrimLeft(pair[1], " ") 93 | case pair[0] == "": 94 | v += pair[1] 95 | } 96 | } 97 | if k != "" { 98 | m.Attributes[k] = strings.TrimSuffix(v, "\r\n") 99 | } 100 | return 101 | } 102 | func WriteMessage(m *Message, w io.Writer) (err error) { 103 | wr := bufio.NewWriter(w) 104 | _, _ = wr.WriteString(fmt.Sprintf("%v: %v\r\n", m.Type, m.Name)) 105 | for k, v := range m.Attributes { 106 | _, _ = wr.WriteString(fmt.Sprintf("%v: %v\r\n", k, v)) 107 | } 108 | _, _ = wr.WriteString("\r\n") 109 | err = wr.Flush() 110 | return 111 | } 112 | 113 | func (m *Message) Read(r *bufio.Reader) (err error) { 114 | return ReadMessage(m, r) 115 | } 116 | func (m *Message) Write(w io.Writer) (err error) { 117 | return WriteMessage(m, w) 118 | } 119 | 120 | func (m Message) Format() string { 121 | b := bytes.Buffer{} 122 | _ = m.Write(&b) 123 | return b.String() 124 | } 125 | 126 | func (m *Message) AttrString(name string) string { 127 | if m.Attributes == nil { 128 | return "" 129 | } 130 | msg := m.Attributes[name] 131 | if msg == nil { 132 | return "" 133 | } 134 | return fmt.Sprint(msg) 135 | } 136 | func (m *Message) Message() string { 137 | return m.AttrString("Message") 138 | } 139 | 140 | func (m *Message) Success() bool { 141 | if m.Type == MessageTypeResponse && m.Name == "Success" { 142 | return true 143 | } 144 | return false 145 | } 146 | 147 | func (m *Message) Error() error { 148 | if m.Type == MessageTypeResponse && m.Name == "Error" { 149 | msg := m.Message() 150 | if msg == "" { 151 | msg = "error response" 152 | } 153 | return errors.New(msg) 154 | } 155 | return nil 156 | } 157 | func (m *Message) SetAttr(name string, val interface{}) { 158 | if m.Attributes == nil { 159 | m.Attributes = make(map[string]interface{}) 160 | } 161 | m.Attributes[name] = val 162 | } 163 | 164 | func MustConvertToMessage(in interface{}) (msg *Message) { 165 | m, err := ConvertToMessage(in) 166 | if err != nil { 167 | panic(err) 168 | } 169 | return m 170 | } 171 | func ConvertToMessage(in interface{}) (msg *Message, err error) { 172 | msg = &Message{} 173 | switch a := in.(type) { 174 | case Message: 175 | return &a, err 176 | case *Message: 177 | return a, err 178 | case amimodels.Action: 179 | msg.Type = MessageTypeAction 180 | msg.Name = a.ActionTypeName() 181 | case amimodels.Event: 182 | msg.Type = MessageTypeEvent 183 | msg.Name = a.EventTypeName() 184 | default: 185 | return nil, errors.Errorf("invalid type: %T", in) 186 | } 187 | m := make(map[string]interface{}) 188 | err = mapstructure.Decode(in, &m) 189 | // remove zero 190 | for k, v := range m { 191 | rm := v == nil 192 | 193 | // NOTE support require tag, prevent remove required empty value ? 194 | switch v := v.(type) { 195 | case string: 196 | rm = v == "" 197 | case int: 198 | rm = v == 0 199 | } 200 | if rm { 201 | delete(m, k) 202 | } 203 | } 204 | msg.Attributes = m 205 | return 206 | } 207 | -------------------------------------------------------------------------------- /ami/connect.go: -------------------------------------------------------------------------------- 1 | package ami 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "fmt" 7 | "net" 8 | "regexp" 9 | "sync/atomic" 10 | "time" 11 | 12 | "github.com/hashicorp/go-multierror" 13 | "github.com/pkg/errors" 14 | "github.com/wenerme/astgo/ami/amimodels" 15 | "go.uber.org/zap" 16 | "golang.org/x/sync/errgroup" 17 | ) 18 | 19 | // CustomDialer can be used to specify any dialer, not necessarily 20 | // a *net.Dialer. 21 | type CustomDialer interface { 22 | Dial(network, address string) (net.Conn, error) 23 | } 24 | 25 | type ConnErrHandler func(*Conn, error) 26 | type ConnHandler func(*Conn) 27 | 28 | type ConnectOptions struct { 29 | Context context.Context 30 | Timeout time.Duration 31 | AllowReconnect bool 32 | Username string // login username 33 | Secret string // login secret 34 | Logger *zap.Logger 35 | Dialer CustomDialer 36 | OnConnectErr ConnErrHandler 37 | OnConnected ConnHandler 38 | subscribers []struct { 39 | sub SubscribeFunc 40 | opts []SubscribeOption 41 | } 42 | } 43 | type ConnectOption func(c *ConnectOptions) error 44 | 45 | func WithAuth(username string, secret string) ConnectOption { 46 | return func(c *ConnectOptions) error { 47 | c.Username = username 48 | c.Secret = secret 49 | return nil 50 | } 51 | } 52 | 53 | func WithSubscribe(cb SubscribeFunc, opts ...SubscribeOption) ConnectOption { 54 | return func(c *ConnectOptions) error { 55 | c.subscribers = append(c.subscribers, struct { 56 | sub SubscribeFunc 57 | opts []SubscribeOption 58 | }{sub: cb, opts: opts}) 59 | return nil 60 | } 61 | } 62 | func Connect(addr string, opts ...ConnectOption) (conn *Conn, err error) { 63 | opt := &ConnectOptions{ 64 | Timeout: 10 * time.Second, 65 | Context: context.Background(), 66 | Logger: zap.L(), 67 | } 68 | 69 | for _, v := range opts { 70 | if err = v(opt); err != nil { 71 | return nil, err 72 | } 73 | } 74 | if opt.Dialer == nil { 75 | opt.Dialer = &net.Dialer{ 76 | Timeout: opt.Timeout, 77 | } 78 | } 79 | 80 | var id uint64 81 | conn = &Conn{ 82 | ctx: opt.Context, 83 | conf: opt, 84 | logger: opt.Logger, 85 | recv: make(chan *Message, 4096), 86 | pending: make(chan *asyncMsg, 100), 87 | nextID: func() string { 88 | return fmt.Sprint(atomic.AddUint64(&id, 1)) 89 | }, 90 | } 91 | for _, sub := range opt.subscribers { 92 | _, err = conn.Subscribe(sub.sub, sub.opts...) 93 | if err != nil { 94 | return nil, err 95 | } 96 | } 97 | return conn, conn.dial(addr) 98 | } 99 | 100 | func (c *Conn) Close() error { 101 | if c.closer != nil { 102 | c.closed = true 103 | c.closer() 104 | c.closer = nil 105 | err := c.g.Wait() 106 | if errors.Is(err, errClose) { 107 | return nil 108 | } 109 | return err 110 | } 111 | if c.closed { 112 | return nil 113 | } 114 | return errors.New("not init") 115 | } 116 | 117 | func (c *Conn) dial(addr string) (err error) { 118 | conf := c.conf 119 | 120 | if conf.AllowReconnect { 121 | // NOTE reconnect keep pending, but fail all async 122 | go func() { 123 | log := c.logger 124 | onErr := conf.OnConnectErr 125 | if onErr == nil { 126 | onErr = func(conn *Conn, err error) { 127 | } 128 | } 129 | 130 | var err error 131 | for !c.closed { 132 | err = c.dialOnce(addr) 133 | if err != nil { 134 | log.Sugar().With("err", err).Warn("ami.Conn: dial") 135 | 136 | onErr(c, err) 137 | // fixme improve wait strategy 138 | <-time.NewTimer(time.Second).C 139 | continue 140 | } 141 | if conf.OnConnected != nil { 142 | conf.OnConnected(c) 143 | log.Sugar().Info("ami.Conn: connected") 144 | } 145 | err = c.g.Wait() 146 | if err != nil { 147 | log.Sugar().With("err", err).Warn("ami.Conn: error") 148 | onErr(c, err) 149 | } 150 | c.g = nil 151 | } 152 | log.Sugar().Info("ami.Conn: stop reconnect, conn closed") 153 | }() 154 | return nil 155 | } 156 | return c.dialOnce(addr) 157 | } 158 | 159 | func (c *Conn) dialOnce(addr string) (err error) { 160 | conf := c.conf 161 | conn, err := conf.Dialer.Dial("tcp", addr) 162 | if err != nil { 163 | return err 164 | } 165 | defer func() { 166 | if err != nil { 167 | if e := conn.Close(); e != nil { 168 | err = multierror.Append(err, e) 169 | } 170 | } 171 | }() 172 | return c.connect(conn) 173 | } 174 | 175 | var errClose = errors.New("Close") 176 | 177 | func (c *Conn) connect(conn net.Conn) (err error) { 178 | log := c.logger 179 | r := bufio.NewReader(conn) 180 | c.reader = r 181 | c.conn = conn 182 | 183 | line, err := r.ReadString('\n') 184 | if err != nil { 185 | return errors.Wrap(err, "scan ami initial line") 186 | } 187 | 188 | // check connection 189 | match := regexp.MustCompile("Asterisk Call Manager/([0-9.]+)").FindStringSubmatch(line) 190 | if len(match) > 1 { 191 | c.version = match[1] 192 | log.Sugar().With("version", c.version).Debug("AMI Version") 193 | } else { 194 | err = errors.Errorf("Invalid server header: %q", line) 195 | return 196 | } 197 | 198 | ctx := c.ctx 199 | if ctx == nil { 200 | ctx = context.Background() 201 | } 202 | c.g, ctx = errgroup.WithContext(ctx) 203 | c.g.Go(func() error { 204 | return c.read(ctx) 205 | }) 206 | c.g.Go(func() error { 207 | return c.loop(ctx) 208 | }) 209 | // manual close 210 | waitCtx, closer := context.WithCancel(ctx) 211 | c.g.Go(func() error { 212 | <-ctx.Done() 213 | closer() 214 | return conn.Close() 215 | }) 216 | c.g.Go(func() error { 217 | <-waitCtx.Done() 218 | return errClose 219 | }) 220 | c.closer = closer 221 | 222 | conf := c.conf 223 | if conf.Username != "" { 224 | var resp *Message 225 | resp, err = c.Request(amimodels.LoginAction{ 226 | UserName: conf.Username, 227 | Secret: conf.Secret, 228 | }, RequestTimeout(2*time.Second)) 229 | 230 | if err != nil { 231 | err = errors.Wrap(err, "request login") 232 | } else if !resp.Success() { 233 | err = errors.Wrap(resp.Error(), "login") 234 | } 235 | if err != nil { 236 | log.Sugar().With("err", err).Info("login failed") 237 | return err 238 | } 239 | log.Info("login success") 240 | } 241 | 242 | //log.Sugar().Debug("do conn check ping") 243 | // be ready 244 | // may not FullyBooted 245 | // _, err = c.Request(amimodels.PingAction{}) 246 | return 247 | } 248 | -------------------------------------------------------------------------------- /pkg/tools/xmlgen/tmpl.go: -------------------------------------------------------------------------------- 1 | package xmlgen 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "embed" 7 | "fmt" 8 | "go/format" 9 | "log" 10 | "os" 11 | "path/filepath" 12 | "strings" 13 | "text/template" 14 | 15 | "github.com/Masterminds/goutils" 16 | "github.com/Masterminds/sprig" 17 | "github.com/pkg/errors" 18 | "golang.org/x/tools/imports" 19 | ) 20 | 21 | //go:embed template/*.tmpl template/**/*.tmpl 22 | var templateFS embed.FS 23 | 24 | func MustParseTemplates() *template.Template { 25 | //ext := pick(sprig.TxtFuncMap(), "ternary", "title") 26 | tpl := template.Must( 27 | template.New("gqlgen"). 28 | //Funcs(gen.Funcs). 29 | //Funcs(ext). 30 | Funcs(sprig.TxtFuncMap()). 31 | Funcs(map[string]interface{}{ 32 | "ParamName": ParamName, 33 | }). 34 | //Funcs(Funcs). 35 | ParseFS(templateFS, "template/*.tmpl", "template/**/*.tmpl"), 36 | ) 37 | //return &gen.Template{ 38 | // Template: tpl, 39 | // FuncMap: gen.Funcs, 40 | //} 41 | return tpl 42 | } 43 | 44 | func NewAMIGenerator() *Generator { 45 | return &Generator{ 46 | Template: MustParseTemplates(), 47 | Formatter: GoFormatter, 48 | Templates: []TemplateGenerate{ 49 | &GenerateTemplate{ 50 | Name: "ami/actions", 51 | FormatName: "actions.go", 52 | }, 53 | &GenerateTemplate{ 54 | Name: "ami/events", 55 | FormatName: "events.go", 56 | }, 57 | }, 58 | } 59 | } 60 | func NewAGIGenerator() *Generator { 61 | return &Generator{ 62 | Template: MustParseTemplates(), 63 | Formatter: GoFormatter, 64 | Templates: []TemplateGenerate{ 65 | //&GenerateTemplate{ 66 | // Name: "agi/commands", 67 | // FormatName: "commands.go", 68 | //}, 69 | &GenerateTemplate{ 70 | Name: "ami/actions", 71 | FormatName: "ami/amimodels/actions.go", 72 | }, 73 | &GenerateTemplate{ 74 | Name: "ami/events", 75 | FormatName: "ami/amimodels/events.go", 76 | }, 77 | }, 78 | } 79 | } 80 | 81 | type Generator struct { 82 | Files []File 83 | Template *template.Template 84 | Templates []TemplateGenerate 85 | Debug bool 86 | Formatter func(file *File) error 87 | Loader func() error 88 | } 89 | type File struct { 90 | Name string 91 | Content []byte 92 | } 93 | type WriteConfig struct { 94 | Target string 95 | DryRun bool 96 | } 97 | 98 | func (ge *Generator) Write(s WriteConfig) error { 99 | for _, v := range ge.Files { 100 | o := filepath.Join(s.Target, v.Name) 101 | // log.Println("write ", v.Name, " to ", o) 102 | if !s.DryRun { 103 | if err := os.WriteFile(o, v.Content, 0644); err != nil { 104 | return err 105 | } 106 | } 107 | if s.DryRun || ge.Debug { 108 | log.Println(o, "\n", strings.TrimSpace(string(v.Content))) 109 | } 110 | } 111 | return nil 112 | } 113 | 114 | func (ge *Generator) Generate(ctx context.Context, source interface{}) (err error) { 115 | if err = ge.init(); err != nil { 116 | return 117 | } 118 | var all []File 119 | for _, tmpl := range ge.Templates { 120 | var files []File 121 | files, err = tmpl.Generate(ctx, ge, source) 122 | if err != nil { 123 | return err 124 | } 125 | if ge.Formatter != nil { 126 | for i := range files { 127 | f := files[i] 128 | file := &files[i] 129 | if err = ge.Formatter(file); err != nil { 130 | if ge.Debug { 131 | log.Println(f.Name, err.Error(), "\n", lineNumber(string(f.Content))) 132 | } 133 | return errors.Wrapf(err, "format %v", file.Name) 134 | } 135 | } 136 | } 137 | all = append(all, files...) 138 | } 139 | ge.Files = append(ge.Files, all...) 140 | return 141 | } 142 | 143 | func (ge *Generator) init() error { 144 | if ge.Debug { 145 | log.Println("generating") 146 | } 147 | if ge.Loader != nil { 148 | if ge.Debug { 149 | log.Println("generator loading") 150 | } 151 | if err := ge.Loader(); err != nil { 152 | return err 153 | } 154 | } 155 | return nil 156 | } 157 | 158 | type TemplateGenerate interface { 159 | Generate(ctx context.Context, ge *Generator, source interface{}) ([]File, error) 160 | } 161 | type GenerateTemplate struct { 162 | Name string // template name. 163 | DeriveSource func(ctx context.Context, rootSource interface{}) interface{} 164 | Skip func(ctx context.Context, source interface{}) bool // skip condition (storage constraints or gated by a feature-flag). 165 | Format func(ctx context.Context, source interface{}) string // file name format. 166 | FormatName string // for single 167 | } 168 | 169 | func (tmpl *GenerateTemplate) Generate(ctx context.Context, ge *Generator, source interface{}) (files []File, err error) { 170 | if tmpl.DeriveSource != nil { 171 | source = tmpl.DeriveSource(ctx, source) 172 | } 173 | all, ok := source.([]interface{}) 174 | if !ok { 175 | all = []interface{}{source} 176 | } 177 | 178 | for _, source := range all { 179 | f, err := tmpl.one(ctx, ge, source) 180 | if err != nil { 181 | return nil, err 182 | } 183 | if f.Name != "" { 184 | files = append(files, f) 185 | } 186 | } 187 | return 188 | } 189 | func (tmpl *GenerateTemplate) one(ctx context.Context, ge *Generator, source interface{}) (file File, err error) { 190 | if tmpl.Skip != nil { 191 | if tmpl.Skip(ctx, source) { 192 | return 193 | } 194 | } 195 | 196 | b := bytes.NewBuffer(nil) 197 | if err = ge.Template.ExecuteTemplate(b, tmpl.Name, source); err != nil { 198 | return file, fmt.Errorf("execute template %q: %w", tmpl.Name, err) 199 | } 200 | 201 | fn := tmpl.FormatName 202 | if tmpl.Format != nil { 203 | fn = tmpl.Format(ctx, source) 204 | } 205 | file.Name = fn 206 | file.Content = b.Bytes() 207 | if ge.Debug { 208 | log.Printf("generate %s with %T to %s", tmpl.Name, source, fn) 209 | } 210 | return 211 | } 212 | 213 | func GoFormatter(f *File) (err error) { 214 | f.Content, err = format.Source(f.Content) 215 | if err == nil { 216 | f.Content, err = imports.Process(f.Name, f.Content, nil) 217 | } 218 | return 219 | } 220 | func ReportFile(f *File) { 221 | log.Println(f.Name, "\n", lineNumber(string(f.Content))) 222 | } 223 | func lineNumber(s string) string { 224 | lines := strings.Split(s, "\n") 225 | c := strings.Builder{} 226 | for i, v := range lines { 227 | c.WriteString(fmt.Sprintf("%v: ", i)) 228 | c.WriteString(v) 229 | c.WriteRune('\n') 230 | } 231 | return c.String() 232 | } 233 | 234 | func ParamName(s string) string { 235 | s = goutils.Uncapitalize(s) 236 | switch s { 237 | case "interface": 238 | s = "iface" 239 | } 240 | return s 241 | } 242 | -------------------------------------------------------------------------------- /astdb/_enum_gen_test.go: -------------------------------------------------------------------------------- 1 | package astdb_test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | "regexp" 8 | "sort" 9 | "strings" 10 | "testing" 11 | "text/template" 12 | 13 | "github.com/iancoleman/strcase" 14 | "github.com/stretchr/testify/assert" 15 | "github.com/wenerme/astgo/pkg/tools/xmlgen" 16 | ) 17 | 18 | func TestEnumGen(t *testing.T) { 19 | // from exported pg sql 20 | s := ` 21 | CREATE TYPE ast_bool_values AS ENUM ('0', '1', 'off', 'on', 'false', 'true', 'no', 'yes'); 22 | CREATE TYPE iax_encryption_values AS ENUM ('yes', 'no', 'aes128'); 23 | CREATE TYPE iax_requirecalltoken_values AS ENUM ('yes', 'no', 'auto'); 24 | CREATE TYPE iax_transfer_values AS ENUM ('yes', 'no', 'mediaonly'); 25 | CREATE TYPE moh_mode_values AS ENUM ('custom', 'files', 'mp3nb', 'quietmp3nb', 'quietmp3'); 26 | CREATE TYPE moh_mode_values AS ENUM ('custom', 'files', 'mp3nb', 'quietmp3nb', 'quietmp3', 'playlist'); 27 | CREATE TYPE pjsip_100rel_values AS ENUM ('no', 'required', 'yes'); 28 | CREATE TYPE pjsip_auth_type_values AS ENUM ('md5', 'userpass'); 29 | CREATE TYPE pjsip_auth_type_values_v2 AS ENUM ('md5', 'userpass', 'google_oauth'); 30 | CREATE TYPE pjsip_cid_privacy_values AS ENUM ('allowed_not_screened', 'allowed_passed_screened', 'allowed_failed_screened', 'allowed', 'prohib_not_screened', 'prohib_passed_screened', 'prohib_failed_screened', 'prohib', 'unavailable'); 31 | CREATE TYPE pjsip_connected_line_method_values AS ENUM ('invite', 'reinvite', 'update'); 32 | CREATE TYPE pjsip_direct_media_glare_mitigation_values AS ENUM ('none', 'outgoing', 'incoming'); 33 | CREATE TYPE pjsip_dtls_setup_values AS ENUM ('active', 'passive', 'actpass'); 34 | CREATE TYPE pjsip_dtmf_mode_values AS ENUM ('rfc4733', 'inband', 'info'); 35 | CREATE TYPE pjsip_dtmf_mode_values_v2 AS ENUM ('rfc4733', 'inband', 'info', 'auto'); 36 | CREATE TYPE pjsip_dtmf_mode_values_v3 AS ENUM ('rfc4733', 'inband', 'info', 'auto', 'auto_info'); 37 | CREATE TYPE pjsip_identify_by_values AS ENUM ('username'); 38 | CREATE TYPE pjsip_identify_by_values AS ENUM ('username', 'auth_username'); 39 | CREATE TYPE pjsip_identify_by_values AS ENUM ('username', 'auth_username', 'ip'); 40 | CREATE TYPE pjsip_media_encryption_values AS ENUM ('no', 'sdes', 'dtls'); 41 | CREATE TYPE pjsip_redirect_method_values AS ENUM ('user', 'uri_core', 'uri_pjsip'); 42 | CREATE TYPE pjsip_t38udptl_ec_values AS ENUM ('none', 'fec', 'redundancy'); 43 | CREATE TYPE pjsip_taskprocessor_overload_trigger_values AS ENUM ('none', 'global', 'pjsip_only'); 44 | CREATE TYPE pjsip_timer_values AS ENUM ('forced', 'no', 'required', 'yes'); 45 | CREATE TYPE pjsip_transport_method_values AS ENUM ('default', 'unspecified', 'tlsv1', 'sslv2', 'sslv3', 'sslv23'); 46 | CREATE TYPE pjsip_transport_protocol_values AS ENUM ('udp', 'tcp', 'tls', 'ws', 'wss'); 47 | CREATE TYPE pjsip_transport_protocol_values_v2 AS ENUM ('udp', 'tcp', 'tls', 'ws', 'wss', 'flow'); 48 | CREATE TYPE queue_autopause_values AS ENUM ('yes', 'no', 'all'); 49 | CREATE TYPE queue_strategy_values AS ENUM ('ringall', 'leastrecent', 'fewestcalls', 'random', 'rrmemory', 'linear', 'wrandom', 'rrordered'); 50 | CREATE TYPE sha_hash_values AS ENUM ('SHA-1', 'SHA-256'); 51 | CREATE TYPE sip_callingpres_values AS ENUM ('allowed_not_screened', 'allowed_passed_screen', 'allowed_failed_screen', 'allowed', 'prohib_not_screened', 'prohib_passed_screen', 'prohib_failed_screen', 'prohib'); 52 | CREATE TYPE sip_directmedia_values AS ENUM ('yes', 'no', 'nonat', 'update'); 53 | CREATE TYPE sip_directmedia_values_v2 AS ENUM ('yes', 'no', 'nonat', 'update', 'outgoing'); 54 | CREATE TYPE sip_dtmfmode_values AS ENUM ('rfc2833', 'info', 'shortinfo', 'inband', 'auto'); 55 | CREATE TYPE sip_progressinband_values AS ENUM ('yes', 'no', 'never'); 56 | CREATE TYPE sip_session_refresher_values AS ENUM ('uac', 'uas'); 57 | CREATE TYPE sip_session_timers_values AS ENUM ('accept', 'refuse', 'originate'); 58 | CREATE TYPE sip_transport_values AS ENUM ('udp', 'tcp', 'tls', 'ws', 'wss', 'udp,tcp', 'tcp,udp'); 59 | CREATE TYPE type_values AS ENUM ('friend', 'user', 'peer'); 60 | CREATE TYPE yes_no_values AS ENUM ('yes', 'no'); 61 | CREATE TYPE yesno_values AS ENUM ('yes', 'no'); 62 | ` 63 | 64 | s = strings.TrimSpace(s) 65 | lines := strings.Split(s, "\n") 66 | reg := regexp.MustCompile(`CREATE TYPE (\S+) AS ENUM\s\(([^)]+)`) 67 | enumDedup := map[string]*EnumModel{} 68 | for _, v := range lines { 69 | m := reg.FindStringSubmatch(v) 70 | if len(m) < 1 { 71 | fmt.Println("Failed Match", v) 72 | continue 73 | } 74 | d := &EnumModel{ 75 | Name: m[1], 76 | } 77 | for _, v := range strings.Split(strings.ReplaceAll(m[2], "'", ""), ",") { 78 | d.Values = append(d.Values, strings.TrimSpace(v)) 79 | } 80 | d.Name = strings.TrimSuffix(d.Name, "_values") 81 | d.Name = strings.TrimSuffix(d.Name, "_values_v2") 82 | d.Name = strings.TrimSuffix(d.Name, "_values_v3") 83 | switch d.Name { 84 | case "yesno": 85 | d.Name = "yes_no" 86 | case "type": 87 | d.Name = "sip_peer_type" 88 | } 89 | enumDedup[strings.ReplaceAll(d.Name, "_", "")] = d 90 | } 91 | var enums []*EnumModel 92 | for _, def := range enumDedup { 93 | def.Values = dedup(def.Values) 94 | enums = append(enums, def) 95 | } 96 | sort.Slice(enums, func(i, j int) bool { 97 | return enums[i].Name < enums[j].Name 98 | }) 99 | for _, d := range enums { 100 | fmt.Println("Enum", d.Name, d.Values) 101 | } 102 | strcase.ConfigureAcronym("TCP", "tcp") 103 | strcase.ConfigureAcronym("UDP", "udp") 104 | strcase.ConfigureAcronym("WS", "ws") 105 | strcase.ConfigureAcronym("WSS", "wss") 106 | strcase.ConfigureAcronym("TLS", "tls") 107 | strcase.ConfigureAcronym("SSL", "ttl") 108 | 109 | tpl := template.Must(template.New("enum").Parse(` 110 | 111 | 112 | {{define "enums"}} 113 | // Code generated by enums_test.go, DO NOT EDIT. 114 | 115 | package adb 116 | {{- range $_,$e := .Enums}} 117 | {{template "enum/type" $e}} 118 | {{- end}} 119 | 120 | {{- range $_,$e := .Enums}} 121 | {{template "enum/func" $e}} 122 | {{- end}} 123 | {{end}} 124 | 125 | {{define "enum/type"}} 126 | type {{.TypeName}} string 127 | {{$e := .}} 128 | const ( 129 | {{- range $_,$v := $.Values}} 130 | {{$.ValueName $v}} {{$e.TypeName}} = "{{$v}}" 131 | {{- end}} 132 | ) 133 | {{end}} 134 | 135 | {{define "enum/func"}} 136 | func (e {{.TypeName}}) String() string { 137 | return string(e) 138 | } 139 | func ({{.TypeName}}) Values() []string { 140 | return []string{ 141 | {{- range $_,$v := $.Values}} 142 | "{{$v}}", 143 | {{- end}} 144 | } 145 | } 146 | {{end}} 147 | `)) 148 | buffer := bytes.NewBuffer(nil) 149 | if assert.NoError(t, tpl.ExecuteTemplate(buffer, "enums", &EnumGenModel{ 150 | Enums: enums, 151 | })) { 152 | f := &xmlgen.File{Name: "inmem", Content: buffer.Bytes()} 153 | if !assert.NoError(t, xmlgen.GoFormatter(f)) { 154 | xmlgen.ReportFile(f) 155 | } else { 156 | assert.NoError(t, os.WriteFile("enums.go", f.Content, 0644)) 157 | } 158 | } 159 | } 160 | 161 | type EnumModel struct { 162 | Name string 163 | Values []string 164 | } 165 | 166 | func (e EnumModel) TypeName() string { 167 | return strcase.ToCamel(e.Name) 168 | } 169 | func (e EnumModel) ValueName(s string) string { 170 | return e.TypeName() + strcase.ToCamel(s) 171 | } 172 | 173 | type EnumGenModel struct { 174 | Enums []*EnumModel 175 | } 176 | 177 | func dedup(s []string) []string { 178 | m := map[string]bool{} 179 | for _, v := range s { 180 | m[v] = true 181 | } 182 | var o []string 183 | for k := range m { 184 | o = append(o, k) 185 | } 186 | sort.Strings(o) 187 | return o 188 | } 189 | -------------------------------------------------------------------------------- /agi/agi.go: -------------------------------------------------------------------------------- 1 | package agi 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "io" 7 | "net" 8 | "sort" 9 | "strconv" 10 | "strings" 11 | 12 | "github.com/pkg/errors" 13 | "github.com/wenerme/astgo/agi/agimodels" 14 | "go.uber.org/zap" 15 | ) 16 | 17 | const ( 18 | // StatusOK indicates operation was completed successfully. 19 | StatusOK = 200 20 | 21 | // StatusInvalid indicates invalid or unknown command. 22 | StatusInvalid = 510 23 | 24 | // StatusDeadChannel indicates the command cant be executed on a dead (closed, terminated, hung up) channel. 25 | StatusDeadChannel = 511 26 | 27 | // StatusEndUsage indicates end of proper usage, when the command returns its syntax. 28 | StatusEndUsage = 520 29 | ) 30 | 31 | type Request struct { 32 | } 33 | type Response struct { 34 | Error error // Error received, if any 35 | Status int // HTTP-style status code received 36 | Result int // Result is the numerical return (if parseable) 37 | ResultString string // Result value as a string 38 | Value string // Value is the (optional) string value returned 39 | } 40 | 41 | // Res returns the ResultString of a Response, as well as any error encountered. Depending on the command, this is sometimes more useful than Val() 42 | func (r *Response) Res() (string, error) { 43 | return r.ResultString, r.Error 44 | } 45 | 46 | // Err returns the error value from the response 47 | func (r *Response) Err() error { 48 | return r.Error 49 | } 50 | 51 | // Val returns the response value and error 52 | func (r *Response) Val() (string, error) { 53 | return r.Value, r.Error 54 | } 55 | 56 | type Session struct { 57 | Variables map[string]string 58 | r io.Reader 59 | w io.Writer 60 | ctx context.Context 61 | conn net.Conn 62 | } 63 | 64 | type HandlerFunc func(session *Session) 65 | 66 | func NewSession(c context.Context, r io.Reader, w io.Writer, conn net.Conn) (*Session, error) { 67 | session := &Session{ 68 | ctx: c, 69 | r: r, 70 | w: w, 71 | conn: conn, 72 | Variables: make(map[string]string), 73 | } 74 | return session, session.scanVariables() 75 | } 76 | func (a *Session) scanVariables() error { 77 | s := bufio.NewScanner(a.r) 78 | for s.Scan() { 79 | if s.Text() == "" { 80 | break 81 | } 82 | 83 | terms := strings.SplitN(s.Text(), ":", 2) 84 | if len(terms) == 2 { 85 | a.Variables[strings.TrimSpace(terms[0])] = strings.TrimSpace(terms[1]) 86 | } 87 | } 88 | return s.Err() 89 | } 90 | 91 | // Close closes any network connection associated with the AGI instance 92 | func (a *Session) Close() (err error) { 93 | if a.conn != nil { 94 | err = a.conn.Close() 95 | a.conn = nil 96 | } 97 | return 98 | } 99 | func ParseResponse(s string) *Response { 100 | sp := strings.SplitN(s, " ", 3) 101 | if len(sp) < 2 { 102 | return &Response{ 103 | Error: errors.Errorf("invalid response: %q", s), 104 | } 105 | } 106 | r := &Response{} 107 | r.Status, r.Error = strconv.Atoi(sp[0]) 108 | r.ResultString = sp[1][len("result="):] 109 | r.Result, _ = strconv.Atoi(r.ResultString) 110 | if len(sp) == 3 { 111 | r.Value = sp[2] 112 | } 113 | if r.Status != 200 { 114 | r.Error = errors.Errorf("status %v result %v extra %v", r.Status, r.ResultString, r.Value) 115 | } 116 | return r 117 | } 118 | func (a *Session) Command(cmd string) *Response { 119 | _, err := a.w.Write([]byte(cmd + "\n")) 120 | r := &Response{Error: err} 121 | if err != nil { 122 | return r 123 | } 124 | s := bufio.NewScanner(a.r) 125 | if s.Scan() { 126 | return ParseResponse(s.Text()) 127 | } 128 | return r 129 | } 130 | func (a *Session) Client() *agimodels.Client { 131 | return &agimodels.Client{ 132 | Handler: agimodels.HandlerFunc(func(cmd agimodels.Command) agimodels.Response { 133 | command, err := cmd.Command() 134 | if err != nil { 135 | return &Response{ 136 | Error: err, 137 | } 138 | } 139 | return a.Command(command) 140 | }), 141 | } 142 | } 143 | func (a *Session) RequestVariable() *RequestVariable { 144 | rc := &RequestVariable{} 145 | rc.Load(a.Variables) 146 | return rc 147 | } 148 | 149 | // Listen binds an AGI HandlerFunc to the given TCP `host:port` address, creating a FastAGI service. 150 | func Listen(addr string, handler HandlerFunc) error { 151 | if addr == "" { 152 | addr = "0.0.0.0:4573" 153 | } 154 | 155 | l, err := net.Listen("tcp", addr) 156 | if err != nil { 157 | return errors.Wrap(err, "failed to bind server") 158 | } 159 | defer l.Close() // nolint: errcheck 160 | 161 | for { 162 | conn, err := l.Accept() 163 | if err != nil { 164 | return errors.Wrap(err, "failed to accept TCP connection") 165 | } 166 | 167 | session, err := NewSession(nil, conn, conn, conn) 168 | if err != nil { 169 | return errors.Wrap(err, "failed init session") 170 | } 171 | go func() { 172 | defer session.Close() 173 | handler(session) 174 | }() 175 | } 176 | } 177 | 178 | type RequestVariable struct { 179 | Request string 180 | Channel string 181 | Language string 182 | Type string 183 | UniqueID string 184 | Version string 185 | CallerID string 186 | CallerIDName string 187 | CallingPres int // presentation of callerid 188 | CallingAni2 int 189 | CallingTon int // ast_channel_caller(chan)->id.number.plan 190 | CallingTns int // ast_channel_dialed(chan)->transit_network_select) 191 | DNID string // ast_channel_dialed(chan)->number.str 192 | RDNIS string // ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str 193 | Context string 194 | Extension string 195 | Priority int 196 | Enhanced bool 197 | AccountCode string 198 | ThreadID int 199 | Args []string 200 | } 201 | 202 | func (rc *RequestVariable) Load(m map[string]string) { 203 | var args []struct { 204 | i int 205 | v string 206 | } 207 | for k, v := range m { 208 | if !strings.HasPrefix(k, "agi_") { 209 | continue 210 | } 211 | name := k[4:] 212 | if strings.HasPrefix(name, "arg_") { 213 | i, err := strconv.Atoi(name[4:]) 214 | if err != nil { 215 | //return errors.Wrapf(err, "invalid arg %v", name) 216 | zap.S().With("arg", name).Warn("skip invalid request arg") 217 | } 218 | args = append(args, struct { 219 | i int 220 | v string 221 | }{i: i, v: v}) 222 | continue 223 | } 224 | if v == "unknown" { 225 | continue 226 | } 227 | var err error 228 | switch name { 229 | case "request": 230 | rc.Request = v 231 | case "channel": 232 | rc.Channel = v 233 | case "language": 234 | rc.Language = v 235 | case "type": 236 | rc.Type = v 237 | case "uniqueid": 238 | rc.UniqueID = v 239 | case "version": 240 | rc.Version = v 241 | case "callerid": 242 | rc.CallerID = v 243 | case "calleridname": 244 | rc.CallerIDName = v 245 | case "callingpres": 246 | rc.CallingPres, err = strconv.Atoi(v) 247 | case "callingani2": 248 | rc.CallingAni2, err = strconv.Atoi(v) 249 | case "callington": 250 | rc.CallingTon, err = strconv.Atoi(v) 251 | case "callingtns": 252 | rc.CallingTns, err = strconv.Atoi(v) 253 | case "dnid": 254 | rc.DNID = v 255 | case "rdnis": 256 | rc.RDNIS = v 257 | case "context": 258 | rc.Context = v 259 | case "extension": 260 | rc.Extension = v 261 | case "priority": 262 | rc.Priority, err = strconv.Atoi(v) 263 | case "enhanced": 264 | rc.Enhanced = v == "1.0" 265 | case "accountcode": 266 | rc.AccountCode = v 267 | case "threadid": 268 | rc.ThreadID, err = strconv.Atoi(v) 269 | } 270 | if err != nil { 271 | zap.S().With("err", err, "name", k, "value", v).Warn("failed to handle request variable") 272 | } 273 | } 274 | sort.Slice(args, func(i, j int) bool { 275 | return args[i].i < args[j].i 276 | }) 277 | for _, v := range args { 278 | rc.Args = append(rc.Args, v.v) 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /pkg/tools/xmlgen/doc_model.go: -------------------------------------------------------------------------------- 1 | package xmlgen 2 | 3 | //go:generate gomodifytags -file=doc_model.go -w -all -add-tags json -transform camelcase --skip-unexported -add-options json=omitempty 4 | 5 | import ( 6 | "encoding/xml" 7 | "fmt" 8 | "log" 9 | "regexp" 10 | "strings" 11 | 12 | "github.com/iancoleman/strcase" 13 | ) 14 | 15 | type AgiDocModel struct { 16 | Name string `xml:"name,attr" json:"name,omitempty"` 17 | Language string `xml:"language,attr" json:"language,omitempty"` 18 | Synopsis string `xml:"synopsis" json:"synopsis,omitempty"` 19 | Syntax *SyntaxDocModel `xml:"syntax" json:"syntax,omitempty"` 20 | SeeAlso *SeeAlsoModel `xml:"see-also" json:"seeAlso,omitempty"` 21 | Description *DescriptionModel `xml:"description" json:"description,omitempty"` 22 | } 23 | 24 | type ManagerResponseListElementModel struct { 25 | Text string `xml:",chardata" json:"text,omitempty"` 26 | ManagerEvent []*ManagerEventModel `xml:"managerEvent" json:"managerEvent,omitempty"` 27 | } 28 | type ManagerResponseModel struct { 29 | Text string `xml:",chardata" json:"text,omitempty"` 30 | ListElements *ManagerResponseListElementModel `xml:"list-elements" json:"listElements,omitempty"` 31 | ManagerEvent *ManagerEventModel `xml:"managerEvent" json:"managerEvent,omitempty"` 32 | } 33 | 34 | func (m ManagerResponseModel) Events() []*ManagerEventModel { 35 | var o []*ManagerEventModel 36 | if m.ManagerEvent != nil { 37 | o = append(o, m.ManagerEvent) 38 | } 39 | if m.ListElements != nil { 40 | o = append(o, m.ListElements.ManagerEvent...) 41 | } 42 | return o 43 | } 44 | 45 | type ManagerActionModel struct { 46 | Name string `xml:"name,attr" json:"name,omitempty"` 47 | Language string `xml:"language,attr" json:"language,omitempty"` 48 | Synopsis string `xml:"synopsis" json:"synopsis,omitempty"` 49 | Syntax *SyntaxDocModel `xml:"syntax" json:"syntax,omitempty"` 50 | SeeAlso *SeeAlsoModel `xml:"see-also" json:"seeAlso,omitempty"` 51 | Description *DescriptionModel `xml:"description" json:"description,omitempty"` 52 | Responses ManagerResponseModel `xml:"responses" json:"responses,omitempty"` 53 | } 54 | type ManagerEventInstanceModel struct { 55 | Text string `xml:",chardata"` 56 | Class string `xml:"class,attr"` 57 | Synopsis string `xml:"synopsis"` 58 | Language string `xml:"language,attr" json:"language,omitempty"` 59 | Syntax *SyntaxDocModel `xml:"syntax" json:"syntax,omitempty"` 60 | SeeAlso *SeeAlsoModel `xml:"see-also" json:"seeAlso,omitempty"` 61 | Description *DescriptionModel `xml:"description" json:"description,omitempty"` 62 | } 63 | type ManagerEventModel struct { 64 | Name string `xml:"name,attr" json:"name,omitempty"` 65 | Instance ManagerEventInstanceModel `xml:"managerEventInstance"` 66 | } 67 | 68 | type SyntaxDocModel struct { 69 | Parameter []SyntaxParameterDocModel `xml:"parameter" json:"parameter,omitempty"` 70 | ChannelSnapshot []struct { 71 | Text string `xml:",chardata"` 72 | Prefix string `xml:"prefix,attr"` 73 | } `xml:"channel_snapshot"` 74 | BridgeSnapshot []struct { 75 | Text string `xml:",chardata"` 76 | Prefix string `xml:"prefix,attr"` 77 | } `xml:"bridge_snapshot"` 78 | } 79 | type SyntaxParameterDocModel struct { 80 | Name string `xml:"name,attr" json:"name,omitempty"` 81 | Required bool `xml:"required,attr" json:"required,omitempty"` 82 | Para ParaRaw `xml:"para" json:"para,omitempty"` 83 | EnumList *EnumListModel `xml:"enumlist" json:"enumList,omitempty"` 84 | 85 | Note *struct { 86 | Text string `xml:",chardata"` 87 | Para struct { 88 | Text string `xml:",chardata"` 89 | Literal []string `xml:"literal"` 90 | Filename string `xml:"filename"` 91 | Replaceable string `xml:"replaceable"` 92 | } `xml:"para"` 93 | } `xml:"note"` 94 | } 95 | type DocModel struct { 96 | XMLName xml.Name `xml:"docs" json:"xmlName,omitempty"` 97 | Agi []*AgiDocModel `xml:"agi" json:"agi,omitempty"` 98 | Manager []*ManagerActionModel `xml:"manager" json:"manager,omitempty"` 99 | ManagerEvent []*ManagerEventModel `xml:"managerEvent" json:"managerEvent,omitempty"` 100 | } 101 | 102 | // ParaRaw contain ELEMENT astcli|literal|emphasis|filename|directory|replaceable|variable 103 | type ParaRaw struct { 104 | Data string `xml:",innerxml" json:"data,omitempty"` 105 | } 106 | type DescriptionModel struct { 107 | //Text string `xml:",chardata"` 108 | Para []ParaRaw `xml:"para" json:"para,omitempty"` 109 | EnumList *EnumListModel `xml:"enumlist" json:"enumList,omitempty"` 110 | VariableList *VariableListModel `xml:"variablelist" json:"variableList,omitempty"` 111 | Note *struct { 112 | Text string `xml:",chardata"` 113 | Para struct { 114 | Text string `xml:",chardata"` 115 | Filename string `xml:"filename"` 116 | } `xml:"para"` 117 | } `xml:"note"` 118 | } 119 | 120 | type VariableListVariableValueModel struct { 121 | Text string `xml:",chardata" json:"text,omitempty"` 122 | Name string `xml:"name,attr" json:"name,omitempty"` 123 | } 124 | type VariableListVariableModel struct { 125 | Name string `xml:"name,attr" json:"name,omitempty"` 126 | Para ParaRaw `xml:"para" json:"para,omitempty"` 127 | Value []VariableListVariableValueModel `xml:"value" json:"value,omitempty"` 128 | } 129 | type VariableListModel struct { 130 | Variable []VariableListVariableModel `xml:"variable" json:"variable,omitempty"` 131 | } 132 | type SeeAlsoModel struct { 133 | Ref []SeeAlsoRefModel `json:"ref,omitempty"` 134 | } 135 | type SeeAlsoRefModel struct { 136 | Text string `xml:",chardata" json:"text,omitempty"` 137 | Type string `xml:"type,attr" json:"type,omitempty"` 138 | } 139 | 140 | type EnumModel struct { 141 | //Text string `xml:",chardata"` 142 | Name string `xml:"name,attr" json:"name,omitempty"` 143 | Para ParaRaw `xml:"para" json:"para,omitempty"` 144 | } 145 | type EnumListModel struct { 146 | //Text string `xml:",chardata"` 147 | Enum []EnumModel `xml:"enum" json:"enum,omitempty"` 148 | } 149 | 150 | func (d *DocModel) Synopsis(in string) string { 151 | s := strings.TrimSpace(in) 152 | s = deindent(s) 153 | return s 154 | } 155 | func (d *DocModel) ManagerModel(in *ManagerActionModel) *ManagerAction { 156 | o := &ManagerAction{ 157 | Name: in.Name, 158 | Synopsis: d.Synopsis(in.Synopsis), 159 | Syntax: d.Syntax(in.Syntax), 160 | SeeAlso: d.SeeAlso(in.SeeAlso), 161 | Description: d.Description(in.Description), 162 | } 163 | resp := in.Responses.Events() 164 | 165 | for _, v := range resp { 166 | o.Responses = append(o.Responses, d.ManagerEventModel(v)) 167 | } 168 | 169 | if len(resp) > 1 { 170 | fmt.Println("Multi response", len(resp), o.Name) 171 | for _, v := range resp { 172 | fmt.Print(" ", v.Name) 173 | } 174 | fmt.Println() 175 | } 176 | return o 177 | } 178 | 179 | func (d *DocModel) ManagerEventModel(in *ManagerEventModel) *ManagerEvent { 180 | o := &ManagerEvent{ 181 | Name: in.Name, 182 | Synopsis: d.Synopsis(in.Instance.Synopsis), 183 | Syntax: d.Syntax(in.Instance.Syntax), 184 | SeeAlso: d.SeeAlso(in.Instance.SeeAlso), 185 | } 186 | return o 187 | } 188 | func (d *DocModel) Description(in *DescriptionModel) string { 189 | if in == nil { 190 | return "" 191 | } 192 | var p []string 193 | for _, v := range in.Para { 194 | p = append(p, v.Data) 195 | } 196 | // ignore enum,note 197 | return formatParas(p) 198 | } 199 | func (d *DocModel) AGIModel(in *AgiDocModel) *AGICommand { 200 | o := &AGICommand{ 201 | Name: in.Name, 202 | Synopsis: d.Synopsis(in.Synopsis), 203 | Syntax: d.Syntax(in.Syntax), 204 | SeeAlso: d.SeeAlso(in.SeeAlso), 205 | Description: d.Description(in.Description), 206 | } 207 | return o 208 | } 209 | func (d *DocModel) SeeAlso(in *SeeAlsoModel) (o []*SeeAlso) { 210 | if in == nil { 211 | return 212 | } 213 | for _, v := range in.Ref { 214 | o = append(o, &SeeAlso{ 215 | Name: v.Text, 216 | Type: v.Type, 217 | }) 218 | } 219 | return 220 | } 221 | func (d *DocModel) Syntax(in *SyntaxDocModel) *Syntax { 222 | o := &Syntax{} 223 | 224 | if in == nil { 225 | return o 226 | } 227 | for _, v := range in.Parameter { 228 | parameter := d.Parameter(&v) 229 | if parameter.Name == "" { 230 | log.Println("drop param", in) 231 | o.Missing = true 232 | continue 233 | } 234 | o.Params = append(o.Params, parameter) 235 | } 236 | return o 237 | } 238 | 239 | var regDefaultTo = regexp.MustCompile(`Defaults to\s*(.*?)`) 240 | 241 | func parseParaDefaultTo(s string) string { 242 | sub := regDefaultTo.FindStringSubmatch(s) 243 | if len(sub) > 1 { 244 | return sub[1] 245 | } 246 | return "" 247 | } 248 | 249 | var regIndent = regexp.MustCompile(`(?m)^\s+`) 250 | 251 | func deindent(s string) string { 252 | return regIndent.ReplaceAllString(s, "") 253 | } 254 | 255 | var paraTagReplace = strings.NewReplacer( 256 | "", "`", "", "`", 257 | "", "`", "", "`", 258 | "", "`", "", "`", 259 | "", " ", "", " ", 260 | "", " ", "", " ", 261 | "\n", " ", 262 | ) 263 | 264 | func formatParas(s []string) string { 265 | sb := strings.Builder{} 266 | for _, v := range s { 267 | sb.WriteString(formatPara(v)) 268 | sb.WriteString("\n\n") 269 | } 270 | return strings.TrimSpace(sb.String()) 271 | } 272 | func formatPara(s string) string { 273 | s = deindent(s) 274 | return paraTagReplace.Replace(s) 275 | } 276 | func (*DocModel) Parameter(in *SyntaxParameterDocModel) *Parameter { 277 | o := &Parameter{ 278 | RawName: in.Name, 279 | Required: in.Required, 280 | } 281 | para := strings.TrimSpace(in.Para.Data) 282 | if para != "" { 283 | o.Default = parseParaDefaultTo(para) 284 | if o.Default == "" { 285 | // description 286 | o.Description = formatPara(para) 287 | } 288 | } 289 | processParameter(o) 290 | return o 291 | } 292 | 293 | func processParameter(p *Parameter) { 294 | rawName := p.RawName 295 | var name string 296 | typ := "string" 297 | switch rawName { 298 | case "skipms": 299 | name = "SkipMS" 300 | case "offsetms": 301 | name = "OffsetMS" 302 | case "keytree": 303 | name = "KeyTree" 304 | } 305 | if name == "" { 306 | name = rawName 307 | // s=slicense 308 | name = strings.ReplaceAll(name, "=", "-") 309 | if len(name) > 4 { 310 | name = strings.ReplaceAll(name, "name", "Name") 311 | } 312 | // xstrings VariableName -> Variablename 313 | name = strcase.ToCamel(name) 314 | } 315 | switch name { 316 | case "SkipMS", "OffsetMS", "SampleOffset", "Priority", "Timeout": 317 | typ = "int" 318 | case "Time": // AutoHangup 319 | typ = "float64" 320 | case "": 321 | default: 322 | if name[0] >= '0' && name[0] <= '9' { 323 | name = "Field" + name 324 | } 325 | } 326 | 327 | p.Name = name 328 | p.Type = typ 329 | } 330 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /astdb/enums.go: -------------------------------------------------------------------------------- 1 | // Code generated by enums_test.go, DO NOT EDIT. 2 | 3 | package astdb 4 | 5 | type AstBool string 6 | 7 | const ( 8 | AstBool0 AstBool = "0" 9 | AstBool1 AstBool = "1" 10 | AstBoolFalse AstBool = "false" 11 | AstBoolNo AstBool = "no" 12 | AstBoolOff AstBool = "off" 13 | AstBoolOn AstBool = "on" 14 | AstBoolTrue AstBool = "true" 15 | AstBoolYes AstBool = "yes" 16 | ) 17 | 18 | type IaxEncryption string 19 | 20 | const ( 21 | IaxEncryptionAes128 IaxEncryption = "aes128" 22 | IaxEncryptionNo IaxEncryption = "no" 23 | IaxEncryptionYes IaxEncryption = "yes" 24 | ) 25 | 26 | type IaxRequirecalltoken string 27 | 28 | const ( 29 | IaxRequirecalltokenAuto IaxRequirecalltoken = "auto" 30 | IaxRequirecalltokenNo IaxRequirecalltoken = "no" 31 | IaxRequirecalltokenYes IaxRequirecalltoken = "yes" 32 | ) 33 | 34 | type IaxTransfer string 35 | 36 | const ( 37 | IaxTransferMediaonly IaxTransfer = "mediaonly" 38 | IaxTransferNo IaxTransfer = "no" 39 | IaxTransferYes IaxTransfer = "yes" 40 | ) 41 | 42 | type MohMode string 43 | 44 | const ( 45 | MohModeCustom MohMode = "custom" 46 | MohModeFiles MohMode = "files" 47 | MohModeMp3Nb MohMode = "mp3nb" 48 | MohModePlaylist MohMode = "playlist" 49 | MohModeQuietmp3 MohMode = "quietmp3" 50 | MohModeQuietmp3Nb MohMode = "quietmp3nb" 51 | ) 52 | 53 | type Pjsip100Rel string 54 | 55 | const ( 56 | Pjsip100RelNo Pjsip100Rel = "no" 57 | Pjsip100RelRequired Pjsip100Rel = "required" 58 | Pjsip100RelYes Pjsip100Rel = "yes" 59 | ) 60 | 61 | type PjsipAuthType string 62 | 63 | const ( 64 | PjsipAuthTypeGoogleOauth PjsipAuthType = "google_oauth" 65 | PjsipAuthTypeMd5 PjsipAuthType = "md5" 66 | PjsipAuthTypeUserpass PjsipAuthType = "userpass" 67 | ) 68 | 69 | type PjsipCidPrivacy string 70 | 71 | const ( 72 | PjsipCidPrivacyAllowed PjsipCidPrivacy = "allowed" 73 | PjsipCidPrivacyAllowedFailedScreened PjsipCidPrivacy = "allowed_failed_screened" 74 | PjsipCidPrivacyAllowedNotScreened PjsipCidPrivacy = "allowed_not_screened" 75 | PjsipCidPrivacyAllowedPassedScreened PjsipCidPrivacy = "allowed_passed_screened" 76 | PjsipCidPrivacyProhib PjsipCidPrivacy = "prohib" 77 | PjsipCidPrivacyProhibFailedScreened PjsipCidPrivacy = "prohib_failed_screened" 78 | PjsipCidPrivacyProhibNotScreened PjsipCidPrivacy = "prohib_not_screened" 79 | PjsipCidPrivacyProhibPassedScreened PjsipCidPrivacy = "prohib_passed_screened" 80 | PjsipCidPrivacyUnavailable PjsipCidPrivacy = "unavailable" 81 | ) 82 | 83 | type PjsipConnectedLineMethod string 84 | 85 | const ( 86 | PjsipConnectedLineMethodInvite PjsipConnectedLineMethod = "invite" 87 | PjsipConnectedLineMethodReinvite PjsipConnectedLineMethod = "reinvite" 88 | PjsipConnectedLineMethodUpdate PjsipConnectedLineMethod = "update" 89 | ) 90 | 91 | type PjsipDirectMediaGlareMitigation string 92 | 93 | const ( 94 | PjsipDirectMediaGlareMitigationIncoming PjsipDirectMediaGlareMitigation = "incoming" 95 | PjsipDirectMediaGlareMitigationNone PjsipDirectMediaGlareMitigation = "none" 96 | PjsipDirectMediaGlareMitigationOutgoing PjsipDirectMediaGlareMitigation = "outgoing" 97 | ) 98 | 99 | type PjsipDtlsSetup string 100 | 101 | const ( 102 | PjsipDtlsSetupActive PjsipDtlsSetup = "active" 103 | PjsipDtlsSetupActpass PjsipDtlsSetup = "actpass" 104 | PjsipDtlsSetupPassive PjsipDtlsSetup = "passive" 105 | ) 106 | 107 | type PjsipDtmfMode string 108 | 109 | const ( 110 | PjsipDtmfModeAuto PjsipDtmfMode = "auto" 111 | PjsipDtmfModeAutoInfo PjsipDtmfMode = "auto_info" 112 | PjsipDtmfModeInband PjsipDtmfMode = "inband" 113 | PjsipDtmfModeInfo PjsipDtmfMode = "info" 114 | PjsipDtmfModeRfc4733 PjsipDtmfMode = "rfc4733" 115 | ) 116 | 117 | type PjsipIdentifyBy string 118 | 119 | const ( 120 | PjsipIdentifyByAuthUsername PjsipIdentifyBy = "auth_username" 121 | PjsipIdentifyByIp PjsipIdentifyBy = "ip" 122 | PjsipIdentifyByUsername PjsipIdentifyBy = "username" 123 | ) 124 | 125 | type PjsipMediaEncryption string 126 | 127 | const ( 128 | PjsipMediaEncryptionDtls PjsipMediaEncryption = "dtls" 129 | PjsipMediaEncryptionNo PjsipMediaEncryption = "no" 130 | PjsipMediaEncryptionSdes PjsipMediaEncryption = "sdes" 131 | ) 132 | 133 | type PjsipRedirectMethod string 134 | 135 | const ( 136 | PjsipRedirectMethodUriCore PjsipRedirectMethod = "uri_core" 137 | PjsipRedirectMethodUriPjsip PjsipRedirectMethod = "uri_pjsip" 138 | PjsipRedirectMethodUser PjsipRedirectMethod = "user" 139 | ) 140 | 141 | type PjsipT38UdptlEc string 142 | 143 | const ( 144 | PjsipT38UdptlEcFec PjsipT38UdptlEc = "fec" 145 | PjsipT38UdptlEcNone PjsipT38UdptlEc = "none" 146 | PjsipT38UdptlEcRedundancy PjsipT38UdptlEc = "redundancy" 147 | ) 148 | 149 | type PjsipTaskprocessorOverloadTrigger string 150 | 151 | const ( 152 | PjsipTaskprocessorOverloadTriggerGlobal PjsipTaskprocessorOverloadTrigger = "global" 153 | PjsipTaskprocessorOverloadTriggerNone PjsipTaskprocessorOverloadTrigger = "none" 154 | PjsipTaskprocessorOverloadTriggerPjsipOnly PjsipTaskprocessorOverloadTrigger = "pjsip_only" 155 | ) 156 | 157 | type PjsipTimer string 158 | 159 | const ( 160 | PjsipTimerForced PjsipTimer = "forced" 161 | PjsipTimerNo PjsipTimer = "no" 162 | PjsipTimerRequired PjsipTimer = "required" 163 | PjsipTimerYes PjsipTimer = "yes" 164 | ) 165 | 166 | type PjsipTransportMethod string 167 | 168 | const ( 169 | PjsipTransportMethodDefault PjsipTransportMethod = "default" 170 | PjsipTransportMethodSslv2 PjsipTransportMethod = "sslv2" 171 | PjsipTransportMethodSslv23 PjsipTransportMethod = "sslv23" 172 | PjsipTransportMethodSslv3 PjsipTransportMethod = "sslv3" 173 | PjsipTransportMethodTlsv1 PjsipTransportMethod = "tlsv1" 174 | PjsipTransportMethodUnspecified PjsipTransportMethod = "unspecified" 175 | ) 176 | 177 | type PjsipTransportProtocol string 178 | 179 | const ( 180 | PjsipTransportProtocolFlow PjsipTransportProtocol = "flow" 181 | PjsipTransportProtocolTcp PjsipTransportProtocol = "tcp" 182 | PjsipTransportProtocolTls PjsipTransportProtocol = "tls" 183 | PjsipTransportProtocolUdp PjsipTransportProtocol = "udp" 184 | PjsipTransportProtocolWs PjsipTransportProtocol = "ws" 185 | PjsipTransportProtocolWss PjsipTransportProtocol = "wss" 186 | ) 187 | 188 | type QueueAutopause string 189 | 190 | const ( 191 | QueueAutopauseAll QueueAutopause = "all" 192 | QueueAutopauseNo QueueAutopause = "no" 193 | QueueAutopauseYes QueueAutopause = "yes" 194 | ) 195 | 196 | type QueueStrategy string 197 | 198 | const ( 199 | QueueStrategyFewestcalls QueueStrategy = "fewestcalls" 200 | QueueStrategyLeastrecent QueueStrategy = "leastrecent" 201 | QueueStrategyLinear QueueStrategy = "linear" 202 | QueueStrategyRandom QueueStrategy = "random" 203 | QueueStrategyRingall QueueStrategy = "ringall" 204 | QueueStrategyRrmemory QueueStrategy = "rrmemory" 205 | QueueStrategyRrordered QueueStrategy = "rrordered" 206 | QueueStrategyWrandom QueueStrategy = "wrandom" 207 | ) 208 | 209 | type ShaHash string 210 | 211 | const ( 212 | ShaHashSHA1 ShaHash = "SHA-1" 213 | ShaHashSHA256 ShaHash = "SHA-256" 214 | ) 215 | 216 | type SipCallingpres string 217 | 218 | const ( 219 | SipCallingpresAllowed SipCallingpres = "allowed" 220 | SipCallingpresAllowedFailedScreen SipCallingpres = "allowed_failed_screen" 221 | SipCallingpresAllowedNotScreened SipCallingpres = "allowed_not_screened" 222 | SipCallingpresAllowedPassedScreen SipCallingpres = "allowed_passed_screen" 223 | SipCallingpresProhib SipCallingpres = "prohib" 224 | SipCallingpresProhibFailedScreen SipCallingpres = "prohib_failed_screen" 225 | SipCallingpresProhibNotScreened SipCallingpres = "prohib_not_screened" 226 | SipCallingpresProhibPassedScreen SipCallingpres = "prohib_passed_screen" 227 | ) 228 | 229 | type SipDirectmedia string 230 | 231 | const ( 232 | SipDirectmediaNo SipDirectmedia = "no" 233 | SipDirectmediaNonat SipDirectmedia = "nonat" 234 | SipDirectmediaOutgoing SipDirectmedia = "outgoing" 235 | SipDirectmediaUpdate SipDirectmedia = "update" 236 | SipDirectmediaYes SipDirectmedia = "yes" 237 | ) 238 | 239 | type SipDtmfmode string 240 | 241 | const ( 242 | SipDtmfmodeAuto SipDtmfmode = "auto" 243 | SipDtmfmodeInband SipDtmfmode = "inband" 244 | SipDtmfmodeInfo SipDtmfmode = "info" 245 | SipDtmfmodeRfc2833 SipDtmfmode = "rfc2833" 246 | SipDtmfmodeShortinfo SipDtmfmode = "shortinfo" 247 | ) 248 | 249 | type SipPeerType string 250 | 251 | const ( 252 | SipPeerTypeFriend SipPeerType = "friend" 253 | SipPeerTypePeer SipPeerType = "peer" 254 | SipPeerTypeUser SipPeerType = "user" 255 | ) 256 | 257 | type SipProgressinband string 258 | 259 | const ( 260 | SipProgressinbandNever SipProgressinband = "never" 261 | SipProgressinbandNo SipProgressinband = "no" 262 | SipProgressinbandYes SipProgressinband = "yes" 263 | ) 264 | 265 | type SipSessionRefresher string 266 | 267 | const ( 268 | SipSessionRefresherUac SipSessionRefresher = "uac" 269 | SipSessionRefresherUas SipSessionRefresher = "uas" 270 | ) 271 | 272 | type SipSessionTimers string 273 | 274 | const ( 275 | SipSessionTimersAccept SipSessionTimers = "accept" 276 | SipSessionTimersOriginate SipSessionTimers = "originate" 277 | SipSessionTimersRefuse SipSessionTimers = "refuse" 278 | ) 279 | 280 | type SipTransport string 281 | 282 | const ( 283 | SipTransportTcp SipTransport = "tcp" 284 | SipTransportTls SipTransport = "tls" 285 | SipTransportUdp SipTransport = "udp" 286 | SipTransportWs SipTransport = "ws" 287 | SipTransportWss SipTransport = "wss" 288 | ) 289 | 290 | type YesNo string 291 | 292 | const ( 293 | YesNoNo YesNo = "no" 294 | YesNoYes YesNo = "yes" 295 | ) 296 | 297 | func (e AstBool) String() string { 298 | return string(e) 299 | } 300 | func (AstBool) Values() []string { 301 | return []string{ 302 | "0", 303 | "1", 304 | "false", 305 | "no", 306 | "off", 307 | "on", 308 | "true", 309 | "yes", 310 | } 311 | } 312 | 313 | func (e IaxEncryption) String() string { 314 | return string(e) 315 | } 316 | func (IaxEncryption) Values() []string { 317 | return []string{ 318 | "aes128", 319 | "no", 320 | "yes", 321 | } 322 | } 323 | 324 | func (e IaxRequirecalltoken) String() string { 325 | return string(e) 326 | } 327 | func (IaxRequirecalltoken) Values() []string { 328 | return []string{ 329 | "auto", 330 | "no", 331 | "yes", 332 | } 333 | } 334 | 335 | func (e IaxTransfer) String() string { 336 | return string(e) 337 | } 338 | func (IaxTransfer) Values() []string { 339 | return []string{ 340 | "mediaonly", 341 | "no", 342 | "yes", 343 | } 344 | } 345 | 346 | func (e MohMode) String() string { 347 | return string(e) 348 | } 349 | func (MohMode) Values() []string { 350 | return []string{ 351 | "custom", 352 | "files", 353 | "mp3nb", 354 | "playlist", 355 | "quietmp3", 356 | "quietmp3nb", 357 | } 358 | } 359 | 360 | func (e Pjsip100Rel) String() string { 361 | return string(e) 362 | } 363 | func (Pjsip100Rel) Values() []string { 364 | return []string{ 365 | "no", 366 | "required", 367 | "yes", 368 | } 369 | } 370 | 371 | func (e PjsipAuthType) String() string { 372 | return string(e) 373 | } 374 | func (PjsipAuthType) Values() []string { 375 | return []string{ 376 | "google_oauth", 377 | "md5", 378 | "userpass", 379 | } 380 | } 381 | 382 | func (e PjsipCidPrivacy) String() string { 383 | return string(e) 384 | } 385 | func (PjsipCidPrivacy) Values() []string { 386 | return []string{ 387 | "allowed", 388 | "allowed_failed_screened", 389 | "allowed_not_screened", 390 | "allowed_passed_screened", 391 | "prohib", 392 | "prohib_failed_screened", 393 | "prohib_not_screened", 394 | "prohib_passed_screened", 395 | "unavailable", 396 | } 397 | } 398 | 399 | func (e PjsipConnectedLineMethod) String() string { 400 | return string(e) 401 | } 402 | func (PjsipConnectedLineMethod) Values() []string { 403 | return []string{ 404 | "invite", 405 | "reinvite", 406 | "update", 407 | } 408 | } 409 | 410 | func (e PjsipDirectMediaGlareMitigation) String() string { 411 | return string(e) 412 | } 413 | func (PjsipDirectMediaGlareMitigation) Values() []string { 414 | return []string{ 415 | "incoming", 416 | "none", 417 | "outgoing", 418 | } 419 | } 420 | 421 | func (e PjsipDtlsSetup) String() string { 422 | return string(e) 423 | } 424 | func (PjsipDtlsSetup) Values() []string { 425 | return []string{ 426 | "active", 427 | "actpass", 428 | "passive", 429 | } 430 | } 431 | 432 | func (e PjsipDtmfMode) String() string { 433 | return string(e) 434 | } 435 | func (PjsipDtmfMode) Values() []string { 436 | return []string{ 437 | "auto", 438 | "auto_info", 439 | "inband", 440 | "info", 441 | "rfc4733", 442 | } 443 | } 444 | 445 | func (e PjsipIdentifyBy) String() string { 446 | return string(e) 447 | } 448 | func (PjsipIdentifyBy) Values() []string { 449 | return []string{ 450 | "auth_username", 451 | "ip", 452 | "username", 453 | } 454 | } 455 | 456 | func (e PjsipMediaEncryption) String() string { 457 | return string(e) 458 | } 459 | func (PjsipMediaEncryption) Values() []string { 460 | return []string{ 461 | "dtls", 462 | "no", 463 | "sdes", 464 | } 465 | } 466 | 467 | func (e PjsipRedirectMethod) String() string { 468 | return string(e) 469 | } 470 | func (PjsipRedirectMethod) Values() []string { 471 | return []string{ 472 | "uri_core", 473 | "uri_pjsip", 474 | "user", 475 | } 476 | } 477 | 478 | func (e PjsipT38UdptlEc) String() string { 479 | return string(e) 480 | } 481 | func (PjsipT38UdptlEc) Values() []string { 482 | return []string{ 483 | "fec", 484 | "none", 485 | "redundancy", 486 | } 487 | } 488 | 489 | func (e PjsipTaskprocessorOverloadTrigger) String() string { 490 | return string(e) 491 | } 492 | func (PjsipTaskprocessorOverloadTrigger) Values() []string { 493 | return []string{ 494 | "global", 495 | "none", 496 | "pjsip_only", 497 | } 498 | } 499 | 500 | func (e PjsipTimer) String() string { 501 | return string(e) 502 | } 503 | func (PjsipTimer) Values() []string { 504 | return []string{ 505 | "forced", 506 | "no", 507 | "required", 508 | "yes", 509 | } 510 | } 511 | 512 | func (e PjsipTransportMethod) String() string { 513 | return string(e) 514 | } 515 | func (PjsipTransportMethod) Values() []string { 516 | return []string{ 517 | "default", 518 | "sslv2", 519 | "sslv23", 520 | "sslv3", 521 | "tlsv1", 522 | "unspecified", 523 | } 524 | } 525 | 526 | func (e PjsipTransportProtocol) String() string { 527 | return string(e) 528 | } 529 | func (PjsipTransportProtocol) Values() []string { 530 | return []string{ 531 | "flow", 532 | "tcp", 533 | "tls", 534 | "udp", 535 | "ws", 536 | "wss", 537 | } 538 | } 539 | 540 | func (e QueueAutopause) String() string { 541 | return string(e) 542 | } 543 | func (QueueAutopause) Values() []string { 544 | return []string{ 545 | "all", 546 | "no", 547 | "yes", 548 | } 549 | } 550 | 551 | func (e QueueStrategy) String() string { 552 | return string(e) 553 | } 554 | func (QueueStrategy) Values() []string { 555 | return []string{ 556 | "fewestcalls", 557 | "leastrecent", 558 | "linear", 559 | "random", 560 | "ringall", 561 | "rrmemory", 562 | "rrordered", 563 | "wrandom", 564 | } 565 | } 566 | 567 | func (e ShaHash) String() string { 568 | return string(e) 569 | } 570 | func (ShaHash) Values() []string { 571 | return []string{ 572 | "SHA-1", 573 | "SHA-256", 574 | } 575 | } 576 | 577 | func (e SipCallingpres) String() string { 578 | return string(e) 579 | } 580 | func (SipCallingpres) Values() []string { 581 | return []string{ 582 | "allowed", 583 | "allowed_failed_screen", 584 | "allowed_not_screened", 585 | "allowed_passed_screen", 586 | "prohib", 587 | "prohib_failed_screen", 588 | "prohib_not_screened", 589 | "prohib_passed_screen", 590 | } 591 | } 592 | 593 | func (e SipDirectmedia) String() string { 594 | return string(e) 595 | } 596 | func (SipDirectmedia) Values() []string { 597 | return []string{ 598 | "no", 599 | "nonat", 600 | "outgoing", 601 | "update", 602 | "yes", 603 | } 604 | } 605 | 606 | func (e SipDtmfmode) String() string { 607 | return string(e) 608 | } 609 | func (SipDtmfmode) Values() []string { 610 | return []string{ 611 | "auto", 612 | "inband", 613 | "info", 614 | "rfc2833", 615 | "shortinfo", 616 | } 617 | } 618 | 619 | func (e SipPeerType) String() string { 620 | return string(e) 621 | } 622 | func (SipPeerType) Values() []string { 623 | return []string{ 624 | "friend", 625 | "peer", 626 | "user", 627 | } 628 | } 629 | 630 | func (e SipProgressinband) String() string { 631 | return string(e) 632 | } 633 | func (SipProgressinband) Values() []string { 634 | return []string{ 635 | "never", 636 | "no", 637 | "yes", 638 | } 639 | } 640 | 641 | func (e SipSessionRefresher) String() string { 642 | return string(e) 643 | } 644 | func (SipSessionRefresher) Values() []string { 645 | return []string{ 646 | "uac", 647 | "uas", 648 | } 649 | } 650 | 651 | func (e SipSessionTimers) String() string { 652 | return string(e) 653 | } 654 | func (SipSessionTimers) Values() []string { 655 | return []string{ 656 | "accept", 657 | "originate", 658 | "refuse", 659 | } 660 | } 661 | 662 | func (e SipTransport) String() string { 663 | return string(e) 664 | } 665 | func (SipTransport) Values() []string { 666 | return []string{ 667 | "tcp", 668 | "tls", 669 | "udp", 670 | "ws", 671 | "wss", 672 | } 673 | } 674 | 675 | func (e YesNo) String() string { 676 | return string(e) 677 | } 678 | func (YesNo) Values() []string { 679 | return []string{ 680 | "no", 681 | "yes", 682 | } 683 | } 684 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= 4 | github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= 5 | github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= 6 | github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= 7 | github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= 8 | github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= 9 | github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= 10 | github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= 11 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 13 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 14 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= 15 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= 16 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= 17 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= 18 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 19 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 20 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 21 | github.com/go-pg/pg v8.0.7+incompatible h1:ty/sXL1OZLo+47KK9N8llRcmbA9tZasqbQ/OO4ld53g= 22 | github.com/go-pg/pg v8.0.7+incompatible/go.mod h1:a2oXow+aFOrvwcKs3eIA0lNFmMilrxK2sOkB5NWe0vA= 23 | github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= 24 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 25 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= 26 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 27 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 28 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 29 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 30 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 31 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 32 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 33 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 34 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 35 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 36 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 37 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 38 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 39 | github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= 40 | github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 41 | github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= 42 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 43 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 44 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 45 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 46 | github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= 47 | github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 48 | github.com/iancoleman/strcase v0.1.3 h1:dJBk1m2/qjL1twPLf68JND55vvivMupZ4wIzE8CTdBw= 49 | github.com/iancoleman/strcase v0.1.3/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= 50 | github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= 51 | github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 52 | github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= 53 | github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= 54 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 55 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 56 | github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= 57 | github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 58 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 59 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 60 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 61 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 62 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 63 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 64 | github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= 65 | github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 66 | github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= 67 | github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= 68 | github.com/mitchellh/copystructure v1.1.2 h1:Th2TIvG1+6ma3e/0/bopBKohOTY7s4dA8V2q4EUcBJ0= 69 | github.com/mitchellh/copystructure v1.1.2/go.mod h1:EBArHfARyrSWO/+Wyr9zwEkc6XMFB9XyNgFNmRkZZU4= 70 | github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= 71 | github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 72 | github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= 73 | github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 74 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 75 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= 76 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 77 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 78 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 79 | github.com/onsi/ginkgo v1.15.2 h1:l77YT15o814C2qVL47NOyjV/6RbaP7kKdrvZnxQ3Org= 80 | github.com/onsi/ginkgo v1.15.2/go.mod h1:Dd6YFfwBW84ETqqtL0CPyPXillHgY6XhQH3uuCCTr/o= 81 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 82 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 83 | github.com/onsi/gomega v1.11.0 h1:+CqWgvj0OZycCaqclBD1pxKHAU+tOkHmQIWvDHq2aug= 84 | github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg= 85 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 86 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 87 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 88 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 89 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 90 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 91 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 92 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 93 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 94 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 95 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 96 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 97 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 98 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 99 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 100 | go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= 101 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 102 | go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= 103 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 104 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= 105 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 106 | go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= 107 | go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= 108 | golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 109 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 110 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 111 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 112 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 113 | golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 114 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 115 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 116 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= 117 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 118 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 119 | golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= 120 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 121 | golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 122 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 123 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 124 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 125 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 126 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 127 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 128 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 129 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 130 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U= 131 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 132 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 133 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 134 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= 135 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 136 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 137 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 138 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 139 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 140 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 141 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 142 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 143 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 144 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 145 | golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw= 146 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 147 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 148 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 149 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 150 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 151 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 152 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 153 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 154 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 155 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 156 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE= 157 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 158 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 159 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 160 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 161 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 162 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 163 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 164 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 165 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 166 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 167 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 168 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 169 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 170 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 171 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 172 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 173 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 174 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 175 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 176 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 177 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 178 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 179 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 180 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 181 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 182 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 183 | honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= 184 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 185 | mellium.im/sasl v0.2.1 h1:nspKSRg7/SyO0cRGY71OkfHab8tf9kCts6a6oTDut0w= 186 | mellium.im/sasl v0.2.1/go.mod h1:ROaEDLQNuf9vjKqE1SrAfnsobm2YKXT1gnN1uDp1PjQ= 187 | -------------------------------------------------------------------------------- /pkg/tools/xmlgen/model.go: -------------------------------------------------------------------------------- 1 | package xmlgen 2 | 3 | import "encoding/xml" 4 | 5 | type Docs struct { 6 | XMLName xml.Name `xml:"docs"` 7 | Text string `xml:",chardata"` 8 | Xi string `xml:"xi,attr"` 9 | ConfigInfo []struct { 10 | Text string `xml:",chardata"` 11 | Name string `xml:"name,attr"` 12 | Language string `xml:"language,attr"` 13 | Synopsis string `xml:"synopsis"` 14 | Description struct { 15 | Text string `xml:",chardata"` 16 | Para []struct { 17 | Text string `xml:",chardata"` 18 | Emphasis []string `xml:"emphasis"` 19 | Literal []string `xml:"literal"` 20 | Filename string `xml:"filename"` 21 | } `xml:"para"` 22 | Warning struct { 23 | Text string `xml:",chardata"` 24 | Para struct { 25 | Text string `xml:",chardata"` 26 | Emphasis string `xml:"emphasis"` 27 | } `xml:"para"` 28 | } `xml:"warning"` 29 | Note struct { 30 | Text string `xml:",chardata"` 31 | Para string `xml:"para"` 32 | } `xml:"note"` 33 | Enumlist []struct { 34 | Text string `xml:",chardata"` 35 | Enum []struct { 36 | Text string `xml:",chardata"` 37 | Name string `xml:"name,attr"` 38 | Para string `xml:"para"` 39 | } `xml:"enum"` 40 | } `xml:"enumlist"` 41 | } `xml:"description"` 42 | ConfigFile struct { 43 | Text string `xml:",chardata"` 44 | Name string `xml:"name,attr"` 45 | ConfigObject []struct { 46 | Text string `xml:",chardata"` 47 | Name string `xml:"name,attr"` 48 | Synopsis string `xml:"synopsis"` 49 | ConfigOption []struct { 50 | Text string `xml:",chardata"` 51 | Name string `xml:"name,attr"` 52 | Default string `xml:"default,attr"` 53 | Synopsis string `xml:"synopsis"` 54 | Description struct { 55 | Text string `xml:",chardata"` 56 | Para []struct { 57 | Text string `xml:",chardata"` 58 | Literal []string `xml:"literal"` 59 | Filename []string `xml:"filename"` 60 | Replaceable []string `xml:"replaceable"` 61 | Variable string `xml:"variable"` 62 | Emphasis []string `xml:"emphasis"` 63 | Astcli string `xml:"astcli"` 64 | } `xml:"para"` 65 | Enumlist struct { 66 | Text string `xml:",chardata"` 67 | Enum []struct { 68 | Text string `xml:",chardata"` 69 | Name string `xml:"name,attr"` 70 | Para []struct { 71 | Text string `xml:",chardata"` 72 | Literal []string `xml:"literal"` 73 | Replaceable string `xml:"replaceable"` 74 | Emphasis string `xml:"emphasis"` 75 | } `xml:"para"` 76 | Enumlist struct { 77 | Text string `xml:",chardata"` 78 | Enum []struct { 79 | Text string `xml:",chardata"` 80 | Name string `xml:"name,attr"` 81 | Para string `xml:"para"` 82 | } `xml:"enum"` 83 | } `xml:"enumlist"` 84 | Note struct { 85 | Text string `xml:",chardata"` 86 | Para struct { 87 | Text string `xml:",chardata"` 88 | Literal string `xml:"literal"` 89 | } `xml:"para"` 90 | } `xml:"note"` 91 | } `xml:"enum"` 92 | } `xml:"enumlist"` 93 | Note []struct { 94 | Text string `xml:",chardata"` 95 | Para []struct { 96 | Text string `xml:",chardata"` 97 | Variable string `xml:"variable"` 98 | Literal []string `xml:"literal"` 99 | Replaceable []string `xml:"replaceable"` 100 | Filename string `xml:"filename"` 101 | } `xml:"para"` 102 | } `xml:"note"` 103 | Include struct { 104 | Text string `xml:",chardata"` 105 | Xpointer string `xml:"xpointer,attr"` 106 | } `xml:"include"` 107 | Warning struct { 108 | Text string `xml:",chardata"` 109 | Para struct { 110 | Text string `xml:",chardata"` 111 | Literal string `xml:"literal"` 112 | } `xml:"para"` 113 | } `xml:"warning"` 114 | Variablelist struct { 115 | Text string `xml:",chardata"` 116 | Variable []struct { 117 | Text string `xml:",chardata"` 118 | Name string `xml:"name,attr"` 119 | Para struct { 120 | Text string `xml:",chardata"` 121 | Variable string `xml:"variable"` 122 | } `xml:"para"` 123 | } `xml:"variable"` 124 | } `xml:"variablelist"` 125 | Example []struct { 126 | Text string `xml:",chardata"` 127 | Title string `xml:"title,attr"` 128 | } `xml:"example"` 129 | } `xml:"description"` 130 | SeeAlso struct { 131 | Text string `xml:",chardata"` 132 | Ref []struct { 133 | Text string `xml:",chardata"` 134 | Type string `xml:"type,attr"` 135 | } `xml:"ref"` 136 | } `xml:"see-also"` 137 | Syntax struct { 138 | Text string `xml:",chardata"` 139 | Argsep string `xml:"argsep,attr"` 140 | Parameter []struct { 141 | Text string `xml:",chardata"` 142 | Name string `xml:"name,attr"` 143 | Required string `xml:"required,attr"` 144 | Para string `xml:"para"` 145 | Optionlist struct { 146 | Text string `xml:",chardata"` 147 | Option []struct { 148 | Text string `xml:",chardata"` 149 | Name string `xml:"name,attr"` 150 | Para string `xml:"para"` 151 | } `xml:"option"` 152 | } `xml:"optionlist"` 153 | Argument struct { 154 | Text string `xml:",chardata"` 155 | Name string `xml:"name,attr"` 156 | Required string `xml:"required,attr"` 157 | Para string `xml:"para"` 158 | } `xml:"argument"` 159 | } `xml:"parameter"` 160 | } `xml:"syntax"` 161 | } `xml:"configOption"` 162 | Description struct { 163 | Text string `xml:",chardata"` 164 | Include struct { 165 | Text string `xml:",chardata"` 166 | Xpointer string `xml:"xpointer,attr"` 167 | } `xml:"include"` 168 | Para []struct { 169 | Text string `xml:",chardata"` 170 | Literal []string `xml:"literal"` 171 | Replaceable []string `xml:"replaceable"` 172 | Emphasis []string `xml:"emphasis"` 173 | Filename string `xml:"filename"` 174 | } `xml:"para"` 175 | Enumlist struct { 176 | Text string `xml:",chardata"` 177 | Enum []struct { 178 | Text string `xml:",chardata"` 179 | Name string `xml:"name,attr"` 180 | Para string `xml:"para"` 181 | } `xml:"enum"` 182 | } `xml:"enumlist"` 183 | Note struct { 184 | Text string `xml:",chardata"` 185 | Para struct { 186 | Text string `xml:",chardata"` 187 | Emphasis string `xml:"emphasis"` 188 | Filename string `xml:"filename"` 189 | } `xml:"para"` 190 | } `xml:"note"` 191 | } `xml:"description"` 192 | } `xml:"configObject"` 193 | } `xml:"configFile"` 194 | } `xml:"configInfo"` 195 | Manager []struct { 196 | Text string `xml:",chardata"` 197 | Name string `xml:"name,attr"` 198 | Language string `xml:"language,attr"` 199 | Module string `xml:"module,attr"` 200 | Synopsis string `xml:"synopsis"` 201 | Syntax struct { 202 | Text string `xml:",chardata"` 203 | Include []struct { 204 | Text string `xml:",chardata"` 205 | Xpointer string `xml:"xpointer,attr"` 206 | } `xml:"include"` 207 | Parameter []struct { 208 | Text string `xml:",chardata"` 209 | Name string `xml:"name,attr"` 210 | Required string `xml:"required,attr"` 211 | Default string `xml:"default,attr"` 212 | Para []struct { 213 | Text string `xml:",chardata"` 214 | Replaceable []string `xml:"replaceable"` 215 | Literal []string `xml:"literal"` 216 | Variable string `xml:"variable"` 217 | Filename string `xml:"filename"` 218 | } `xml:"para"` 219 | Enumlist []struct { 220 | Text string `xml:",chardata"` 221 | Enum []struct { 222 | Text string `xml:",chardata"` 223 | Name string `xml:"name,attr"` 224 | Para struct { 225 | Text string `xml:",chardata"` 226 | Replaceable string `xml:"replaceable"` 227 | } `xml:"para"` 228 | Note struct { 229 | Text string `xml:",chardata"` 230 | Para struct { 231 | Text string `xml:",chardata"` 232 | Literal string `xml:"literal"` 233 | } `xml:"para"` 234 | } `xml:"note"` 235 | Enumlist struct { 236 | Text string `xml:",chardata"` 237 | Enum []struct { 238 | Text string `xml:",chardata"` 239 | Name string `xml:"name,attr"` 240 | Para []struct { 241 | Text string `xml:",chardata"` 242 | Literal string `xml:"literal"` 243 | } `xml:"para"` 244 | Include struct { 245 | Text string `xml:",chardata"` 246 | Xpointer string `xml:"xpointer,attr"` 247 | } `xml:"include"` 248 | } `xml:"enum"` 249 | } `xml:"enumlist"` 250 | } `xml:"enum"` 251 | } `xml:"enumlist"` 252 | Warning struct { 253 | Text string `xml:",chardata"` 254 | Para struct { 255 | Text string `xml:",chardata"` 256 | Variable []string `xml:"variable"` 257 | } `xml:"para"` 258 | } `xml:"warning"` 259 | Include struct { 260 | Text string `xml:",chardata"` 261 | Xpointer string `xml:"xpointer,attr"` 262 | } `xml:"include"` 263 | } `xml:"parameter"` 264 | } `xml:"syntax"` 265 | Description struct { 266 | Text string `xml:",chardata"` 267 | Para []struct { 268 | Text string `xml:",chardata"` 269 | Variable string `xml:"variable"` 270 | Literal []string `xml:"literal"` 271 | Replaceable []string `xml:"replaceable"` 272 | } `xml:"para"` 273 | Note struct { 274 | Text string `xml:",chardata"` 275 | Para struct { 276 | Text string `xml:",chardata"` 277 | Literal []string `xml:"literal"` 278 | Replaceable []string `xml:"replaceable"` 279 | } `xml:"para"` 280 | } `xml:"note"` 281 | Variablelist struct { 282 | Text string `xml:",chardata"` 283 | Variable struct { 284 | Text string `xml:",chardata"` 285 | Name string `xml:"name,attr"` 286 | Para string `xml:"para"` 287 | } `xml:"variable"` 288 | } `xml:"variablelist"` 289 | Warning struct { 290 | Text string `xml:",chardata"` 291 | Para struct { 292 | Text string `xml:",chardata"` 293 | Literal string `xml:"literal"` 294 | } `xml:"para"` 295 | } `xml:"warning"` 296 | } `xml:"description"` 297 | SeeAlso struct { 298 | Text string `xml:",chardata"` 299 | Ref []struct { 300 | Text string `xml:",chardata"` 301 | Type string `xml:"type,attr"` 302 | Module string `xml:"module,attr"` 303 | } `xml:"ref"` 304 | } `xml:"see-also"` 305 | Responses struct { 306 | Text string `xml:",chardata"` 307 | ListElements struct { 308 | Text string `xml:",chardata"` 309 | Include []struct { 310 | Text string `xml:",chardata"` 311 | Xpointer string `xml:"xpointer,attr"` 312 | } `xml:"include"` 313 | ManagerEvent struct { 314 | Text string `xml:",chardata"` 315 | Language string `xml:"language,attr"` 316 | Name string `xml:"name,attr"` 317 | ManagerEventInstance struct { 318 | Text string `xml:",chardata"` 319 | Class string `xml:"class,attr"` 320 | Synopsis string `xml:"synopsis"` 321 | Syntax struct { 322 | Text string `xml:",chardata"` 323 | ChannelSnapshot string `xml:"channel_snapshot"` 324 | } `xml:"syntax"` 325 | } `xml:"managerEventInstance"` 326 | } `xml:"managerEvent"` 327 | } `xml:"list-elements"` 328 | Include struct { 329 | Text string `xml:",chardata"` 330 | Xpointer string `xml:"xpointer,attr"` 331 | } `xml:"include"` 332 | ManagerEvent struct { 333 | Text string `xml:",chardata"` 334 | Name string `xml:"name,attr"` 335 | Language string `xml:"language,attr"` 336 | ManagerEventInstance struct { 337 | Text string `xml:",chardata"` 338 | Class string `xml:"class,attr"` 339 | Synopsis string `xml:"synopsis"` 340 | Syntax struct { 341 | Text string `xml:",chardata"` 342 | Parameter []struct { 343 | Text string `xml:",chardata"` 344 | Name string `xml:"name,attr"` 345 | Para string `xml:"para"` 346 | } `xml:"parameter"` 347 | BridgeSnapshot string `xml:"bridge_snapshot"` 348 | } `xml:"syntax"` 349 | } `xml:"managerEventInstance"` 350 | } `xml:"managerEvent"` 351 | } `xml:"responses"` 352 | } `xml:"manager"` 353 | Function []struct { 354 | Text string `xml:",chardata"` 355 | Name string `xml:"name,attr"` 356 | Language string `xml:"language,attr"` 357 | Module string `xml:"module,attr"` 358 | Synopsis string `xml:"synopsis"` 359 | Syntax struct { 360 | Text string `xml:",chardata"` 361 | Argsep string `xml:"argsep,attr"` 362 | Parameter []struct { 363 | Text string `xml:",chardata"` 364 | Name string `xml:"name,attr"` 365 | Required string `xml:"required,attr"` 366 | Argsep string `xml:"argsep,attr"` 367 | Multiple string `xml:"multiple,attr"` 368 | Default string `xml:"default,attr"` 369 | Para []struct { 370 | Text string `xml:",chardata"` 371 | Literal []string `xml:"literal"` 372 | Replaceable []string `xml:"replaceable"` 373 | Filename string `xml:"filename"` 374 | Emphasis string `xml:"emphasis"` 375 | } `xml:"para"` 376 | Enumlist struct { 377 | Text string `xml:",chardata"` 378 | Enum []struct { 379 | Text string `xml:",chardata"` 380 | Name string `xml:"name,attr"` 381 | Para []struct { 382 | Text string `xml:",chardata"` 383 | Replaceable []string `xml:"replaceable"` 384 | Literal []string `xml:"literal"` 385 | Include struct { 386 | Text string `xml:",chardata"` 387 | Xpointer string `xml:"xpointer,attr"` 388 | } `xml:"include"` 389 | Filename string `xml:"filename"` 390 | Emphasis []string `xml:"emphasis"` 391 | } `xml:"para"` 392 | Enumlist struct { 393 | Text string `xml:",chardata"` 394 | Enum []struct { 395 | Text string `xml:",chardata"` 396 | Name string `xml:"name,attr"` 397 | Para struct { 398 | Text string `xml:",chardata"` 399 | Literal string `xml:"literal"` 400 | } `xml:"para"` 401 | } `xml:"enum"` 402 | } `xml:"enumlist"` 403 | Warning struct { 404 | Text string `xml:",chardata"` 405 | Para string `xml:"para"` 406 | } `xml:"warning"` 407 | Note struct { 408 | Text string `xml:",chardata"` 409 | Para string `xml:"para"` 410 | } `xml:"note"` 411 | } `xml:"enum"` 412 | ConfigOptionToEnum struct { 413 | Text string `xml:",chardata"` 414 | Include struct { 415 | Text string `xml:",chardata"` 416 | Xpointer string `xml:"xpointer,attr"` 417 | } `xml:"include"` 418 | } `xml:"configOptionToEnum"` 419 | } `xml:"enumlist"` 420 | Optionlist struct { 421 | Text string `xml:",chardata"` 422 | Option []struct { 423 | Text string `xml:",chardata"` 424 | Name string `xml:"name,attr"` 425 | Para []struct { 426 | Text string `xml:",chardata"` 427 | Literal []string `xml:"literal"` 428 | Replaceable []string `xml:"replaceable"` 429 | } `xml:"para"` 430 | Argument []struct { 431 | Text string `xml:",chardata"` 432 | Name string `xml:"name,attr"` 433 | Required string `xml:"required,attr"` 434 | Para string `xml:"para"` 435 | } `xml:"argument"` 436 | } `xml:"option"` 437 | } `xml:"optionlist"` 438 | Argument []struct { 439 | Text string `xml:",chardata"` 440 | Name string `xml:"name,attr"` 441 | Required string `xml:"required,attr"` 442 | } `xml:"argument"` 443 | Include struct { 444 | Text string `xml:",chardata"` 445 | Xpointer string `xml:"xpointer,attr"` 446 | } `xml:"include"` 447 | Warning struct { 448 | Text string `xml:",chardata"` 449 | Para struct { 450 | Text string `xml:",chardata"` 451 | Variable []string `xml:"variable"` 452 | } `xml:"para"` 453 | } `xml:"warning"` 454 | Note struct { 455 | Text string `xml:",chardata"` 456 | Para string `xml:"para"` 457 | } `xml:"note"` 458 | } `xml:"parameter"` 459 | } `xml:"syntax"` 460 | Description struct { 461 | Text string `xml:",chardata"` 462 | Para []struct { 463 | Text string `xml:",chardata"` 464 | Replaceable []string `xml:"replaceable"` 465 | Literal []string `xml:"literal"` 466 | Variable []string `xml:"variable"` 467 | Filename []string `xml:"filename"` 468 | Emphasis string `xml:"emphasis"` 469 | } `xml:"para"` 470 | Warning struct { 471 | Text string `xml:",chardata"` 472 | Para struct { 473 | Text string `xml:",chardata"` 474 | Emphasis []string `xml:"emphasis"` 475 | Literal string `xml:"literal"` 476 | } `xml:"para"` 477 | } `xml:"warning"` 478 | Example []struct { 479 | Text string `xml:",chardata"` 480 | Title string `xml:"title,attr"` 481 | Language string `xml:"language,attr"` 482 | } `xml:"example"` 483 | Note []struct { 484 | Text string `xml:",chardata"` 485 | Para []struct { 486 | Text string `xml:",chardata"` 487 | Literal []string `xml:"literal"` 488 | Replaceable []string `xml:"replaceable"` 489 | Emphasis []string `xml:"emphasis"` 490 | } `xml:"para"` 491 | } `xml:"note"` 492 | Enumlist []struct { 493 | Text string `xml:",chardata"` 494 | Enum []struct { 495 | Text string `xml:",chardata"` 496 | Name string `xml:"name,attr"` 497 | Para string `xml:"para"` 498 | } `xml:"enum"` 499 | } `xml:"enumlist"` 500 | Include struct { 501 | Text string `xml:",chardata"` 502 | Xpointer string `xml:"xpointer,attr"` 503 | } `xml:"include"` 504 | Variablelist struct { 505 | Text string `xml:",chardata"` 506 | Variable struct { 507 | Text string `xml:",chardata"` 508 | Name string `xml:"name,attr"` 509 | Value []struct { 510 | Text string `xml:",chardata"` 511 | Name string `xml:"name,attr"` 512 | } `xml:"value"` 513 | Para string `xml:"para"` 514 | } `xml:"variable"` 515 | } `xml:"variablelist"` 516 | } `xml:"description"` 517 | SeeAlso struct { 518 | Text string `xml:",chardata"` 519 | Ref []struct { 520 | Text string `xml:",chardata"` 521 | Type string `xml:"type,attr"` 522 | Module string `xml:"module,attr"` 523 | } `xml:"ref"` 524 | } `xml:"see-also"` 525 | } `xml:"function"` 526 | Info []struct { 527 | Text string `xml:",chardata"` 528 | Name string `xml:"name,attr"` 529 | Language string `xml:"language,attr"` 530 | Tech string `xml:"tech,attr"` 531 | Enumlist struct { 532 | Text string `xml:",chardata"` 533 | Enum []struct { 534 | Text string `xml:",chardata"` 535 | Name string `xml:"name,attr"` 536 | Para []struct { 537 | Text string `xml:",chardata"` 538 | Replaceable string `xml:"replaceable"` 539 | Literal []string `xml:"literal"` 540 | } `xml:"para"` 541 | Parameter []struct { 542 | Text string `xml:",chardata"` 543 | Name string `xml:"name,attr"` 544 | Required string `xml:"required,attr"` 545 | Para struct { 546 | Text string `xml:",chardata"` 547 | Replaceable string `xml:"replaceable"` 548 | Literal string `xml:"literal"` 549 | } `xml:"para"` 550 | Enumlist struct { 551 | Text string `xml:",chardata"` 552 | Enum []struct { 553 | Text string `xml:",chardata"` 554 | Name string `xml:"name,attr"` 555 | Para []struct { 556 | Text string `xml:",chardata"` 557 | Literal []string `xml:"literal"` 558 | } `xml:"para"` 559 | Enumlist struct { 560 | Text string `xml:",chardata"` 561 | Enum []struct { 562 | Text string `xml:",chardata"` 563 | Name string `xml:"name,attr"` 564 | Para struct { 565 | Text string `xml:",chardata"` 566 | Literal string `xml:"literal"` 567 | } `xml:"para"` 568 | } `xml:"enum"` 569 | } `xml:"enumlist"` 570 | Note struct { 571 | Text string `xml:",chardata"` 572 | Para struct { 573 | Text string `xml:",chardata"` 574 | Literal string `xml:"literal"` 575 | } `xml:"para"` 576 | } `xml:"note"` 577 | } `xml:"enum"` 578 | } `xml:"enumlist"` 579 | } `xml:"parameter"` 580 | Enumlist struct { 581 | Text string `xml:",chardata"` 582 | Enum []struct { 583 | Text string `xml:",chardata"` 584 | Name string `xml:"name,attr"` 585 | Para string `xml:"para"` 586 | } `xml:"enum"` 587 | } `xml:"enumlist"` 588 | } `xml:"enum"` 589 | } `xml:"enumlist"` 590 | Example struct { 591 | Text string `xml:",chardata"` 592 | Title string `xml:"title,attr"` 593 | } `xml:"example"` 594 | Para struct { 595 | Text string `xml:",chardata"` 596 | Literal []string `xml:"literal"` 597 | } `xml:"para"` 598 | } `xml:"info"` 599 | Application []struct { 600 | Text string `xml:",chardata"` 601 | Name string `xml:"name,attr"` 602 | Language string `xml:"language,attr"` 603 | Module string `xml:"module,attr"` 604 | Synopsis string `xml:"synopsis"` 605 | Syntax struct { 606 | Text string `xml:",chardata"` 607 | Argsep string `xml:"argsep,attr"` 608 | Parameter []struct { 609 | Text string `xml:",chardata"` 610 | Name string `xml:"name,attr"` 611 | Required string `xml:"required,attr"` 612 | Argsep string `xml:"argsep,attr"` 613 | Multiple string `xml:"multiple,attr"` 614 | Hasparams string `xml:"hasparams,attr"` 615 | Para []struct { 616 | Text string `xml:",chardata"` 617 | Literal []string `xml:"literal"` 618 | Directory string `xml:"directory"` 619 | Filename string `xml:"filename"` 620 | Variable string `xml:"variable"` 621 | Replaceable []string `xml:"replaceable"` 622 | Emphasis string `xml:"emphasis"` 623 | } `xml:"para"` 624 | Enumlist struct { 625 | Text string `xml:",chardata"` 626 | Enum []struct { 627 | Text string `xml:",chardata"` 628 | Name string `xml:"name,attr"` 629 | Para string `xml:"para"` 630 | } `xml:"enum"` 631 | } `xml:"enumlist"` 632 | Optionlist struct { 633 | Text string `xml:",chardata"` 634 | Option []struct { 635 | Text string `xml:",chardata"` 636 | Name string `xml:"name,attr"` 637 | Implies string `xml:"implies,attr"` 638 | Argsep string `xml:"argsep,attr"` 639 | Hasparams string `xml:"hasparams,attr"` 640 | Para []struct { 641 | Text string `xml:",chardata"` 642 | Literal []string `xml:"literal"` 643 | Replaceable []string `xml:"replaceable"` 644 | Variable []string `xml:"variable"` 645 | Emphasis []string `xml:"emphasis"` 646 | Filename string `xml:"filename"` 647 | Directory string `xml:"directory"` 648 | } `xml:"para"` 649 | Argument []struct { 650 | Text string `xml:",chardata"` 651 | Name string `xml:"name,attr"` 652 | Required string `xml:"required,attr"` 653 | Hasparams string `xml:"hasparams,attr"` 654 | Argsep string `xml:"argsep,attr"` 655 | Multiple string `xml:"multiple,attr"` 656 | Para []struct { 657 | Text string `xml:",chardata"` 658 | Replaceable string `xml:"replaceable"` 659 | Variable string `xml:"variable"` 660 | Literal []string `xml:"literal"` 661 | Filename string `xml:"filename"` 662 | Emphasis string `xml:"emphasis"` 663 | } `xml:"para"` 664 | Argument []struct { 665 | Text string `xml:",chardata"` 666 | Name string `xml:"name,attr"` 667 | Multiple string `xml:"multiple,attr"` 668 | Required string `xml:"required,attr"` 669 | } `xml:"argument"` 670 | } `xml:"argument"` 671 | Enumlist struct { 672 | Text string `xml:",chardata"` 673 | Enum []struct { 674 | Text string `xml:",chardata"` 675 | Name string `xml:"name,attr"` 676 | Para string `xml:"para"` 677 | } `xml:"enum"` 678 | } `xml:"enumlist"` 679 | Warning struct { 680 | Text string `xml:",chardata"` 681 | Para struct { 682 | Text string `xml:",chardata"` 683 | Variable []string `xml:"variable"` 684 | } `xml:"para"` 685 | } `xml:"warning"` 686 | Note []struct { 687 | Text string `xml:",chardata"` 688 | Para struct { 689 | Text string `xml:",chardata"` 690 | Literal []string `xml:"literal"` 691 | Replaceable string `xml:"replaceable"` 692 | Variable string `xml:"variable"` 693 | } `xml:"para"` 694 | } `xml:"note"` 695 | Variablelist struct { 696 | Text string `xml:",chardata"` 697 | Variable []struct { 698 | Text string `xml:",chardata"` 699 | Name string `xml:"name,attr"` 700 | Para struct { 701 | Text string `xml:",chardata"` 702 | Replaceable []string `xml:"replaceable"` 703 | } `xml:"para"` 704 | Value []struct { 705 | Text string `xml:",chardata"` 706 | Name string `xml:"name,attr"` 707 | Default string `xml:"default,attr"` 708 | } `xml:"value"` 709 | } `xml:"variable"` 710 | } `xml:"variablelist"` 711 | } `xml:"option"` 712 | } `xml:"optionlist"` 713 | Argument []struct { 714 | Text string `xml:",chardata"` 715 | Name string `xml:"name,attr"` 716 | Argsep string `xml:"argsep,attr"` 717 | Required string `xml:"required,attr"` 718 | Multiple string `xml:"multiple,attr"` 719 | Hasparams string `xml:"hasparams,attr"` 720 | Argument []struct { 721 | Text string `xml:",chardata"` 722 | Name string `xml:"name,attr"` 723 | Required string `xml:"required,attr"` 724 | Multiple string `xml:"multiple,attr"` 725 | } `xml:"argument"` 726 | Para []struct { 727 | Text string `xml:",chardata"` 728 | Literal string `xml:"literal"` 729 | Replaceable []string `xml:"replaceable"` 730 | Filename string `xml:"filename"` 731 | } `xml:"para"` 732 | } `xml:"argument"` 733 | Warning struct { 734 | Text string `xml:",chardata"` 735 | Para struct { 736 | Text string `xml:",chardata"` 737 | Variable []string `xml:"variable"` 738 | } `xml:"para"` 739 | } `xml:"warning"` 740 | Note struct { 741 | Text string `xml:",chardata"` 742 | Para struct { 743 | Text string `xml:",chardata"` 744 | Replaceable []string `xml:"replaceable"` 745 | Emphasis string `xml:"emphasis"` 746 | Literal string `xml:"literal"` 747 | } `xml:"para"` 748 | } `xml:"note"` 749 | Include struct { 750 | Text string `xml:",chardata"` 751 | Xpointer string `xml:"xpointer,attr"` 752 | } `xml:"include"` 753 | } `xml:"parameter"` 754 | Include []struct { 755 | Text string `xml:",chardata"` 756 | Xpointer string `xml:"xpointer,attr"` 757 | } `xml:"include"` 758 | } `xml:"syntax"` 759 | Description struct { 760 | Text string `xml:",chardata"` 761 | Para []struct { 762 | Text string `xml:",chardata"` 763 | Replaceable []string `xml:"replaceable"` 764 | Literal []string `xml:"literal"` 765 | Emphasis string `xml:"emphasis"` 766 | Filename []string `xml:"filename"` 767 | Variable []string `xml:"variable"` 768 | Directory string `xml:"directory"` 769 | Astcli string `xml:"astcli"` 770 | } `xml:"para"` 771 | Note []struct { 772 | Text string `xml:",chardata"` 773 | Para []struct { 774 | Text string `xml:",chardata"` 775 | Literal []string `xml:"literal"` 776 | Replaceable []string `xml:"replaceable"` 777 | Variable []string `xml:"variable"` 778 | Filename string `xml:"filename"` 779 | } `xml:"para"` 780 | } `xml:"note"` 781 | Enumlist struct { 782 | Text string `xml:",chardata"` 783 | Enum []struct { 784 | Text string `xml:",chardata"` 785 | Name string `xml:"name,attr"` 786 | Para []struct { 787 | Text string `xml:",chardata"` 788 | Literal []string `xml:"literal"` 789 | Replaceable string `xml:"replaceable"` 790 | Directory string `xml:"directory"` 791 | Filename string `xml:"filename"` 792 | } `xml:"para"` 793 | } `xml:"enum"` 794 | } `xml:"enumlist"` 795 | Variablelist []struct { 796 | Text string `xml:",chardata"` 797 | Variable []struct { 798 | Text string `xml:",chardata"` 799 | Name string `xml:"name,attr"` 800 | Para []struct { 801 | Text string `xml:",chardata"` 802 | Literal []string `xml:"literal"` 803 | Emphasis string `xml:"emphasis"` 804 | Variable string `xml:"variable"` 805 | } `xml:"para"` 806 | Value []struct { 807 | Text string `xml:",chardata"` 808 | Name string `xml:"name,attr"` 809 | } `xml:"value"` 810 | } `xml:"variable"` 811 | } `xml:"variablelist"` 812 | Example []struct { 813 | Text string `xml:",chardata"` 814 | Title string `xml:"title,attr"` 815 | } `xml:"example"` 816 | Warning []struct { 817 | Text string `xml:",chardata"` 818 | Para struct { 819 | Text string `xml:",chardata"` 820 | Literal []string `xml:"literal"` 821 | Variable []string `xml:"variable"` 822 | } `xml:"para"` 823 | } `xml:"warning"` 824 | Include struct { 825 | Text string `xml:",chardata"` 826 | Xpointer string `xml:"xpointer,attr"` 827 | } `xml:"include"` 828 | } `xml:"description"` 829 | SeeAlso struct { 830 | Text string `xml:",chardata"` 831 | Ref []struct { 832 | Text string `xml:",chardata"` 833 | Type string `xml:"type,attr"` 834 | Module string `xml:"module,attr"` 835 | } `xml:"ref"` 836 | } `xml:"see-also"` 837 | } `xml:"application"` 838 | ManagerEvent []struct { 839 | Text string `xml:",chardata"` 840 | Language string `xml:"language,attr"` 841 | Name string `xml:"name,attr"` 842 | ManagerEventInstance struct { 843 | Text string `xml:",chardata"` 844 | Class string `xml:"class,attr"` 845 | Synopsis string `xml:"synopsis"` 846 | Syntax struct { 847 | Text string `xml:",chardata"` 848 | ChannelSnapshot []struct { 849 | Text string `xml:",chardata"` 850 | Prefix string `xml:"prefix,attr"` 851 | } `xml:"channel_snapshot"` 852 | Parameter []struct { 853 | Text string `xml:",chardata"` 854 | Name string `xml:"name,attr"` 855 | Required string `xml:"required,attr"` 856 | Multiple string `xml:"multiple,attr"` 857 | Para []struct { 858 | Text string `xml:",chardata"` 859 | Variable string `xml:"variable"` 860 | Literal []string `xml:"literal"` 861 | Replaceable []string `xml:"replaceable"` 862 | Include struct { 863 | Text string `xml:",chardata"` 864 | Xpointer string `xml:"xpointer,attr"` 865 | } `xml:"include"` 866 | } `xml:"para"` 867 | Note struct { 868 | Text string `xml:",chardata"` 869 | Para struct { 870 | Text string `xml:",chardata"` 871 | Literal []string `xml:"literal"` 872 | Filename string `xml:"filename"` 873 | Replaceable string `xml:"replaceable"` 874 | } `xml:"para"` 875 | } `xml:"note"` 876 | Enumlist struct { 877 | Text string `xml:",chardata"` 878 | Enum []struct { 879 | Text string `xml:",chardata"` 880 | Name string `xml:"name,attr"` 881 | Para []struct { 882 | Text string `xml:",chardata"` 883 | Literal string `xml:"literal"` 884 | } `xml:"para"` 885 | Note struct { 886 | Text string `xml:",chardata"` 887 | Para struct { 888 | Text string `xml:",chardata"` 889 | Filename string `xml:"filename"` 890 | Literal []string `xml:"literal"` 891 | Replaceable string `xml:"replaceable"` 892 | } `xml:"para"` 893 | } `xml:"note"` 894 | } `xml:"enum"` 895 | } `xml:"enumlist"` 896 | } `xml:"parameter"` 897 | Include []struct { 898 | Text string `xml:",chardata"` 899 | Xpointer string `xml:"xpointer,attr"` 900 | } `xml:"include"` 901 | BridgeSnapshot []struct { 902 | Text string `xml:",chardata"` 903 | Prefix string `xml:"prefix,attr"` 904 | } `xml:"bridge_snapshot"` 905 | } `xml:"syntax"` 906 | SeeAlso struct { 907 | Text string `xml:",chardata"` 908 | Ref []struct { 909 | Text string `xml:",chardata"` 910 | Type string `xml:"type,attr"` 911 | } `xml:"ref"` 912 | } `xml:"see-also"` 913 | Description struct { 914 | Text string `xml:",chardata"` 915 | Para []struct { 916 | Text string `xml:",chardata"` 917 | Literal []string `xml:"literal"` 918 | Replaceable string `xml:"replaceable"` 919 | Filename string `xml:"filename"` 920 | } `xml:"para"` 921 | Note struct { 922 | Text string `xml:",chardata"` 923 | Para struct { 924 | Text string `xml:",chardata"` 925 | Filename string `xml:"filename"` 926 | } `xml:"para"` 927 | } `xml:"note"` 928 | } `xml:"description"` 929 | } `xml:"managerEventInstance"` 930 | } `xml:"managerEvent"` 931 | Agi []struct { 932 | Text string `xml:",chardata"` 933 | Name string `xml:"name,attr"` 934 | Language string `xml:"language,attr"` 935 | Synopsis string `xml:"synopsis"` 936 | Syntax struct { 937 | Text string `xml:",chardata"` 938 | Parameter []struct { 939 | Text string `xml:",chardata"` 940 | Name string `xml:"name,attr"` 941 | Required string `xml:"required,attr"` 942 | Para struct { 943 | Text string `xml:",chardata"` 944 | Literal string `xml:"literal"` 945 | Replaceable []string `xml:"replaceable"` 946 | Filename string `xml:"filename"` 947 | } `xml:"para"` 948 | Enumlist struct { 949 | Text string `xml:",chardata"` 950 | Enum []struct { 951 | Text string `xml:",chardata"` 952 | Name string `xml:"name,attr"` 953 | Parameter struct { 954 | Text string `xml:",chardata"` 955 | Name string `xml:"name,attr"` 956 | Literal string `xml:"literal,attr"` 957 | Required string `xml:"required,attr"` 958 | } `xml:"parameter"` 959 | } `xml:"enum"` 960 | } `xml:"enumlist"` 961 | } `xml:"parameter"` 962 | } `xml:"syntax"` 963 | Description struct { 964 | Text string `xml:",chardata"` 965 | Para []struct { 966 | Text string `xml:",chardata"` 967 | Literal []string `xml:"literal"` 968 | Replaceable []string `xml:"replaceable"` 969 | } `xml:"para"` 970 | Enumlist struct { 971 | Text string `xml:",chardata"` 972 | Enum []struct { 973 | Text string `xml:",chardata"` 974 | Name string `xml:"name,attr"` 975 | Para string `xml:"para"` 976 | } `xml:"enum"` 977 | } `xml:"enumlist"` 978 | Variablelist struct { 979 | Text string `xml:",chardata"` 980 | Variable []struct { 981 | Text string `xml:",chardata"` 982 | Name string `xml:"name,attr"` 983 | Para struct { 984 | Text string `xml:",chardata"` 985 | Literal string `xml:"literal"` 986 | } `xml:"para"` 987 | Value []struct { 988 | Text string `xml:",chardata"` 989 | Name string `xml:"name,attr"` 990 | } `xml:"value"` 991 | } `xml:"variable"` 992 | } `xml:"variablelist"` 993 | } `xml:"description"` 994 | SeeAlso struct { 995 | Text string `xml:",chardata"` 996 | Ref []struct { 997 | Text string `xml:",chardata"` 998 | Type string `xml:"type,attr"` 999 | } `xml:"ref"` 1000 | } `xml:"see-also"` 1001 | } `xml:"agi"` 1002 | } 1003 | -------------------------------------------------------------------------------- /agi/agimodels/commands.go: -------------------------------------------------------------------------------- 1 | // Code generated by xmlgen. DO NOT EDIT. 2 | 3 | package agimodels 4 | 5 | // GosubCommand Cause the channel to execute the specified dialplan subroutine. 6 | // 7 | // Cause the channel to execute the specified dialplan subroutine, returning to the dialplan with execution of a Return(). 8 | type GosubCommand struct { 9 | Context string 10 | Extension string 11 | Priority int 12 | OptionalArgument *string 13 | } 14 | 15 | func (cmd GosubCommand) Command() (string, error) { 16 | s := []interface{}{cmd.CommandString(), cmd.Context, cmd.Extension, cmd.Priority, cmd.OptionalArgument} 17 | return joinCommand(s), nil 18 | } 19 | func (cmd GosubCommand) CommandString() string { 20 | return "GOSUB" 21 | } 22 | 23 | func (cmd GosubCommand) SetOptionalArgument(v string) GosubCommand { 24 | cmd.OptionalArgument = &v 25 | return cmd 26 | } 27 | 28 | func (c *Client) Gosub(context string, extension string, priority int) Response { 29 | return c.Handler.Command(GosubCommand{ 30 | Context: context, 31 | Extension: extension, 32 | Priority: priority, 33 | }) 34 | } 35 | 36 | // AnswerCommand Answer channel 37 | // 38 | // Answers channel if not already in answer state. Returns `-1` on channel failure, or `0` if successful. 39 | type AnswerCommand struct { 40 | } 41 | 42 | func (cmd AnswerCommand) Command() (string, error) { 43 | s := []interface{}{cmd.CommandString()} 44 | return joinCommand(s), nil 45 | } 46 | func (cmd AnswerCommand) CommandString() string { 47 | return "ANSWER" 48 | } 49 | 50 | func (c *Client) Answer() Response { 51 | return c.Handler.Command(AnswerCommand{}) 52 | } 53 | 54 | // AsyncAGIBreakCommand Interrupts Async AGI 55 | // 56 | // Interrupts expected flow of Async AGI commands and returns control to previous source (typically, the PBX dialplan). 57 | type AsyncAGIBreakCommand struct { 58 | } 59 | 60 | func (cmd AsyncAGIBreakCommand) Command() (string, error) { 61 | s := []interface{}{cmd.CommandString()} 62 | return joinCommand(s), nil 63 | } 64 | func (cmd AsyncAGIBreakCommand) CommandString() string { 65 | return "ASYNCAGI BREAK" 66 | } 67 | 68 | func (c *Client) AsyncAGIBreak() Response { 69 | return c.Handler.Command(AsyncAGIBreakCommand{}) 70 | } 71 | 72 | // ChannelStatusCommand Returns status of the connected channel. 73 | // 74 | // Returns the status of the specified channelname . If no channel name is given then returns the status of the current channel. 75 | // 76 | // Return values: 77 | type ChannelStatusCommand struct { 78 | ChannelName *string 79 | } 80 | 81 | func (cmd ChannelStatusCommand) Command() (string, error) { 82 | s := []interface{}{cmd.CommandString(), cmd.ChannelName} 83 | return joinCommand(s), nil 84 | } 85 | func (cmd ChannelStatusCommand) CommandString() string { 86 | return "CHANNEL STATUS" 87 | } 88 | 89 | func (cmd ChannelStatusCommand) SetChannelName(v string) ChannelStatusCommand { 90 | cmd.ChannelName = &v 91 | return cmd 92 | } 93 | 94 | func (c *Client) ChannelStatus() Response { 95 | return c.Handler.Command(ChannelStatusCommand{}) 96 | } 97 | 98 | // ControlStreamFileCommand Sends audio file on channel and allows the listener to control the stream. 99 | // 100 | // Send the given file, allowing playback to be controlled by the given digits, if any. Use double quotes for the digits if you wish none to be permitted. If offsetms is provided then the audio will seek to offsetms before play starts. Returns `0` if playback completes without a digit being pressed, or the ASCII numerical value of the digit if one was pressed, or `-1` on error or if the channel was disconnected. Returns the position where playback was terminated as endpos. 101 | // 102 | // It sets the following channel variables upon completion: 103 | type ControlStreamFileCommand struct { 104 | // FileName The file extension must not be included in the filename. 105 | FileName string 106 | EscapeDigits string 107 | SkipMS *int 108 | Ffchar *string // default to # 109 | Rewchr *string // default to * 110 | Pausechr *string 111 | // OffsetMS Offset, in milliseconds, to start the audio playback 112 | OffsetMS *int 113 | } 114 | 115 | func (cmd ControlStreamFileCommand) Command() (string, error) { 116 | s := []interface{}{cmd.CommandString(), cmd.FileName, cmd.EscapeDigits, cmd.SkipMS, cmd.Ffchar, cmd.Rewchr, cmd.Pausechr, cmd.OffsetMS} 117 | return joinCommand(s), nil 118 | } 119 | func (cmd ControlStreamFileCommand) CommandString() string { 120 | return "CONTROL STREAM FILE" 121 | } 122 | 123 | func (cmd ControlStreamFileCommand) SetSkipMS(v int) ControlStreamFileCommand { 124 | cmd.SkipMS = &v 125 | return cmd 126 | } 127 | func (cmd ControlStreamFileCommand) SetFfchar(v string) ControlStreamFileCommand { 128 | cmd.Ffchar = &v 129 | return cmd 130 | } 131 | func (cmd ControlStreamFileCommand) SetRewchr(v string) ControlStreamFileCommand { 132 | cmd.Rewchr = &v 133 | return cmd 134 | } 135 | func (cmd ControlStreamFileCommand) SetPausechr(v string) ControlStreamFileCommand { 136 | cmd.Pausechr = &v 137 | return cmd 138 | } 139 | func (cmd ControlStreamFileCommand) SetOffsetMS(v int) ControlStreamFileCommand { 140 | cmd.OffsetMS = &v 141 | return cmd 142 | } 143 | 144 | func (c *Client) ControlStreamFile(fileName string, escapeDigits string) Response { 145 | return c.Handler.Command(ControlStreamFileCommand{ 146 | FileName: fileName, 147 | EscapeDigits: escapeDigits, 148 | }) 149 | } 150 | 151 | // DatabaseDelCommand Removes database key/value 152 | // 153 | // Deletes an entry in the Asterisk database for a given family and key . 154 | // 155 | // Returns `1` if successful, `0` otherwise. 156 | type DatabaseDelCommand struct { 157 | Family string 158 | Key string 159 | } 160 | 161 | func (cmd DatabaseDelCommand) Command() (string, error) { 162 | s := []interface{}{cmd.CommandString(), cmd.Family, cmd.Key} 163 | return joinCommand(s), nil 164 | } 165 | func (cmd DatabaseDelCommand) CommandString() string { 166 | return "DATABASE DEL" 167 | } 168 | 169 | func (c *Client) DatabaseDel(family string, key string) Response { 170 | return c.Handler.Command(DatabaseDelCommand{ 171 | Family: family, 172 | Key: key, 173 | }) 174 | } 175 | 176 | // DatabaseDelTreeCommand Removes database keytree/value 177 | // 178 | // Deletes a family or specific keytree within a family in the Asterisk database. 179 | // 180 | // Returns `1` if successful, `0` otherwise. 181 | type DatabaseDelTreeCommand struct { 182 | Family string 183 | KeyTree *string 184 | } 185 | 186 | func (cmd DatabaseDelTreeCommand) Command() (string, error) { 187 | s := []interface{}{cmd.CommandString(), cmd.Family, cmd.KeyTree} 188 | return joinCommand(s), nil 189 | } 190 | func (cmd DatabaseDelTreeCommand) CommandString() string { 191 | return "DATABASE DELTREE" 192 | } 193 | 194 | func (cmd DatabaseDelTreeCommand) SetKeyTree(v string) DatabaseDelTreeCommand { 195 | cmd.KeyTree = &v 196 | return cmd 197 | } 198 | 199 | func (c *Client) DatabaseDelTree(family string) Response { 200 | return c.Handler.Command(DatabaseDelTreeCommand{ 201 | Family: family, 202 | }) 203 | } 204 | 205 | // DatabaseGetCommand Gets database value 206 | // 207 | // Retrieves an entry in the Asterisk database for a given family and key . 208 | // 209 | // Returns `0` if key is not set. Returns `1` if key is set and returns the variable in parenthesis. 210 | // 211 | // Example return code: 200 result=1 (testvariable) 212 | type DatabaseGetCommand struct { 213 | Family string 214 | Key string 215 | } 216 | 217 | func (cmd DatabaseGetCommand) Command() (string, error) { 218 | s := []interface{}{cmd.CommandString(), cmd.Family, cmd.Key} 219 | return joinCommand(s), nil 220 | } 221 | func (cmd DatabaseGetCommand) CommandString() string { 222 | return "DATABASE GET" 223 | } 224 | 225 | func (c *Client) DatabaseGet(family string, key string) Response { 226 | return c.Handler.Command(DatabaseGetCommand{ 227 | Family: family, 228 | Key: key, 229 | }) 230 | } 231 | 232 | // DatabasePutCommand Adds/updates database value 233 | // 234 | // Adds or updates an entry in the Asterisk database for a given family , key , and value . 235 | // 236 | // Returns `1` if successful, `0` otherwise. 237 | type DatabasePutCommand struct { 238 | Family string 239 | Key string 240 | Value string 241 | } 242 | 243 | func (cmd DatabasePutCommand) Command() (string, error) { 244 | s := []interface{}{cmd.CommandString(), cmd.Family, cmd.Key, cmd.Value} 245 | return joinCommand(s), nil 246 | } 247 | func (cmd DatabasePutCommand) CommandString() string { 248 | return "DATABASE PUT" 249 | } 250 | 251 | func (c *Client) DatabasePut(family string, key string, value string) Response { 252 | return c.Handler.Command(DatabasePutCommand{ 253 | Family: family, 254 | Key: key, 255 | Value: value, 256 | }) 257 | } 258 | 259 | // ExecCommand Executes a given Application 260 | // 261 | // Executes application with given options . 262 | // 263 | // Returns whatever the application returns, or `-2` on failure to find application . 264 | type ExecCommand struct { 265 | Application string 266 | Options string 267 | } 268 | 269 | func (cmd ExecCommand) Command() (string, error) { 270 | s := []interface{}{cmd.CommandString(), cmd.Application, cmd.Options} 271 | return joinCommand(s), nil 272 | } 273 | func (cmd ExecCommand) CommandString() string { 274 | return "EXEC" 275 | } 276 | 277 | func (c *Client) Exec(application string, options string) Response { 278 | return c.Handler.Command(ExecCommand{ 279 | Application: application, 280 | Options: options, 281 | }) 282 | } 283 | 284 | // GetDataCommand Prompts for DTMF on a channel 285 | // 286 | // Stream the given file , and receive DTMF data. 287 | // 288 | // Returns the digits received from the channel at the other end. 289 | type GetDataCommand struct { 290 | File string 291 | Timeout *int 292 | Maxdigits *string 293 | } 294 | 295 | func (cmd GetDataCommand) Command() (string, error) { 296 | s := []interface{}{cmd.CommandString(), cmd.File, cmd.Timeout, cmd.Maxdigits} 297 | return joinCommand(s), nil 298 | } 299 | func (cmd GetDataCommand) CommandString() string { 300 | return "GET DATA" 301 | } 302 | 303 | func (cmd GetDataCommand) SetTimeout(v int) GetDataCommand { 304 | cmd.Timeout = &v 305 | return cmd 306 | } 307 | func (cmd GetDataCommand) SetMaxdigits(v string) GetDataCommand { 308 | cmd.Maxdigits = &v 309 | return cmd 310 | } 311 | 312 | func (c *Client) GetData(file string) Response { 313 | return c.Handler.Command(GetDataCommand{ 314 | File: file, 315 | }) 316 | } 317 | 318 | // GetFullVariableCommand Evaluates a channel expression 319 | // 320 | // Evaluates the given expression against the channel specified by channelname , or the current channel if channelname is not provided. 321 | // 322 | // Unlike GET VARIABLE, the expression is processed in a manner similar to dialplan evaluation, allowing complex and built-in variables to be accessed, e.g. `The time is ${EPOCH} ` 323 | // 324 | // Returns `0` if no channel matching channelname exists, `1` otherwise. 325 | // 326 | // Example return code: 200 result=1 (The time is 1578493800) 327 | type GetFullVariableCommand struct { 328 | Expression string 329 | ChannelName *string 330 | } 331 | 332 | func (cmd GetFullVariableCommand) Command() (string, error) { 333 | s := []interface{}{cmd.CommandString(), cmd.Expression, cmd.ChannelName} 334 | return joinCommand(s), nil 335 | } 336 | func (cmd GetFullVariableCommand) CommandString() string { 337 | return "GET FULL VARIABLE" 338 | } 339 | 340 | func (cmd GetFullVariableCommand) SetChannelName(v string) GetFullVariableCommand { 341 | cmd.ChannelName = &v 342 | return cmd 343 | } 344 | 345 | func (c *Client) GetFullVariable(expression string) Response { 346 | return c.Handler.Command(GetFullVariableCommand{ 347 | Expression: expression, 348 | }) 349 | } 350 | 351 | // GetOptionCommand Stream file, prompt for DTMF, with timeout. 352 | // 353 | // Behaves similar to STREAM FILE but used with a timeout option. 354 | type GetOptionCommand struct { 355 | FileName string 356 | EscapeDigits string 357 | Timeout *int 358 | } 359 | 360 | func (cmd GetOptionCommand) Command() (string, error) { 361 | s := []interface{}{cmd.CommandString(), cmd.FileName, cmd.EscapeDigits, cmd.Timeout} 362 | return joinCommand(s), nil 363 | } 364 | func (cmd GetOptionCommand) CommandString() string { 365 | return "GET OPTION" 366 | } 367 | 368 | func (cmd GetOptionCommand) SetTimeout(v int) GetOptionCommand { 369 | cmd.Timeout = &v 370 | return cmd 371 | } 372 | 373 | func (c *Client) GetOption(fileName string, escapeDigits string) Response { 374 | return c.Handler.Command(GetOptionCommand{ 375 | FileName: fileName, 376 | EscapeDigits: escapeDigits, 377 | }) 378 | } 379 | 380 | // GetVariableCommand Gets a channel variable. 381 | // 382 | // Returns `0` if variablename is not set. Returns `1` if variablename is set and returns the variable in parentheses. 383 | // 384 | // Example return code: 200 result=1 (testvariable) 385 | type GetVariableCommand struct { 386 | VariableName string 387 | } 388 | 389 | func (cmd GetVariableCommand) Command() (string, error) { 390 | s := []interface{}{cmd.CommandString(), cmd.VariableName} 391 | return joinCommand(s), nil 392 | } 393 | func (cmd GetVariableCommand) CommandString() string { 394 | return "GET VARIABLE" 395 | } 396 | 397 | func (c *Client) GetVariable(variableName string) Response { 398 | return c.Handler.Command(GetVariableCommand{ 399 | VariableName: variableName, 400 | }) 401 | } 402 | 403 | // HangupCommand Hangup a channel. 404 | // 405 | // Hangs up the specified channel. If no channel name is given, hangs up the current channel 406 | type HangupCommand struct { 407 | ChannelName *string 408 | } 409 | 410 | func (cmd HangupCommand) Command() (string, error) { 411 | s := []interface{}{cmd.CommandString(), cmd.ChannelName} 412 | return joinCommand(s), nil 413 | } 414 | func (cmd HangupCommand) CommandString() string { 415 | return "HANGUP" 416 | } 417 | 418 | func (cmd HangupCommand) SetChannelName(v string) HangupCommand { 419 | cmd.ChannelName = &v 420 | return cmd 421 | } 422 | 423 | func (c *Client) Hangup() Response { 424 | return c.Handler.Command(HangupCommand{}) 425 | } 426 | 427 | // NoopCommand Does nothing. 428 | // 429 | // Does nothing. 430 | type NoopCommand struct { 431 | } 432 | 433 | func (cmd NoopCommand) Command() (string, error) { 434 | s := []interface{}{cmd.CommandString()} 435 | return joinCommand(s), nil 436 | } 437 | func (cmd NoopCommand) CommandString() string { 438 | return "NOOP" 439 | } 440 | 441 | func (c *Client) Noop() Response { 442 | return c.Handler.Command(NoopCommand{}) 443 | } 444 | 445 | // ReceiveCharCommand Receives one character from channels supporting it. 446 | // 447 | // Receives a character of text on a channel. Most channels do not support the reception of text. Returns the decimal value of the character if one is received, or `0` if the channel does not support text reception. Returns `-1` only on error/hangup. 448 | type ReceiveCharCommand struct { 449 | // Timeout The maximum time to wait for input in milliseconds, or `0` for infinite. Most channels 450 | Timeout int 451 | } 452 | 453 | func (cmd ReceiveCharCommand) Command() (string, error) { 454 | s := []interface{}{cmd.CommandString(), cmd.Timeout} 455 | return joinCommand(s), nil 456 | } 457 | func (cmd ReceiveCharCommand) CommandString() string { 458 | return "RECEIVE CHAR" 459 | } 460 | 461 | func (c *Client) ReceiveChar(timeout int) Response { 462 | return c.Handler.Command(ReceiveCharCommand{ 463 | Timeout: timeout, 464 | }) 465 | } 466 | 467 | // ReceiveTextCommand Receives text from channels supporting it. 468 | // 469 | // Receives a string of text on a channel. Most channels do not support the reception of text. Returns `-1` for failure or `1` for success, and the string in parenthesis. 470 | type ReceiveTextCommand struct { 471 | // Timeout The timeout to be the maximum time to wait for input in milliseconds, or `0` for infinite. 472 | Timeout int 473 | } 474 | 475 | func (cmd ReceiveTextCommand) Command() (string, error) { 476 | s := []interface{}{cmd.CommandString(), cmd.Timeout} 477 | return joinCommand(s), nil 478 | } 479 | func (cmd ReceiveTextCommand) CommandString() string { 480 | return "RECEIVE TEXT" 481 | } 482 | 483 | func (c *Client) ReceiveText(timeout int) Response { 484 | return c.Handler.Command(ReceiveTextCommand{ 485 | Timeout: timeout, 486 | }) 487 | } 488 | 489 | // RecordFileCommand Records to a given file. 490 | // 491 | // Record to a file until a given dtmf digit in the sequence is received. Returns `-1` on hangup or error. The format will specify what kind of file will be recorded. The timeout is the maximum record time in milliseconds, or `-1` for no timeout . offset samples is optional, and, if provided, will seek to the offset without exceeding the end of the file. beep can take any value, and causes Asterisk to play a beep to the channel that is about to be recorded. silence is the number of seconds of silence allowed before the function returns despite the lack of dtmf digits or reaching timeout . silence value must be preceded by `s=` and is also optional. 492 | type RecordFileCommand struct { 493 | // FileName The destination filename of the recorded audio. 494 | FileName string 495 | // Format The audio format in which to save the resulting file. 496 | Format string 497 | // EscapeDigits The DTMF digits that will terminate the recording process. 498 | EscapeDigits string 499 | // Timeout The maximum recording time in milliseconds. Set to -1 for no limit. 500 | Timeout int 501 | // OffsetSamples Causes the recording to first seek to the specified offset before recording begins. 502 | OffsetSamples *string 503 | // Beep Causes Asterisk to play a beep as recording begins. This argument can take any value. 504 | Beep *string 505 | // SSilence The number of seconds of silence that are permitted before the recording is terminated, regardless of the escape_digits or timeout arguments. If specified, this parameter must be preceded by `s=`. 506 | SSilence *string 507 | } 508 | 509 | func (cmd RecordFileCommand) Command() (string, error) { 510 | s := []interface{}{cmd.CommandString(), cmd.FileName, cmd.Format, cmd.EscapeDigits, cmd.Timeout, cmd.OffsetSamples, cmd.Beep, cmd.SSilence} 511 | return joinCommand(s), nil 512 | } 513 | func (cmd RecordFileCommand) CommandString() string { 514 | return "RECORD FILE" 515 | } 516 | 517 | func (cmd RecordFileCommand) SetOffsetSamples(v string) RecordFileCommand { 518 | cmd.OffsetSamples = &v 519 | return cmd 520 | } 521 | func (cmd RecordFileCommand) SetBeep(v string) RecordFileCommand { 522 | cmd.Beep = &v 523 | return cmd 524 | } 525 | func (cmd RecordFileCommand) SetSSilence(v string) RecordFileCommand { 526 | cmd.SSilence = &v 527 | return cmd 528 | } 529 | 530 | func (c *Client) RecordFile(fileName string, format string, escapeDigits string, timeout int) Response { 531 | return c.Handler.Command(RecordFileCommand{ 532 | FileName: fileName, 533 | Format: format, 534 | EscapeDigits: escapeDigits, 535 | Timeout: timeout, 536 | }) 537 | } 538 | 539 | // SayAlphaCommand Says a given character string. 540 | // 541 | // Say a given character string, returning early if any of the given DTMF digits are received on the channel. Returns `0` if playback completes without a digit being pressed, or the ASCII numerical value of the digit if one was pressed or `-1` on error/hangup. 542 | type SayAlphaCommand struct { 543 | Number string 544 | EscapeDigits string 545 | } 546 | 547 | func (cmd SayAlphaCommand) Command() (string, error) { 548 | s := []interface{}{cmd.CommandString(), cmd.Number, cmd.EscapeDigits} 549 | return joinCommand(s), nil 550 | } 551 | func (cmd SayAlphaCommand) CommandString() string { 552 | return "SAY ALPHA" 553 | } 554 | 555 | func (c *Client) SayAlpha(number string, escapeDigits string) Response { 556 | return c.Handler.Command(SayAlphaCommand{ 557 | Number: number, 558 | EscapeDigits: escapeDigits, 559 | }) 560 | } 561 | 562 | // SayDigitsCommand Says a given digit string. 563 | // 564 | // Say a given digit string, returning early if any of the given DTMF digits are received on the channel. Returns `0` if playback completes without a digit being pressed, or the ASCII numerical value of the digit if one was pressed or `-1` on error/hangup. 565 | type SayDigitsCommand struct { 566 | Number string 567 | EscapeDigits string 568 | } 569 | 570 | func (cmd SayDigitsCommand) Command() (string, error) { 571 | s := []interface{}{cmd.CommandString(), cmd.Number, cmd.EscapeDigits} 572 | return joinCommand(s), nil 573 | } 574 | func (cmd SayDigitsCommand) CommandString() string { 575 | return "SAY DIGITS" 576 | } 577 | 578 | func (c *Client) SayDigits(number string, escapeDigits string) Response { 579 | return c.Handler.Command(SayDigitsCommand{ 580 | Number: number, 581 | EscapeDigits: escapeDigits, 582 | }) 583 | } 584 | 585 | // SayNumberCommand Says a given number. 586 | // 587 | // Say a given number, returning early if any of the given DTMF digits are received on the channel. Returns `0` if playback completes without a digit being pressed, or the ASCII numerical value of the digit if one was pressed or `-1` on error/hangup. 588 | type SayNumberCommand struct { 589 | Number string 590 | EscapeDigits string 591 | Gender *string 592 | } 593 | 594 | func (cmd SayNumberCommand) Command() (string, error) { 595 | s := []interface{}{cmd.CommandString(), cmd.Number, cmd.EscapeDigits, cmd.Gender} 596 | return joinCommand(s), nil 597 | } 598 | func (cmd SayNumberCommand) CommandString() string { 599 | return "SAY NUMBER" 600 | } 601 | 602 | func (cmd SayNumberCommand) SetGender(v string) SayNumberCommand { 603 | cmd.Gender = &v 604 | return cmd 605 | } 606 | 607 | func (c *Client) SayNumber(number string, escapeDigits string) Response { 608 | return c.Handler.Command(SayNumberCommand{ 609 | Number: number, 610 | EscapeDigits: escapeDigits, 611 | }) 612 | } 613 | 614 | // SayPhoneticCommand Says a given character string with phonetics. 615 | // 616 | // Say a given character string with phonetics, returning early if any of the given DTMF digits are received on the channel. Returns `0` if playback completes without a digit pressed, the ASCII numerical value of the digit if one was pressed, or `-1` on error/hangup. 617 | type SayPhoneticCommand struct { 618 | String string 619 | EscapeDigits string 620 | } 621 | 622 | func (cmd SayPhoneticCommand) Command() (string, error) { 623 | s := []interface{}{cmd.CommandString(), cmd.String, cmd.EscapeDigits} 624 | return joinCommand(s), nil 625 | } 626 | func (cmd SayPhoneticCommand) CommandString() string { 627 | return "SAY PHONETIC" 628 | } 629 | 630 | func (c *Client) SayPhonetic(string string, escapeDigits string) Response { 631 | return c.Handler.Command(SayPhoneticCommand{ 632 | String: string, 633 | EscapeDigits: escapeDigits, 634 | }) 635 | } 636 | 637 | // SayDateCommand Says a given date. 638 | // 639 | // Say a given date, returning early if any of the given DTMF digits are received on the channel. Returns `0` if playback completes without a digit being pressed, or the ASCII numerical value of the digit if one was pressed or `-1` on error/hangup. 640 | type SayDateCommand struct { 641 | // Date Is number of seconds elapsed since 00:00:00 on January 1, 1970. Coordinated Universal Time (UTC). 642 | Date string 643 | EscapeDigits string 644 | } 645 | 646 | func (cmd SayDateCommand) Command() (string, error) { 647 | s := []interface{}{cmd.CommandString(), cmd.Date, cmd.EscapeDigits} 648 | return joinCommand(s), nil 649 | } 650 | func (cmd SayDateCommand) CommandString() string { 651 | return "SAY DATE" 652 | } 653 | 654 | func (c *Client) SayDate(date string, escapeDigits string) Response { 655 | return c.Handler.Command(SayDateCommand{ 656 | Date: date, 657 | EscapeDigits: escapeDigits, 658 | }) 659 | } 660 | 661 | // SayTimeCommand Says a given time. 662 | // 663 | // Say a given time, returning early if any of the given DTMF digits are received on the channel. Returns `0` if playback completes without a digit being pressed, or the ASCII numerical value of the digit if one was pressed or `-1` on error/hangup. 664 | type SayTimeCommand struct { 665 | // Time Is number of seconds elapsed since 00:00:00 on January 1, 1970. Coordinated Universal Time (UTC). 666 | Time float64 667 | EscapeDigits string 668 | } 669 | 670 | func (cmd SayTimeCommand) Command() (string, error) { 671 | s := []interface{}{cmd.CommandString(), cmd.Time, cmd.EscapeDigits} 672 | return joinCommand(s), nil 673 | } 674 | func (cmd SayTimeCommand) CommandString() string { 675 | return "SAY TIME" 676 | } 677 | 678 | func (c *Client) SayTime(time float64, escapeDigits string) Response { 679 | return c.Handler.Command(SayTimeCommand{ 680 | Time: time, 681 | EscapeDigits: escapeDigits, 682 | }) 683 | } 684 | 685 | // SayDatetimeCommand Says a given time as specified by the format given. 686 | // 687 | // Say a given time, returning early if any of the given DTMF digits are received on the channel. Returns `0` if playback completes without a digit being pressed, or the ASCII numerical value of the digit if one was pressed or `-1` on error/hangup. 688 | type SayDatetimeCommand struct { 689 | // Time Is number of seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC) 690 | Time float64 691 | EscapeDigits string 692 | // Format Is the format the time should be said in. See voicemail.conf (defaults to `ABdY 'digits/at' IMp`). 693 | Format *string 694 | // Timezone Acceptable values can be found in /usr/share/zoneinfo Defaults to machine default. 695 | Timezone *string 696 | } 697 | 698 | func (cmd SayDatetimeCommand) Command() (string, error) { 699 | s := []interface{}{cmd.CommandString(), cmd.Time, cmd.EscapeDigits, cmd.Format, cmd.Timezone} 700 | return joinCommand(s), nil 701 | } 702 | func (cmd SayDatetimeCommand) CommandString() string { 703 | return "SAY DATETIME" 704 | } 705 | 706 | func (cmd SayDatetimeCommand) SetFormat(v string) SayDatetimeCommand { 707 | cmd.Format = &v 708 | return cmd 709 | } 710 | func (cmd SayDatetimeCommand) SetTimezone(v string) SayDatetimeCommand { 711 | cmd.Timezone = &v 712 | return cmd 713 | } 714 | 715 | func (c *Client) SayDatetime(time float64, escapeDigits string) Response { 716 | return c.Handler.Command(SayDatetimeCommand{ 717 | Time: time, 718 | EscapeDigits: escapeDigits, 719 | }) 720 | } 721 | 722 | // SendImageCommand Sends images to channels supporting it. 723 | // 724 | // Sends the given image on a channel. Most channels do not support the transmission of images. Returns `0` if image is sent, or if the channel does not support image transmission. Returns `-1` only on error/hangup. Image names should not include extensions. 725 | type SendImageCommand struct { 726 | Image string 727 | } 728 | 729 | func (cmd SendImageCommand) Command() (string, error) { 730 | s := []interface{}{cmd.CommandString(), cmd.Image} 731 | return joinCommand(s), nil 732 | } 733 | func (cmd SendImageCommand) CommandString() string { 734 | return "SEND IMAGE" 735 | } 736 | 737 | func (c *Client) SendImage(image string) Response { 738 | return c.Handler.Command(SendImageCommand{ 739 | Image: image, 740 | }) 741 | } 742 | 743 | // SendTextCommand Sends text to channels supporting it. 744 | // 745 | // Sends the given text on a channel. Most channels do not support the transmission of text. Returns `0` if text is sent, or if the channel does not support text transmission. Returns `-1` only on error/hangup. 746 | type SendTextCommand struct { 747 | // TextToSend Text consisting of greater than one word should be placed in quotes since the command only accepts a single argument. 748 | TextToSend string 749 | } 750 | 751 | func (cmd SendTextCommand) Command() (string, error) { 752 | s := []interface{}{cmd.CommandString(), cmd.TextToSend} 753 | return joinCommand(s), nil 754 | } 755 | func (cmd SendTextCommand) CommandString() string { 756 | return "SEND TEXT" 757 | } 758 | 759 | func (c *Client) SendText(textToSend string) Response { 760 | return c.Handler.Command(SendTextCommand{ 761 | TextToSend: textToSend, 762 | }) 763 | } 764 | 765 | // SetAutoHangupCommand Autohangup channel in some time. 766 | // 767 | // Cause the channel to automatically hangup at time seconds in the future. Of course it can be hungup before then as well. Setting to `0` will cause the autohangup feature to be disabled on this channel. 768 | type SetAutoHangupCommand struct { 769 | Time float64 770 | } 771 | 772 | func (cmd SetAutoHangupCommand) Command() (string, error) { 773 | s := []interface{}{cmd.CommandString(), cmd.Time} 774 | return joinCommand(s), nil 775 | } 776 | func (cmd SetAutoHangupCommand) CommandString() string { 777 | return "SET AUTOHANGUP" 778 | } 779 | 780 | func (c *Client) SetAutoHangup(time float64) Response { 781 | return c.Handler.Command(SetAutoHangupCommand{ 782 | Time: time, 783 | }) 784 | } 785 | 786 | // SetCallerIDCommand Sets callerid for the current channel. 787 | // 788 | // Changes the callerid of the current channel. 789 | type SetCallerIDCommand struct { 790 | Number string 791 | } 792 | 793 | func (cmd SetCallerIDCommand) Command() (string, error) { 794 | s := []interface{}{cmd.CommandString(), cmd.Number} 795 | return joinCommand(s), nil 796 | } 797 | func (cmd SetCallerIDCommand) CommandString() string { 798 | return "SET CALLERID" 799 | } 800 | 801 | func (c *Client) SetCallerID(number string) Response { 802 | return c.Handler.Command(SetCallerIDCommand{ 803 | Number: number, 804 | }) 805 | } 806 | 807 | // SetContextCommand Sets channel context. 808 | // 809 | // Sets the context for continuation upon exiting the application. 810 | type SetContextCommand struct { 811 | DesiredContext string 812 | } 813 | 814 | func (cmd SetContextCommand) Command() (string, error) { 815 | s := []interface{}{cmd.CommandString(), cmd.DesiredContext} 816 | return joinCommand(s), nil 817 | } 818 | func (cmd SetContextCommand) CommandString() string { 819 | return "SET CONTEXT" 820 | } 821 | 822 | func (c *Client) SetContext(desiredContext string) Response { 823 | return c.Handler.Command(SetContextCommand{ 824 | DesiredContext: desiredContext, 825 | }) 826 | } 827 | 828 | // SetExtensionCommand Changes channel extension. 829 | // 830 | // Changes the extension for continuation upon exiting the application. 831 | type SetExtensionCommand struct { 832 | NewExtension string 833 | } 834 | 835 | func (cmd SetExtensionCommand) Command() (string, error) { 836 | s := []interface{}{cmd.CommandString(), cmd.NewExtension} 837 | return joinCommand(s), nil 838 | } 839 | func (cmd SetExtensionCommand) CommandString() string { 840 | return "SET EXTENSION" 841 | } 842 | 843 | func (c *Client) SetExtension(newExtension string) Response { 844 | return c.Handler.Command(SetExtensionCommand{ 845 | NewExtension: newExtension, 846 | }) 847 | } 848 | 849 | // SetMusicCommand Enable/Disable Music on hold generator 850 | // 851 | // Enables/Disables the music on hold generator. If class is not specified, then the `default` music on hold class will be used. This generator will be stopped automatically when playing a file. 852 | // 853 | // Always returns `0`. 854 | type SetMusicCommand struct { 855 | Class string 856 | // has missing params 857 | } 858 | 859 | func (cmd SetMusicCommand) Command() (string, error) { 860 | s := []interface{}{cmd.CommandString(), cmd.Class} 861 | return joinCommand(s), nil 862 | } 863 | func (cmd SetMusicCommand) CommandString() string { 864 | return "SET MUSIC" 865 | } 866 | 867 | func (c *Client) SetMusic(class string) Response { 868 | return c.Handler.Command(SetMusicCommand{ 869 | Class: class, 870 | }) 871 | } 872 | 873 | // SetPriorityCommand Set channel dialplan priority. 874 | // 875 | // Changes the priority for continuation upon exiting the application. The priority must be a valid priority or label. 876 | type SetPriorityCommand struct { 877 | Priority int 878 | } 879 | 880 | func (cmd SetPriorityCommand) Command() (string, error) { 881 | s := []interface{}{cmd.CommandString(), cmd.Priority} 882 | return joinCommand(s), nil 883 | } 884 | func (cmd SetPriorityCommand) CommandString() string { 885 | return "SET PRIORITY" 886 | } 887 | 888 | func (c *Client) SetPriority(priority int) Response { 889 | return c.Handler.Command(SetPriorityCommand{ 890 | Priority: priority, 891 | }) 892 | } 893 | 894 | // SetVariableCommand Sets a channel variable. 895 | // 896 | // Sets a variable to the current channel. 897 | type SetVariableCommand struct { 898 | VariableName string 899 | Value string 900 | } 901 | 902 | func (cmd SetVariableCommand) Command() (string, error) { 903 | s := []interface{}{cmd.CommandString(), cmd.VariableName, cmd.Value} 904 | return joinCommand(s), nil 905 | } 906 | func (cmd SetVariableCommand) CommandString() string { 907 | return "SET VARIABLE" 908 | } 909 | 910 | func (c *Client) SetVariable(variableName string, value string) Response { 911 | return c.Handler.Command(SetVariableCommand{ 912 | VariableName: variableName, 913 | Value: value, 914 | }) 915 | } 916 | 917 | // StreamFileCommand Sends audio file on channel. 918 | // 919 | // Send the given file, allowing playback to be interrupted by the given digits, if any. Returns `0` if playback completes without a digit being pressed, or the ASCII numerical value of the digit if one was pressed, or `-1` on error or if the channel was disconnected. If musiconhold is playing before calling stream file it will be automatically stopped and will not be restarted after completion. 920 | // 921 | // It sets the following channel variables upon completion: 922 | type StreamFileCommand struct { 923 | // FileName File name to play. The file extension must not be included in the filename . 924 | FileName string 925 | // EscapeDigits Use double quotes for the digits if you wish none to be permitted. 926 | EscapeDigits string 927 | // SampleOffset If sample offset is provided then the audio will seek to sample offset before play starts. 928 | SampleOffset *int 929 | } 930 | 931 | func (cmd StreamFileCommand) Command() (string, error) { 932 | s := []interface{}{cmd.CommandString(), cmd.FileName, cmd.EscapeDigits, cmd.SampleOffset} 933 | return joinCommand(s), nil 934 | } 935 | func (cmd StreamFileCommand) CommandString() string { 936 | return "STREAM FILE" 937 | } 938 | 939 | func (cmd StreamFileCommand) SetSampleOffset(v int) StreamFileCommand { 940 | cmd.SampleOffset = &v 941 | return cmd 942 | } 943 | 944 | func (c *Client) StreamFile(fileName string, escapeDigits string) Response { 945 | return c.Handler.Command(StreamFileCommand{ 946 | FileName: fileName, 947 | EscapeDigits: escapeDigits, 948 | }) 949 | } 950 | 951 | // TddModeCommand Toggles TDD mode (for the deaf). 952 | // 953 | // Enable/Disable TDD transmission/reception on a channel. Returns `1` if successful, or `0` if channel is not TDD-capable. 954 | type TddModeCommand struct { 955 | Boolean string 956 | } 957 | 958 | func (cmd TddModeCommand) Command() (string, error) { 959 | s := []interface{}{cmd.CommandString(), cmd.Boolean} 960 | return joinCommand(s), nil 961 | } 962 | func (cmd TddModeCommand) CommandString() string { 963 | return "TDD MODE" 964 | } 965 | 966 | func (c *Client) TddMode(boolean string) Response { 967 | return c.Handler.Command(TddModeCommand{ 968 | Boolean: boolean, 969 | }) 970 | } 971 | 972 | // VerboseCommand Logs a message to the asterisk verbose log. 973 | // 974 | // Sends message to the console via verbose message system. level is the verbose level (1-4). Always returns `1` 975 | type VerboseCommand struct { 976 | Message string 977 | Level string 978 | } 979 | 980 | func (cmd VerboseCommand) Command() (string, error) { 981 | s := []interface{}{cmd.CommandString(), cmd.Message, cmd.Level} 982 | return joinCommand(s), nil 983 | } 984 | func (cmd VerboseCommand) CommandString() string { 985 | return "VERBOSE" 986 | } 987 | 988 | func (c *Client) Verbose(message string, level string) Response { 989 | return c.Handler.Command(VerboseCommand{ 990 | Message: message, 991 | Level: level, 992 | }) 993 | } 994 | 995 | // WaitForDigitCommand Waits for a digit to be pressed. 996 | // 997 | // Waits up to timeout milliseconds for channel to receive a DTMF digit. Returns `-1` on channel failure, `0` if no digit is received in the timeout, or the numerical value of the ascii of the digit if one is received. Use `-1` for the timeout value if you desire the call to block indefinitely. 998 | type WaitForDigitCommand struct { 999 | Timeout int 1000 | } 1001 | 1002 | func (cmd WaitForDigitCommand) Command() (string, error) { 1003 | s := []interface{}{cmd.CommandString(), cmd.Timeout} 1004 | return joinCommand(s), nil 1005 | } 1006 | func (cmd WaitForDigitCommand) CommandString() string { 1007 | return "WAIT FOR DIGIT" 1008 | } 1009 | 1010 | func (c *Client) WaitForDigit(timeout int) Response { 1011 | return c.Handler.Command(WaitForDigitCommand{ 1012 | Timeout: timeout, 1013 | }) 1014 | } 1015 | 1016 | // SpeechCreateCommand Creates a speech object. 1017 | // 1018 | // Create a speech object to be used by the other Speech AGI commands. 1019 | type SpeechCreateCommand struct { 1020 | Engine string 1021 | } 1022 | 1023 | func (cmd SpeechCreateCommand) Command() (string, error) { 1024 | s := []interface{}{cmd.CommandString(), cmd.Engine} 1025 | return joinCommand(s), nil 1026 | } 1027 | func (cmd SpeechCreateCommand) CommandString() string { 1028 | return "SPEECH CREATE" 1029 | } 1030 | 1031 | func (c *Client) SpeechCreate(engine string) Response { 1032 | return c.Handler.Command(SpeechCreateCommand{ 1033 | Engine: engine, 1034 | }) 1035 | } 1036 | 1037 | // SpeechSetCommand Sets a speech engine setting. 1038 | // 1039 | // Set an engine-specific setting. 1040 | type SpeechSetCommand struct { 1041 | Name string 1042 | Value string 1043 | } 1044 | 1045 | func (cmd SpeechSetCommand) Command() (string, error) { 1046 | s := []interface{}{cmd.CommandString(), cmd.Name, cmd.Value} 1047 | return joinCommand(s), nil 1048 | } 1049 | func (cmd SpeechSetCommand) CommandString() string { 1050 | return "SPEECH SET" 1051 | } 1052 | 1053 | func (c *Client) SpeechSet(name string, value string) Response { 1054 | return c.Handler.Command(SpeechSetCommand{ 1055 | Name: name, 1056 | Value: value, 1057 | }) 1058 | } 1059 | 1060 | // SpeechDestroyCommand Destroys a speech object. 1061 | // 1062 | // Destroy the speech object created by `SPEECH CREATE`. 1063 | type SpeechDestroyCommand struct { 1064 | } 1065 | 1066 | func (cmd SpeechDestroyCommand) Command() (string, error) { 1067 | s := []interface{}{cmd.CommandString()} 1068 | return joinCommand(s), nil 1069 | } 1070 | func (cmd SpeechDestroyCommand) CommandString() string { 1071 | return "SPEECH DESTROY" 1072 | } 1073 | 1074 | func (c *Client) SpeechDestroy() Response { 1075 | return c.Handler.Command(SpeechDestroyCommand{}) 1076 | } 1077 | 1078 | // SpeechLoadGrammarCommand Loads a grammar. 1079 | // 1080 | // Loads the specified grammar as the specified name. 1081 | type SpeechLoadGrammarCommand struct { 1082 | GrammarName string 1083 | PathToGrammar string 1084 | } 1085 | 1086 | func (cmd SpeechLoadGrammarCommand) Command() (string, error) { 1087 | s := []interface{}{cmd.CommandString(), cmd.GrammarName, cmd.PathToGrammar} 1088 | return joinCommand(s), nil 1089 | } 1090 | func (cmd SpeechLoadGrammarCommand) CommandString() string { 1091 | return "SPEECH LOAD GRAMMAR" 1092 | } 1093 | 1094 | func (c *Client) SpeechLoadGrammar(grammarName string, pathToGrammar string) Response { 1095 | return c.Handler.Command(SpeechLoadGrammarCommand{ 1096 | GrammarName: grammarName, 1097 | PathToGrammar: pathToGrammar, 1098 | }) 1099 | } 1100 | 1101 | // SpeechUnloadGrammarCommand Unloads a grammar. 1102 | // 1103 | // Unloads the specified grammar. 1104 | type SpeechUnloadGrammarCommand struct { 1105 | GrammarName string 1106 | } 1107 | 1108 | func (cmd SpeechUnloadGrammarCommand) Command() (string, error) { 1109 | s := []interface{}{cmd.CommandString(), cmd.GrammarName} 1110 | return joinCommand(s), nil 1111 | } 1112 | func (cmd SpeechUnloadGrammarCommand) CommandString() string { 1113 | return "SPEECH UNLOAD GRAMMAR" 1114 | } 1115 | 1116 | func (c *Client) SpeechUnloadGrammar(grammarName string) Response { 1117 | return c.Handler.Command(SpeechUnloadGrammarCommand{ 1118 | GrammarName: grammarName, 1119 | }) 1120 | } 1121 | 1122 | // SpeechActivateGrammarCommand Activates a grammar. 1123 | // 1124 | // Activates the specified grammar on the speech object. 1125 | type SpeechActivateGrammarCommand struct { 1126 | GrammarName string 1127 | } 1128 | 1129 | func (cmd SpeechActivateGrammarCommand) Command() (string, error) { 1130 | s := []interface{}{cmd.CommandString(), cmd.GrammarName} 1131 | return joinCommand(s), nil 1132 | } 1133 | func (cmd SpeechActivateGrammarCommand) CommandString() string { 1134 | return "SPEECH ACTIVATE GRAMMAR" 1135 | } 1136 | 1137 | func (c *Client) SpeechActivateGrammar(grammarName string) Response { 1138 | return c.Handler.Command(SpeechActivateGrammarCommand{ 1139 | GrammarName: grammarName, 1140 | }) 1141 | } 1142 | 1143 | // SpeechDeactivateGrammarCommand Deactivates a grammar. 1144 | // 1145 | // Deactivates the specified grammar on the speech object. 1146 | type SpeechDeactivateGrammarCommand struct { 1147 | GrammarName string 1148 | } 1149 | 1150 | func (cmd SpeechDeactivateGrammarCommand) Command() (string, error) { 1151 | s := []interface{}{cmd.CommandString(), cmd.GrammarName} 1152 | return joinCommand(s), nil 1153 | } 1154 | func (cmd SpeechDeactivateGrammarCommand) CommandString() string { 1155 | return "SPEECH DEACTIVATE GRAMMAR" 1156 | } 1157 | 1158 | func (c *Client) SpeechDeactivateGrammar(grammarName string) Response { 1159 | return c.Handler.Command(SpeechDeactivateGrammarCommand{ 1160 | GrammarName: grammarName, 1161 | }) 1162 | } 1163 | 1164 | // SpeechRecognizeCommand Recognizes speech. 1165 | // 1166 | // Plays back given prompt while listening for speech and dtmf. 1167 | type SpeechRecognizeCommand struct { 1168 | Prompt string 1169 | Timeout int 1170 | Offset *string 1171 | } 1172 | 1173 | func (cmd SpeechRecognizeCommand) Command() (string, error) { 1174 | s := []interface{}{cmd.CommandString(), cmd.Prompt, cmd.Timeout, cmd.Offset} 1175 | return joinCommand(s), nil 1176 | } 1177 | func (cmd SpeechRecognizeCommand) CommandString() string { 1178 | return "SPEECH RECOGNIZE" 1179 | } 1180 | 1181 | func (cmd SpeechRecognizeCommand) SetOffset(v string) SpeechRecognizeCommand { 1182 | cmd.Offset = &v 1183 | return cmd 1184 | } 1185 | 1186 | func (c *Client) SpeechRecognize(prompt string, timeout int) Response { 1187 | return c.Handler.Command(SpeechRecognizeCommand{ 1188 | Prompt: prompt, 1189 | Timeout: timeout, 1190 | }) 1191 | } 1192 | --------------------------------------------------------------------------------