push.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. package zoho
  2. import (
  3. "crm-api/internal/app/mysql"
  4. "crm-api/pkg/httpclient"
  5. "crm-api/pkg/lfshook"
  6. "encoding/json"
  7. "fmt"
  8. "io/ioutil"
  9. "net/http"
  10. "slices"
  11. "strconv"
  12. "strings"
  13. "time"
  14. "gopkg.in/ini.v1"
  15. )
  16. var TimestampList = make(map[string]string) //临时记录通话成功时的初始时间戳,用于计算通话时长,通话结束后删除
  17. var StartTimeList = make(map[string]time.Time) //临时记录通话的初始时间,通话结束时删除
  18. // var LinkedidList = make(map[string]string) // 临时记录 incoming 和 outgoing
  19. // var DialStatusList = make(map[string]string) // 临时记录 DialStatus:ANSWER NOANSWER
  20. var CallTypeList = make(map[string]string) //通过Linkedid临时记录拨打类型呼入还是呼出,通话结束后删除
  21. var TimetimesList = make(map[string]int) //通过Linkedid临时记录呼入队列或部门的数量,通话结束后删除
  22. var AttendedTransferList = make(map[string]string) //临时记录协商转接的初始时间戳
  23. var AttendedSecondTransferList = make(map[string]string) //临时记录协商转接后的初始时间戳
  24. var ZohoExtenList = make([]string, 0) //记录zoho用户分机列表,只有zoho用户分机的呼入和呼出才会进行推送
  25. var n int //临时记录判断通话未接听的数量,若与呼入数量相同进行推送,若接听让n归0
  26. var ZOHO_URL string //设置推送地址
  27. // macro-trunkdial-failover为呼出判定;macro-stdexten为呼入开启语音留言的判定;macro-stdexten-withoutvm为呼入关闭语音留言的判定
  28. func CallClicktodialerror(event map[string]string) {
  29. //CallClicktodialerrorh函数用于过滤zoho crm拨打时出现的错误
  30. if event["Event"] == "SoftHangupRequest" {
  31. Channel := strings.Split(event["Channel"], "/")
  32. if (event["Context"] == "macro-stdexten" || event["Context"] == "macro-stdexten-withoutvm") && len(event["Exten"]) > 3 && Channel[0] == "Local" {
  33. ExtenStatus := strings.Split(event["Exten"], "-")
  34. if ExtenStatus[1] == "NOANSWER" && slices.Contains(ZohoExtenList, event["ConnectedLineNum"]) { //坐席未接听,通过ExtenStatus判断挂断原因,slices.Contains(ZohoExtenList, event["ConnectedLineNum"])判断主叫是否时zoho用户分机(坐席)
  35. getURL := fmt.Sprintf("%s/phonebridge/v3/clicktodialerror?code=noanswer&from=%s&to=%s", ZOHO_URL, event["ConnectedLineNum"], event["CallerIDNum"])
  36. // fmt.Println("getURL = ", getURL)
  37. go httpclient.ZohoGet(getURL)
  38. return
  39. } else if ExtenStatus[1] == "BUSY" { //坐席不可用
  40. ChannelStatus1 := strings.Split(event["Channel"], "@")
  41. ChannelStatus2 := strings.Split(ChannelStatus1[0], "/")
  42. if slices.Contains(ZohoExtenList, ChannelStatus2[1]) {
  43. getURL := fmt.Sprintf("%s/phonebridge/v3/clicktodialerror?code=notavailble&from=%s&to=%s&message=agentunavailable", ZOHO_URL, ChannelStatus2[1], event["CallerIDNum"])
  44. // fmt.Println("getURL = ", getURL)
  45. go httpclient.ZohoGet(getURL)
  46. return
  47. }
  48. } else if ExtenStatus[1] == "CHANUNAVAIL" && slices.Contains(ZohoExtenList, event["ConnectedLineNum"]) { //坐席拒接
  49. getURL := fmt.Sprintf("%s/phonebridge/v3/clicktodialerror?code=rejected&from=%s&to=%s", ZOHO_URL, event["ConnectedLineNum"], event["CallerIDNum"])
  50. // fmt.Println("getURL = ", getURL)
  51. go httpclient.ZohoGet(getURL)
  52. return
  53. }
  54. }
  55. } else if event["Event"] == "DialEnd" { //坐席未接听
  56. Channel := strings.Split(event["Channel"], "/")
  57. if (event["Context"] == "macro-stdexten" || event["Context"] == "macro-stdexten-withoutvm") && event["DialStatus"] == "CANCEL" && slices.Contains(ZohoExtenList, event["ConnectedLineNum"]) && Channel[0] == "Local" {
  58. getURL := fmt.Sprintf("%s/phonebridge/v3/clicktodialerror?code=noanswer&from=%s&to=%s", ZOHO_URL, event["ConnectedLineNum"], event["CallerIDNum"])
  59. // fmt.Println("getURL = ", getURL)
  60. go httpclient.ZohoGet(getURL)
  61. return
  62. }
  63. }
  64. CallNotify(event)
  65. }
  66. func CallNotify(event map[string]string) {
  67. // 坐席接听后调用此函数
  68. if event["Event"] == "DialBegin" { //响铃标志事件
  69. lfshook.NewLogger().Error(event)
  70. Channel := strings.Split(event["Channel"], "/")
  71. // Outgoing Call - Ringing
  72. if event["Context"] == "macro-trunkdial-failover" && slices.Contains(ZohoExtenList, event["CallerIDNum"]) {
  73. StartTimeList[event["Linkedid"]] = time.Now().Add(-8 * time.Hour)
  74. // https://www.zohoapis.com/phonebridge/v3/callnotify?type=dialed&state=ringing&id=10031&from=123456789&to=12300000001
  75. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=dialed&state=ringing&id=%s&from=%s&to=%s", ZOHO_URL, event["Linkedid"], event["CallerIDNum"], event["DestCallerIDNum"])
  76. // fmt.Println("getURL = ", getURL)
  77. go httpclient.ZohoGet(getURL)
  78. // Incoming Call - Ringing
  79. } else if (event["Context"] == "macro-stdexten" && Channel[0] != "Local" && slices.Contains(ZohoExtenList, event["DestCallerIDNum"])) || (event["Context"] == "macro-stdexten-withoutvm" && Channel[0] != "Local" && slices.Contains(ZohoExtenList, event["DestCallerIDNum"])) { // 开启语音留言:macro-stdexten 关闭语音留言:macro-stdexten-withoutvm
  80. StartTimeList[event["Linkedid"]] = time.Now().Add(-8 * time.Hour)
  81. // https://www.zohoapis.com/phonebridge/v3/callnotify?type=received&state=ringing&id=10033&from=12300000001&to=123456789
  82. // getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=ringing&id=%s&from=%s&to=%s", ZOHO_URL, callId, event["Exten"], callDest) // 取得from to 可能不准
  83. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=ringing&id=%s&from=%s&to=%s", ZOHO_URL, event["Linkedid"], event["CallerIDNum"], event["DestCallerIDNum"])
  84. // fmt.Println("getURL = ", getURL)
  85. go httpclient.ZohoGet(getURL)
  86. } else if (strings.HasPrefix(event["Context"], "department") || strings.HasSuffix(event["Context"], "queue")) && slices.Contains(ZohoExtenList, event["DestCallerIDNum"]) { //队列和部门判断
  87. TimetimesList[event["Linkedid"]] = TimetimesList[event["Linkedid"]] + 1
  88. StartTimeList[event["Linkedid"]] = time.Now().Add(-8 * time.Hour)
  89. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=ringing&id=%s&from=%s&to=%s", ZOHO_URL, event["Linkedid"], event["CallerIDNum"], event["DestCallerIDNum"])
  90. // fmt.Println("getURL = ", getURL)
  91. go httpclient.ZohoGet(getURL)
  92. }
  93. } else if event["Event"] == "DialEnd" { // 呼叫结束事件,未接听集合
  94. Channel := strings.Split(event["Channel"], "/")
  95. // 记录呼叫状态
  96. // Outgoing Call - Unattended
  97. if event["Context"] == "macro-trunkdial-failover" && event["DialStatus"] == "CANCEL" && slices.Contains(ZohoExtenList, event["CallerIDNum"]) { //坐席取消呼叫客户电话,event["DialStatus"]判断未接听挂断原因
  98. // startTime := StartTimeList[event["Linkedid"]].Format("2006-01-02 15:04:09")
  99. startTimeutc := StartTimeList[event["Linkedid"]].In(time.UTC) // 存储从string 改为 time.Time
  100. startTime := startTimeutc.Format(time.RFC3339)
  101. // https://www.zohoapis.com/phonebridge/v3/callnotify?type=dialed&state=busy&id=10025&from=123456789&to=12300000001&start_time=2024-12-04 15:09:10
  102. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=dialed&state=busy&id=%s&from=%s&to=%s&start_time=%s", ZOHO_URL, event["Linkedid"], event["CallerIDNum"], event["DestCallerIDNum"], startTime)
  103. // fmt.Println("getURL = ", getURL)
  104. go httpclient.ZohoGet(getURL)
  105. delete(StartTimeList, event["Linkedid"])
  106. // Incoming Call - Missed
  107. } else if event["Context"] == "macro-trunkdial-failover" && event["DialStatus"] == "NOANSWER" && slices.Contains(ZohoExtenList, event["CallerIDNum"]) { //坐席呼出,客户未接听,event["DialStatus"]判断未接听挂断原因
  108. startTimeutc := StartTimeList[event["Linkedid"]].In(time.UTC) // 存储从string 改为 time.Time
  109. startTime := startTimeutc.Format(time.RFC3339)
  110. // https://www.zohoapis.com/phonebridge/v3/callnotify?type=dialed&state=busy&id=10025&from=123456789&to=12300000001&start_time=2024-12-04 15:09:10
  111. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=dialed&state=noanswer&id=%s&from=%s&to=%s&start_time=%s", ZOHO_URL, event["Linkedid"], event["CallerIDNum"], event["DestCallerIDNum"], startTime)
  112. // fmt.Println("getURL = ", getURL)
  113. go httpclient.ZohoGet(getURL)
  114. delete(StartTimeList, event["Linkedid"])
  115. // Incoming Call - Missed
  116. } else if event["Context"] == "macro-trunkdial-failover" && event["DialStatus"] == "BUSY" && slices.Contains(ZohoExtenList, event["CallerIDNum"]) { //坐席呼出,客户拒接
  117. startTimeutc := StartTimeList[event["Linkedid"]].In(time.UTC) // 存储从string 改为 time.Time
  118. startTime := startTimeutc.Format(time.RFC3339)
  119. // https://www.zohoapis.com/phonebridge/v3/callnotify?type=dialed&state=busy&id=10025&from=123456789&to=12300000001&start_time=2024-12-04 15:09:10
  120. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=dialed&state=rejected&id=%s&from=%s&to=%s&start_time=%s", ZOHO_URL, event["Linkedid"], event["CallerIDNum"], event["DestCallerIDNum"], startTime)
  121. // fmt.Println("getURL = ", getURL)
  122. go httpclient.ZohoGet(getURL)
  123. delete(StartTimeList, event["Linkedid"])
  124. // Incoming Call - Missed
  125. } else if (event["Context"] == "macro-stdexten" && event["DialStatus"] != "ANSWER" && Channel[0] != "Local" && slices.Contains(ZohoExtenList, event["DestCallerIDNum"])) || (event["Context"] == "macro-stdexten-withoutvm" && event["DialStatus"] != "ANSWER" && Channel[0] != "Local" && slices.Contains(ZohoExtenList, event["DestCallerIDNum"])) { // 呼入坐席未接听
  126. startTimeutc := StartTimeList[event["Linkedid"]].In(time.UTC) // 存储从string 改为 time.Time
  127. startTime := startTimeutc.Format(time.RFC3339)
  128. // https://www.zohoapis.com/phonebridge/v3/callnotify?type=received&state=missed&id=10005&from=12300000001&to=123456789&start_time=2024-12-04 15:09:10
  129. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=missed&id=%s&from=%s&to=%s&start_time=%s", ZOHO_URL, event["Linkedid"], event["CallerIDNum"], event["DestCallerIDNum"], startTime)
  130. // fmt.Println("getURL = ", getURL)
  131. go httpclient.ZohoGet(getURL)
  132. delete(StartTimeList, event["Linkedid"])
  133. } else if (strings.HasPrefix(event["Context"], "department") || strings.HasSuffix(event["Context"], "queue")) && event["DialStatus"] != "ANSWER" && TimetimesList[event["Linkedid"]] < 2 && TimetimesList[event["Linkedid"]] > 0 && slices.Contains(ZohoExtenList, event["DestCallerIDNum"]) { //呼入队列或部门成员未接听(一个)
  134. startTimeutc := StartTimeList[event["Linkedid"]].In(time.UTC) // 存储从string 改为 time.Time
  135. startTime := startTimeutc.Format(time.RFC3339)
  136. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=missed&id=%s&from=%s&to=%s&start_time=%s", ZOHO_URL, event["Linkedid"], event["CallerIDNum"], event["DestCallerIDNum"], startTime)
  137. // fmt.Println("getURL = ", getURL)
  138. go httpclient.ZohoGet(getURL)
  139. delete(TimetimesList, event["Linkedid"])
  140. delete(StartTimeList, event["Linkedid"])
  141. } else if (strings.HasPrefix(event["Context"], "department") || strings.HasSuffix(event["Context"], "queue")) && event["DialStatus"] != "ANSWER" && TimetimesList[event["Linkedid"]] >= 2 && slices.Contains(ZohoExtenList, event["DestCallerIDNum"]) { //呼入队列或部门(两个以上)
  142. n = n + 1
  143. if TimetimesList[event["Linkedid"]] == n {
  144. startTimeutc := StartTimeList[event["Linkedid"]].In(time.UTC) // 存储从string 改为 time.Time
  145. startTime := startTimeutc.Format(time.RFC3339)
  146. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=missed&id=%s&from=%s&to=%s&start_time=%s", ZOHO_URL, event["Linkedid"], event["CallerIDNum"], event["DestCallerIDNum"], startTime)
  147. // fmt.Println("getURL = ", getURL)
  148. go httpclient.ZohoGet(getURL)
  149. delete(TimetimesList, event["Linkedid"])
  150. delete(StartTimeList, event["Linkedid"])
  151. n = 0
  152. }
  153. } else if event["DialStatus"] == "ANSWER" {
  154. delete(TimetimesList, event["Linkedid"])
  155. }
  156. // 呼叫已连接事件
  157. } else if event["Event"] == "BridgeEnter" { //接听标志事件
  158. Channel := strings.Split(event["Channel"], "/")
  159. // Outgoing Call - Answered
  160. if event["Context"] == "macro-trunkdial-failover" && event["Priority"] == "1" && slices.Contains(ZohoExtenList, event["ConnectedLineNum"]) { // 注意这里选择 Priority:1 的,便于确认 CallerIDNum,ConnectedLineNum
  161. // 记录开始时间
  162. TimestampList[event["Linkedid"]] = event["Timestamp"]
  163. StartTimeList[event["Linkedid"]] = time.Now().Add(-8 * time.Hour)
  164. // LinkedidList["Linkedid"] = event["Linkedid"]
  165. CallTypeList[event["Linkedid"]] = "outgoing"
  166. // https: //www.zohoapis.com/phonebridge/v3/callnotify?type=dialed&state=answered&id=10003&from=123456789&to=12300000001
  167. // getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=dialed&state=answered&id=%s&from=%s&to=%s", ZOHO_URL, callId, event["CallerIDNum"], event["ConnectedLineNum"])
  168. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=dialed&state=answered&id=%s&from=%s&to=%s", ZOHO_URL, event["Linkedid"], event["ConnectedLineNum"], event["CallerIDNum"])
  169. // fmt.Println("getURL = ", getURL)
  170. go httpclient.ZohoGet(getURL)
  171. // Incoming Call - Answered
  172. } else if (event["Context"] == "macro-stdexten" || event["Context"] == "macro-stdexten-withoutvm") && Channel[0] != "Local" && slices.Contains(ZohoExtenList, event["ConnectedLineNum"]) && event["SwapUniqueid"] == "" { // event["SwapUniqueid"]判断是否转移
  173. // 记录开始时间
  174. TimestampList[event["Linkedid"]] = event["Timestamp"] // 呼入时设置 Uniqueid 不是 Linkedid
  175. StartTimeList[event["Linkedid"]] = time.Now().Add(-8 * time.Hour) // 呼入时设置 Uniqueid 不是 Linkedid
  176. CallTypeList[event["Linkedid"]] = "incoming"
  177. // https://www.zohoapis.com/phonebridge/v3/callnotify?type=received&state=answered&id=10023&from=12300000001&to=123456789
  178. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=answered&id=%s&from=%s&to=%s", ZOHO_URL, event["Linkedid"], event["CallerIDNum"], event["ConnectedLineNum"])
  179. // fmt.Println("getURL = ", getURL)
  180. go httpclient.ZohoGet(getURL)
  181. } else if (strings.HasPrefix(event["Context"], "department") && event["Priority"] == "1" && event["SwapUniqueid"] == "") || (strings.HasSuffix(event["Context"], "queue") && event["SwapUniqueid"] == "") { //部门或队列成员接听
  182. TimestampList[event["Linkedid"]] = event["Timestamp"] // 呼入时设置 Uniqueid 不是 Linkedid
  183. StartTimeList[event["Linkedid"]] = time.Now().Add(-8 * time.Hour) // 呼入时设置 Uniqueid 不是 Linkedid
  184. // LinkedidList["Linkedid"] = event["Linkedid"]
  185. CallTypeList[event["Linkedid"]] = "incoming"
  186. if event["Priority"] == "1" && slices.Contains(ZohoExtenList, event["CallerIDNum"]) {
  187. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=answered&id=%s&from=%s&to=%s", ZOHO_URL, event["Linkedid"], event["ConnectedLineNum"], event["CallerIDNum"])
  188. // fmt.Println("getURL = ", getURL)
  189. go httpclient.ZohoGet(getURL)
  190. } else if slices.Contains(ZohoExtenList, event["ConnectedLineNum"]) {
  191. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=answered&id=%s&from=%s&to=%s", ZOHO_URL, event["Linkedid"], event["CallerIDNum"], event["ConnectedLineNum"])
  192. // fmt.Println("getURL = ", getURL)
  193. go httpclient.ZohoGet(getURL)
  194. }
  195. delete(TimetimesList, event["Linkedid"])
  196. n = 0
  197. }
  198. // else if event["SwapUniqueid"] != "" {
  199. // CallTypeList[event["Linkedid"]] = "incoming"
  200. // }
  201. // 呼叫挂断事件
  202. } else if event["Event"] == "Hangup" { //接听挂断标志事件
  203. // * ===================================================================================== */
  204. // 判断呼叫状态,为 ANSWER 时才推送,否则见以上推送 Outgoing Call - Unattended 或 Incoming Call - Missed
  205. Channel := strings.Split(event["Channel"], "/")
  206. // lfshook.NewLogger().Errorf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa %+v", event["Context"])
  207. // fmt.Println("ChannelHangupRequest1 = ", DialStatusList[event["Linkedid"]])
  208. // Outgoing Call - Ended
  209. if event["Context"] == "macro-trunkdial-failover" && CallTypeList[event["Linkedid"]] == "outgoing" { //坐席呼出挂断
  210. startTimeutc := StartTimeList[event["Linkedid"]].In(time.UTC) // 存储从string 改为 time.Time
  211. startTime := startTimeutc.Format(time.RFC3339)
  212. durationTime := getDuration(event["Linkedid"], event["Timestamp"])
  213. if durationTime != -1 { //判断通话时长是否正常
  214. if event["Priority"] == "1" && slices.Contains(ZohoExtenList, event["ConnectedLineNum"]) {
  215. // https://www.zohoapis.com/phonebridge/v3/callnotify?type=dailed&state=ended&id=10030&from=123456789&to=12300000001&start_time=2024-12-04 11:32:15&duration=325
  216. // getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=dailed&state=ended&id=%s&from=%s&to=%s&start_time=%s&duration=%d", ZOHO_URL, callId, event["CallerIDNum"], event["ConnectedLineNum"], startTime, durationTime)
  217. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=dailed&state=ended&id=%s&from=%s&to=%s&start_time=%s&duration=%d", ZOHO_URL, event["Linkedid"], event["ConnectedLineNum"], event["CallerIDNum"], startTime, durationTime)
  218. // fmt.Println("getURL = ", getURL)
  219. go httpclient.ZohoGet(getURL)
  220. } else if slices.Contains(ZohoExtenList, event["CallerIDNum"]) {
  221. // https://www.zohoapis.com/phonebridge/v3/callnotify?type=dailed&state=ended&id=10030&from=123456789&to=12300000001&start_time=2024-12-04 11:32:15&duration=325
  222. // getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=dailed&state=ended&id=%s&from=%s&to=%s&start_time=%s&duration=%d", ZOHO_URL, callId, event["CallerIDNum"], event["ConnectedLineNum"], startTime, durationTime)
  223. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=dailed&state=ended&id=%s&from=%s&to=%s&start_time=%s&duration=%d", ZOHO_URL, event["Linkedid"], event["CallerIDNum"], event["ConnectedLineNum"], startTime, durationTime)
  224. // fmt.Println("getURL = ", getURL)
  225. go httpclient.ZohoGet(getURL)
  226. }
  227. }
  228. // 删除记录时间
  229. delete(StartTimeList, event["Linkedid"])
  230. delete(CallTypeList, event["Linkedid"])
  231. } else if Channel[0] != "Local" && CallTypeList[event["Linkedid"]] == "incoming" && AttendedTransferList[event["Linkedid"]] == "" { //坐席呼入挂断
  232. startTimeutc := StartTimeList[event["Linkedid"]].In(time.UTC) // 存储从string 改为 time.Time
  233. startTime := startTimeutc.Format(time.RFC3339)
  234. durationTime := getDuration(event["Linkedid"], event["Timestamp"])
  235. if durationTime != -1 {
  236. if event["Priority"] == "1" && slices.Contains(ZohoExtenList, event["CallerIDNum"]) {
  237. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=ended&id=%s&from=%s&to=%s&start_time=%s&duration=%d", ZOHO_URL, event["Linkedid"], event["ConnectedLineNum"], event["CallerIDNum"], startTime, durationTime)
  238. // getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=ended&id=%s&from=%s&to=%s&start_time=%s&duration=%d", ZOHO_URL, callId, event["ConnectedLineNum"], event["CallerIDName"], startTime, durationTime)
  239. // fmt.Println("getURL = ", getURL)
  240. go httpclient.ZohoGet(getURL)
  241. } else if slices.Contains(ZohoExtenList, event["ConnectedLineNum"]) {
  242. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=ended&id=%s&from=%s&to=%s&start_time=%s&duration=%d", ZOHO_URL, event["Linkedid"], event["CallerIDNum"], event["ConnectedLineNum"], startTime, durationTime)
  243. // getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=ended&id=%s&from=%s&to=%s&start_time=%s&duration=%d", ZOHO_URL, callId, event["ConnectedLineNum"], event["CallerIDName"], startTime, durationTime)
  244. // fmt.Println("getURL = ", getURL)
  245. go httpclient.ZohoGet(getURL)
  246. }
  247. }
  248. delete(StartTimeList, event["Linkedid"])
  249. delete(CallTypeList, event["Linkedid"])
  250. } else if AttendedTransferList[event["Linkedid"]] != "" && CallTypeList[event["Linkedid"]] == "incoming" { //转接挂断
  251. startTimeutc := StartTimeList[event["Linkedid"]].In(time.UTC) // 存储从string 改为 time.Time
  252. startTime := startTimeutc.Format(time.RFC3339)
  253. durationTime := getDuration(event["Linkedid"], event["Timestamp"])
  254. if event["Priority"] == "1" && slices.Contains(ZohoExtenList, event["CallerIDNum"]) {
  255. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=ended&id=%s&from=%s&to=%s&start_time=%s&duration=%d", ZOHO_URL, event["Linkedid"], event["ConnectedLineNum"], event["CallerIDNum"], startTime, durationTime)
  256. // getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=ended&id=%s&from=%s&to=%s&start_time=%s&duration=%d", ZOHO_URL, callId, event["ConnectedLineNum"], event["CallerIDName"], startTime, durationTime)
  257. // fmt.Println("getURL = ", getURL)
  258. go httpclient.ZohoGet(getURL)
  259. } else if slices.Contains(ZohoExtenList, event["ConnectedLineNum"]) {
  260. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=ended&id=%s&from=%s&to=%s&start_time=%s&duration=%d", ZOHO_URL, event["Linkedid"], event["CallerIDNum"], event["ConnectedLineNum"], startTime, durationTime)
  261. // getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=ended&id=%s&from=%s&to=%s&start_time=%s&duration=%d", ZOHO_URL, callId, event["ConnectedLineNum"], event["CallerIDName"], startTime, durationTime)
  262. // fmt.Println("getURL = ", getURL)
  263. go httpclient.ZohoGet(getURL)
  264. }
  265. AttendedSecondTransferList[event["Linkedid"]] = AttendedTransferList[event["Linkedid"]] //创建转移后通话的初始时间戳
  266. delete(StartTimeList, event["Linkedid"])
  267. delete(CallTypeList, event["Linkedid"])
  268. delete(AttendedTransferList, event["Linkedid"])
  269. } else if AttendedSecondTransferList[event["Linkedid"]] != "" { //转接挂断
  270. startTimeutc := StartTimeList[AttendedSecondTransferList[event["Linkedid"]]].In(time.UTC) // 存储从string 改为 time.Time
  271. startTime := startTimeutc.Format(time.RFC3339)
  272. durationTime := getDuration(AttendedSecondTransferList[event["Linkedid"]], event["Timestamp"])
  273. if event["Priority"] == "1" && slices.Contains(ZohoExtenList, event["CallerIDNum"]) {
  274. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=ended&id=%s&from=%s&to=%s&start_time=%s&duration=%d", ZOHO_URL, AttendedSecondTransferList[event["Linkedid"]], event["ConnectedLineNum"], event["CallerIDNum"], startTime, durationTime)
  275. // getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=ended&id=%s&from=%s&to=%s&start_time=%s&duration=%d", ZOHO_URL, callId, event["ConnectedLineNum"], event["CallerIDName"], startTime, durationTime)
  276. // fmt.Println("getURL = ", getURL)
  277. go httpclient.ZohoGet(getURL)
  278. } else if slices.Contains(ZohoExtenList, event["ConnectedLineNum"]) {
  279. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=ended&id=%s&from=%s&to=%s&start_time=%s&duration=%d", ZOHO_URL, AttendedSecondTransferList[event["Linkedid"]], event["CallerIDNum"], event["ConnectedLineNum"], startTime, durationTime)
  280. // getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=ended&id=%s&from=%s&to=%s&start_time=%s&duration=%d", ZOHO_URL, callId, event["ConnectedLineNum"], event["CallerIDName"], startTime, durationTime)
  281. // fmt.Println("getURL = ", getURL)
  282. go httpclient.ZohoGet(getURL)
  283. }
  284. delete(StartTimeList, AttendedSecondTransferList[event["Linkedid"]])
  285. delete(AttendedSecondTransferList, event["Linkedid"])
  286. }
  287. } else if event["Event"] == "AttendedTransfer" && slices.Contains(ZohoExtenList, event["TransfereeConnectedLineNum"]) { //热转接
  288. if event["Result"] == "Success" {
  289. AttendedTransferList[event["OrigTransfererLinkedid"]] = event["SecondTransfererLinkedid"]
  290. delete(CallTypeList, event["SecondTransfererLinkedid"])
  291. }
  292. } else if event["Event"] == "SoftHangupRequest" { //呼入离线坐席
  293. Channel := strings.Split(event["Channel"], "/")
  294. if (event["Context"] == "macro-stdexten" || event["Context"] == "macro-stdexten-withoutvm") && Channel[0] != "Local" && len(event["Exten"]) > 3 {
  295. startTimebj := time.Now().Add(-8 * time.Hour)
  296. startTimeutc := startTimebj.In(time.UTC) // 存储从string 改为 time.Time
  297. startTime := startTimeutc.Format(time.RFC3339)
  298. if event["ConnectedLineNum"] == "<unknown>" {
  299. ConnectedLineNum := strings.Split(strings.Split(event["ConnectedLineNum"], "<")[1], ">")[0]
  300. getURL := fmt.Sprintf("%s/phonebridge/v3/callnotify?type=received&state=missed&id=%s&from=%s&to=%s&start_time=%s", ZOHO_URL, event["Linkedid"], event["CallerIDNum"], ConnectedLineNum, startTime)
  301. // fmt.Println("getURL = ", getURL)
  302. go httpclient.ZohoGet(getURL)
  303. }
  304. }
  305. }
  306. // 测试验证下上面有没有删除,以防TimestampList越来越大
  307. // for k, v := range TimestampList {
  308. // fmt.Printf("===Linkedid:%s Timestamp:%s===\n", k, v)
  309. // }
  310. }
  311. // 使用事件中event["Timestamp"]的值计算时间间隔
  312. // func getDuration(uniqueid string, timeStamp string) int {
  313. func getDuration(linkedid string, timeStamp string) int { // 都改为 Linkedid
  314. fmt.Println("startTimeStamp Str: ", TimestampList[linkedid])
  315. if TimestampList[linkedid] == "" || timeStamp == "" {
  316. fmt.Printf("linkedid 或 timeStamp 为空\n")
  317. return -1
  318. }
  319. startTimeStamp, err := strconv.ParseFloat(TimestampList[linkedid], 64)
  320. if err != nil {
  321. fmt.Printf("startTimeStamp转换出错: %v\n", err)
  322. return -1
  323. }
  324. fmt.Printf("startTimeStamp=%f\n", startTimeStamp)
  325. endTimeStamp, err := strconv.ParseFloat(timeStamp, 64)
  326. if err != nil {
  327. fmt.Printf("endTimeStamp转换出错: %v\n", err)
  328. return -1
  329. }
  330. fmt.Printf("endTimeStamp=%f\n", endTimeStamp)
  331. durationTime := time.Duration(endTimeStamp-startTimeStamp) * time.Second
  332. fmt.Printf("durationTime=%d\n", int(durationTime.Seconds())) // durationTime=35
  333. // 删除记录时间
  334. delete(TimestampList, linkedid)
  335. // return durationTime
  336. return int(durationTime.Seconds())
  337. }
  338. // @tags PBX-zoho
  339. // @Summary 刷新token
  340. // @Description 刷新token
  341. // @Security ZohoToken
  342. // @Accept json
  343. // @Produce json
  344. // @Router /api/zoho/refresh-token [post]
  345. func RefreshToken() {
  346. // 获取配置文件信息
  347. // confPath := "/etc/asterisk/vtiger_api.conf"
  348. confPath := "/etc/asterisk/crm_api.conf"
  349. cfg, err := ini.Load(confPath)
  350. if err != nil {
  351. lfshook.NewLogger().Error(err)
  352. return
  353. }
  354. ZohoAuthUrl := cfg.Section("general").Key("zohoAuthUrl").String()
  355. ZohoRefreshToken := cfg.Section("general").Key("zohoRefreshToken").String()
  356. ZohoClientId := cfg.Section("general").Key("zohoClientId").String()
  357. ZohoClientSecret := cfg.Section("general").Key("zohoClientSecret").String()
  358. if ZohoAuthUrl == "" || ZohoRefreshToken == "" || ZohoClientId == "" || ZohoClientSecret == "" {
  359. lfshook.NewLogger().Error("/etc/asterisk/crm_api.conf not set zohoAuthUrl or zohoRefreshToken or zohoClientId or zohoClientSecret")
  360. return
  361. }
  362. // 创建请求
  363. // https://accounts.zoho.com/oauth/v2/token?refresh_token=1004.86c8c0e3db7bfe9133598825bef28eb9.17a82a3bf3e675c504f478c1b0b5c456
  364. // &client_id=1004.LWJCJZD5O9DB6SZLL5YJEWHT7LH0BV&client_secret=fc3aef43dc58af8a49d3ed597710924200b03f74d0&grant_type=refresh_token
  365. getURL := fmt.Sprintf("%s/oauth/v2/token?refresh_token=%s&client_id=%s&client_secret=%s&grant_type=refresh_token", ZohoAuthUrl, ZohoRefreshToken, ZohoClientId, ZohoClientSecret)
  366. fmt.Printf("getURL = %s\n", getURL)
  367. // data := httpRequest(ctx, "POST", getURL)
  368. // /* =====================================================================================
  369. req, err := http.NewRequest("POST", getURL, nil)
  370. if err != nil {
  371. fmt.Println("创建请求时发生错误:", err)
  372. return
  373. }
  374. // 创建HTTP客户端
  375. client := &http.Client{}
  376. // req.Header.Set("Authorization", "Bearer "+accessToken) // 刷新token时不需要
  377. // 发送请求
  378. resp, err := client.Do(req)
  379. if err != nil {
  380. fmt.Println("发送请求时发生错误:", err)
  381. return
  382. }
  383. defer resp.Body.Close()
  384. // 读取请求后的响应
  385. data, err := ioutil.ReadAll(resp.Body)
  386. if err != nil {
  387. // 读取数据错误
  388. lfshook.NewLogger().Warn("ioutil ReadAll failed :", err.Error())
  389. // api.Error(ctx, http.StatusInternalServerError, err.Error())
  390. return
  391. }
  392. lfshook.NewLogger().Infof("data %+v", string(data))
  393. // * ===================================================================================== */
  394. refreshTokenData := RefreshTokenResp{}
  395. err = json.Unmarshal([]byte(data), &refreshTokenData)
  396. if err != nil {
  397. // 转换数据错误
  398. lfshook.NewLogger().Warn("json unmarshal failed :", err.Error())
  399. // api.Error(ctx, http.StatusInternalServerError, err.Error())
  400. return
  401. }
  402. // 将获取的 access_token 写入文件 crm_api.conf
  403. cfg.Section("general").Key("zohoAccessToken").SetValue(refreshTokenData.AccessToken)
  404. ZOHO_URL = refreshTokenData.ApiDomain
  405. err = cfg.SaveTo(confPath)
  406. if err != nil {
  407. lfshook.NewLogger().Error(err)
  408. // api.Error(ctx, http.StatusInternalServerError, err.Error())
  409. return
  410. }
  411. // 打印请求后的响应
  412. fmt.Printf("data = %+v\n", string(data))
  413. }
  414. // 每隔55分钟刷新token
  415. func RefreshTokenTicker() {
  416. ticker := time.NewTicker(55 * time.Minute)
  417. defer ticker.Stop()
  418. done := make(chan bool)
  419. // go func() { // 注释掉OK
  420. for {
  421. select {
  422. case <-done:
  423. return
  424. case t := <-ticker.C:
  425. fmt.Println("Tick at", t)
  426. RefreshToken()
  427. }
  428. }
  429. // }()
  430. }
  431. // 每隔3秒更新zoho用户分机集合
  432. func ZohoUserExtenList() {
  433. // 构建参数
  434. // lfshook.NewLogger().Error("来了")
  435. list := make([]string, 0)
  436. session := mysql.DBOrmInstance.Prepare()
  437. // 查询数据
  438. var data []TabZohouser
  439. _, err := session.FindAndCount(&data)
  440. if err != nil {
  441. lfshook.NewLogger().Error(err)
  442. return
  443. }
  444. for _, v := range data {
  445. list = append(list, v.Exten)
  446. }
  447. ZohoExtenList = list
  448. // lfshook.NewLogger().Error(ZohoExtenList)
  449. }
  450. func RefreshZohoUserExtenListTicker() {
  451. ticker := time.NewTicker(5 * time.Second)
  452. defer ticker.Stop()
  453. done := make(chan bool)
  454. // go func() { // 注释掉OK
  455. for {
  456. select {
  457. case <-done:
  458. return
  459. case <-ticker.C:
  460. // fmt.Println("Tick at", t)
  461. ZohoUserExtenList()
  462. }
  463. }
  464. }