// SetSoftmodemFallback saves the given softmodem fallback setting for a caller id // to FreeSWITCH's mod_db func SetSoftmodemFallback(c *eventsocket.Connection, cidnum string, enabled bool) error { if !Config.Freeswitch.SoftmodemFallback || cidnum == "" { return nil } var err error if c == nil { c, err = eventsocket.Dial(Config.Freeswitch.Socket, Config.Freeswitch.Password) if err != nil { return err } defer c.Close() } return FreeSwitchDBInsert(c, modDbFallbackRealm, cidnum, fmt.Sprintf("%d", time.Now().Unix())) }
// GetSoftmodemFallback checks if fallback to SpanDSP's softmodem (no T.38) // should be enabled for the given callerid number func GetSoftmodemFallback(c *eventsocket.Connection, cidnum string) (bool, error) { if !Config.Freeswitch.SoftmodemFallback || cidnum == "" { return false, nil } var err error if c == nil { c, err = eventsocket.Dial(Config.Freeswitch.Socket, Config.Freeswitch.Password) if err != nil { return false, err } defer c.Close() } exists, err := FreeSwitchDBExists(c, modDbFallbackRealm, cidnum) if err != nil { return false, err } return exists, nil }
func outboundHandler(c *eventsocket.Connection) { r = redis.New() r_err = r.Connect(host, port) if r_err != nil { log.Fatalf("Connect failed: %s\n", r_err.Error()) } log.Println("Connected to redis-server.") fmt.Println("new client:", c.RemoteAddr()) s := Session{} s.c = c { ev, err := c.Send("connect") if err != nil { fmt.Println("ERROR: ", err) return } s.callerid = ev.Get("Channel-Caller-Id-Number") s.callername = ev.Get("Channel-Caller-Id-Name") s.inbound_uuid = ev.Get("Channel-Unique-Id") } c.Send("linger 5") c.Execute("answer", "", true) go secretaryCallOut(&s) c.Execute("playback", "local_stream://moh", true) fmt.Println("playback aborted") normal_clearing := false { ev, err := c.Send("api uuid_getvar " + s.inbound_uuid + " hangup_cause") if err != nil && ev.Body != "" { normal_clearing = true } } hup := false if s.secretary_uuid == "" { hup = true } else { ev, err := c.Send("api uuid_exists " + s.secretary_uuid) if err != nil || ev.Body == "false" { hup = true } else { // secretary is running if normal_clearing { // abort the secretary fmt.Println("Aborting the secretary") c.Send("api uuid_kill " + s.secretary_uuid) } } } if hup { c.Execute("playback", "misc/sorry.wav", true) c.Execute("sleep", "500", true) c.Execute("hangup", "", false) fmt.Println("hup") } c.Close() }
func outboundHandler(c *eventsocket.Connection) { { r = redis.New() r_err = r.Connect(host, port) if r_err != nil { log.Fatalf("Connect failed: %s\n", r_err.Error()) } // log.Println("连接redis服务器.") } fmt.Println("new client:", c.RemoteAddr()) s := Session{} s.c = c { ev, err := c.Send("connect") if err != nil { fmt.Println("ERROR: ", err) return } s.caller_id_number = ev.Get("Channel-Caller-Id-Number") s.callername = ev.Get("Channel-Caller-Id-Name") s.destination_number = ev.Get("Channel-Destination-Number") s.inbound_uuid = ev.Get("Channel-Unique-Id") // 根据主被叫确定办公电话,客户电话,客户电话状态和分机号码 s.office_number_type = "湖南好搜" s.custom_number_status = "未知" if len(s.caller_id_number) == 4 { //主叫4位,内线外呼 s.call_direction = "呼出" s.subnum = s.caller_id_number s.custom_number = s.destination_number s.office_number, err = r.HGet("rec:"+s.subnum, "phone") if err != nil { s.office_number = "82088888" } // 确定分机所属部门及客户电话状态 if subnum, _ := r.SIsMember("allusers:100", s.subnum); subnum { s.subnum_type = "销售" if cnum, _ := r.SIsMember("rec:custom", s.custom_number); cnum { s.custom_number_status = "custom" } if cnum, _ := r.SIsMember("rec:warning", s.custom_number); cnum { s.custom_number_status = "warning" } if cnum, _ := r.SIsMember("rec:bother", s.custom_number); cnum { s.custom_number_status = "bother" } } } else if len(s.caller_id_number) > 7 { //主叫7位以上号码,1.外线呼入,被叫是好搜; 2.分公司/客户呼入,被叫是客户; 3.外线呼入,被叫是分公司/客户) s.call_direction = "呼入" s.custom_number = s.caller_id_number s.office_number = s.destination_number // 确定办公电话类型 if cnum, _ := r.SIsMember("phone:hnzby", s.caller_id_number); cnum { //主叫电话为周边云号码 s.call_direction = "呼出" s.custom_number = s.destination_number s.office_number = s.caller_id_number s.office_number_type = "湖南周边云" } else if dnum, _ := r.SIsMember("phone:hnzby", s.destination_number); dnum { //被叫电话位周边云号码 s.office_number_type = "湖南周边云" } else { s.subnum, err = r.HGet("rec:phone", s.caller_id_number) //通过办公号码找到分机号 if err != nil { s.subnum = "8888" } } } else { //其他异常号码,转前台 s.call_direction = "未知" s.custom_number_status = "异常" } // 确定客户电话类型 s.custom_number = clearNum(s.custom_number) if validate(s.custom_number, MOBILE) { //是否手机号码 if cnum, _ := r.SIsMember("phone:hdata:0731", s.custom_number[0:7]); cnum { s.custom_number_type = "本地手机" } else { s.custom_number_type = "外地手机" s.custom_number = "0" + s.custom_number //加0 } } else { if s.custom_number[0:1] == "0" { s.custom_number_type = "外地固话" } else { s.custom_number_type = "本地固话" } } r.Quit() // log.Println("断开redis服务器.") } // 录音文件名 year := time.Now().Format("2006") month := time.Now().Format("01") day := time.Now().Format("02") s.rec_file = fmt.Sprintf("/mnt/stone/%s/%s/%s/%s_%s_%s.wav", year, month, day, s.caller_id_number, s.destination_number, s.inbound_uuid) // 流程控制 // 1. 是否分公司/客户号码,送到相应网关 // 2. 是否不识别的主/被叫号码(用 call_direction="未知" 来表示) // 3. 呼出号码处理 // 3.1 是否销售分机外呼 --> 是否设置了电话号码状态 --> 播放相应提示语音,DTMF按键 --> 挂机 // 3.2 不满足上述条件,则直接外呼 // 4. 呼入号码处理 // 4.1 是否有对应的分机号码 -->(分机是否在线) --> 直接转分机 // 4.2 不满足上述条件,转前台总机 ringback := "ringback=file_string://" + WAIT c.Execute("set", ringback, false) c.Execute("set", "hangup_after_bridge=true", false) // c.Send("linger 5") c.Execute("answer", "", true) // c.Execute("playback", "local_stream://moh", true) cmd := "user/[email protected]" if s.office_number_type != "湖南好搜" { if s.office_number_type == "湖南周边云" { callerIDNumber := "effective_caller_id_number=" + s.caller_id_number c.Execute("set", callerIDNumber, false) if s.call_direction == "呼入" { cmd = "sofia/gateway/hnzby/" + s.destination_number } else if s.call_direction == "呼出" { cmd = "sofia/gateway/serv36/" + s.destination_number } } } else if s.call_direction == "呼出" { if s.subnum_type == "销售" { if s.custom_number_status == "warning" || s.custom_number_status == "custom" || s.custom_number_status == "bother" { log.Println("销售电话") sound := "/usr/share/freeswitch/sounds/" + s.custom_number_status + ".wav" c.Execute("playback", sound, true) // started := time.Now() // digit := "" c.Execute("hungup", "", true) // for time.Since(started).Seconds() < 60 && digit == "" { // ev, err := c.Send("api uuid_exists " + s.inbound_uuid) // if err != nil || ev.Body == "false" { // break // } // digit = playAndGetOneDigit( // sound, // c, s.inbound_uuid) // } // if digit != "1" { // c.Send("api uuid_break " + s.inbound_uuid) // c.Close() // return // c.Send("api uuid_break " + s.inbound_uuid) c.Close() return } } c.Execute("set", "RECORD_ANSWER_REQ=true", false) c.Execute("record_session", s.rec_file, false) callerIDNumber := "effective_caller_id_number=" + s.office_number c.Execute("set", callerIDNumber, false) cmd = "{ignore_early_media=false}sofia/gateway/serv36/" + s.custom_number } else if s.call_direction == "呼入" { c.Execute("set", "RECORD_ANSWER_REQ=true", false) c.Execute("record_session", s.rec_file, false) cmd = "user/" + s.subnum + "@192.168.36.1" } else { c.Execute("set", "RECORD_ANSWER_REQ=true", false) c.Execute("record_session", s.rec_file, false) } log.Println(s.caller_id_number, "-->", s.destination_number, s.call_direction, " 分机:", s.subnum, ", 办公电话:", s.office_number, ", 电话所属:", s.office_number_type) _, err := c.Execute("bridge", cmd, false) if err != nil { // hup = true log.Println("Error calling out: ", err) } else { // c.Send("api uuid_break " + s.inbound_uuid) //可能有挂断异常的情况,强行挂断。 c.Send("api uuid_kill " + s.inbound_uuid) log.Println(s.caller_id_number, "-->", s.destination_number, "挂断") } // go secretaryCallOut(&s) // c.Execute("playback", "local_stream://moh", true) // fmt.Println("playback aborted") // normal_clearing := false // { // ev, err := c.Send("api uuid_getvar " + s.inbound_uuid + // " hangup_cause") // if err != nil && ev.Body != "" { // normal_clearing = true // } // } // hup := false // if s.secretary_uuid == "" { // hup = true // } else { // ev, err := c.Send("api uuid_exists " + s.secretary_uuid) // if err != nil || ev.Body == "false" { // hup = true // } else { // // secretary is running // if normal_clearing { // // abort the secretary // fmt.Println("Aborting the secretary") // c.Send("api uuid_kill " + s.secretary_uuid) // } // } // } // if hup { // c.Execute("playback", "misc/sorry.wav", true) // c.Execute("sleep", "500", true) // c.Execute("hangup", "", false) // fmt.Println("hup") // } c.Close() }
// Handle incoming call func (e *EventSocketServer) handler(c *eventsocket.Connection) { logger.Logger.Println("Incoming Event Socket connection from", c.RemoteAddr()) connectev, err := c.Send("connect") // Returns: Ganzer Event mit alles if err != nil { c.Send("exit") logger.Logger.Print(err) return } channelUUID := uuid.Parse(connectev.Get("Unique-Id")) if channelUUID == nil { c.Send("exit") logger.Logger.Print(err) return } defer logger.Logger.Println(channelUUID, "Handler ending") // Filter and subscribe to events c.Send("linger") c.Send(fmt.Sprintf("filter Unique-ID %v", channelUUID)) c.Send("event plain CHANNEL_CALLSTATE CUSTOM spandsp::rxfaxnegociateresult spandsp::rxfaxpageresult spandsp::rxfaxresult") // Extract Caller/Callee recipient := connectev.Get("Variable_sip_to_user") cidname := connectev.Get("Channel-Caller-Id-Name") cidnum := connectev.Get("Channel-Caller-Id-Number") logger.Logger.Printf("Incoming call to %v from %v <%v>", recipient, cidname, cidnum) var device *Device if gofaxlib.Config.Gofaxd.AllocateInboundDevices { // Find free device device, err := devmanager.FindDevice(fmt.Sprintf("Receiving facsimile")) if err != nil { logger.Logger.Println(err) c.Execute("respond", "404", true) c.Send("exit") return } defer device.SetReady() } var usedDevice string if device != nil { usedDevice = device.Name } else { usedDevice = defaultDevice } csi := gofaxlib.Config.Freeswitch.Ident // Query DynamicConfig if dcCmd := gofaxlib.Config.Gofaxd.DynamicConfig; dcCmd != "" { logger.Logger.Println("Calling DynamicConfig script", dcCmd) dc, err := gofaxlib.DynamicConfig(dcCmd, usedDevice, cidnum, cidname, recipient) if err != nil { logger.Logger.Println("Error calling DynamicConfig:", err) } else { // Check if call should be rejected if gofaxlib.DynamicConfigBool(dc.GetFirst("RejectCall")) { logger.Logger.Println("DynamicConfig decided to reject this call") c.Execute("respond", "404", true) c.Send("exit") return } // Check if a custom identifier should be set if dynamicCsi := dc.GetFirst("LocalIdentifier"); dynamicCsi != "" { csi = dynamicCsi } } } sessionlog, err := gofaxlib.NewSessionLogger() if err != nil { c.Send("exit") logger.Logger.Print(err) return } logger.Logger.Println(channelUUID, "Logging events for commid", sessionlog.CommID(), "to", sessionlog.Logfile()) sessionlog.Log("Inbound channel UUID: ", channelUUID) // Check if T.38 should be disabled disableT38 := gofaxlib.Config.Freeswitch.DisableT38 if disableT38 { sessionlog.Log("T.38 disabled by configuration") } else { disableT38, err = gofaxlib.GetSoftmodemFallback(nil, cidnum) if err != nil { sessionlog.Log(err) disableT38 = false } if disableT38 { sessionlog.Log(fmt.Sprintf("Softmodem fallback active for caller %s, disabling T.38", cidnum)) } } sessionlog.Log(fmt.Sprintf("Accepting call to %v from %v <%v> with commid %v", recipient, cidname, cidnum, sessionlog.CommID())) if device != nil { // Notify faxq gofaxlib.Faxq.ModemStatus(device.Name, "I"+sessionlog.CommID()) gofaxlib.Faxq.ReceiveStatus(device.Name, "B") gofaxlib.Faxq.ReceiveStatus(device.Name, "S") defer gofaxlib.Faxq.ReceiveStatus(device.Name, "E") } // Start interacting with the caller if gofaxlib.Config.Gofaxd.Answerafter != 0 { c.Execute("ring_ready", "", true) c.Execute("sleep", strconv.FormatUint(gofaxlib.Config.Gofaxd.Answerafter, 10), true) } c.Execute("answer", "", true) if gofaxlib.Config.Gofaxd.Waittime != 0 { c.Execute("playback", "silence_stream://"+strconv.FormatUint(gofaxlib.Config.Gofaxd.Waittime, 10), true) } // Find filename in recvq to save received .tif seq, err := gofaxlib.GetSeqFor(recvqDir) if err != nil { c.Send("exit") sessionlog.Log(err) return } filename := filepath.Join(recvqDir, fmt.Sprintf(recvqFileFormat, seq)) filenameAbs := filepath.Join(gofaxlib.Config.Hylafax.Spooldir, filename) sessionlog.Log("Rxfax to", filenameAbs) if disableT38 { c.Execute("set", "fax_enable_t38=false", true) } else { c.Execute("set", "fax_enable_t38_request=true", true) c.Execute("set", "fax_enable_t38=true", true) } c.Execute("set", fmt.Sprintf("fax_ident=%s", csi), true) c.Execute("rxfax", filenameAbs, true) result := gofaxlib.NewFaxResult(channelUUID, sessionlog) es := gofaxlib.NewEventStream(c) pages := result.TransferredPages EventLoop: for { select { case ev := <-es.Events(): if ev.Get("Content-Type") == "text/disconnect-notice" { sessionlog.Log("Received disconnect message") //c.Close() //break EventLoop } else { result.AddEvent(ev) if result.Hangupcause != "" { c.Close() break EventLoop } if pages != result.TransferredPages { pages = result.TransferredPages if device != nil { gofaxlib.Faxq.ReceiveStatus(device.Name, "P") } } } case err := <-es.Errors(): if err.Error() == "EOF" { sessionlog.Log("Event socket client disconnected") } else { sessionlog.Log("Error:", err) } break EventLoop case _ = <-e.killChan: sessionlog.Log("Kill reqeust received, destroying channel") c.Send(fmt.Sprintf("api uuid_kill %v", channelUUID)) c.Close() return } } if device != nil { gofaxlib.Faxq.ReceiveStatus(device.Name, "D") } sessionlog.Log(fmt.Sprintf("Success: %v, Hangup Cause: %v, Result: %v", result.Success, result.Hangupcause, result.ResultText)) xfl := gofaxlib.NewXFRecord(result) xfl.Modem = usedDevice xfl.Filename = filename xfl.Destnum = recipient xfl.Cidnum = cidnum xfl.Cidname = cidname if err = xfl.SaveReceptionReport(); err != nil { sessionlog.Log(err) } // If reception failed: // Check if softmodem fallback should be enabled on the next call if gofaxlib.Config.Freeswitch.SoftmodemFallback && !result.Success { var activateFallback bool if result.NegotiateCount > 1 { // Activate fallback if negotiation was repeated sessionlog.Log(fmt.Sprintf("Fax failed with %d negotiations, enabling softmodem fallback for calls from/to %s.", result.NegotiateCount, cidnum)) activateFallback = true } else { var badrows uint for _, p := range result.PageResults { badrows += p.BadRows } if badrows > 0 { // Activate fallback if any bad rows were present sessionlog.Log(fmt.Sprintf("Fax failed with %d bad rows in %d pages, enabling softmodem fallback for calls from/to %s.", badrows, result.TransferredPages, cidnum)) activateFallback = true } } if activateFallback { err = gofaxlib.SetSoftmodemFallback(nil, cidnum, true) if err != nil { sessionlog.Log(err) } } } // Process received file rcvdcmd := gofaxlib.Config.Gofaxd.FaxRcvdCmd if rcvdcmd == "" { rcvdcmd = defaultFaxrcvdCmd } errmsg := "" if !result.Success { errmsg = result.ResultText } cmd := exec.Command(rcvdcmd, filename, usedDevice, sessionlog.CommID(), errmsg, cidnum, cidname, recipient) sessionlog.Log("Calling", cmd.Path, cmd.Args) if output, err := cmd.CombinedOutput(); err != nil { sessionlog.Log(cmd.Path, "ended with", err) sessionlog.Log(output) } else { sessionlog.Log(cmd.Path, "ended successfully") } return }