push.go 29 KB

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