在C#中构建一个虚拟软件电话,该软件电话可以在您的呼叫中心中作为振铃组...
生活随笔
收集整理的這篇文章主要介紹了
在C#中构建一个虚拟软件电话,该软件电话可以在您的呼叫中心中作为振铃组...
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文重點介紹C#中環組的開發。 任何呼叫中心的有效性不僅取決于運營商的行為,還取決于呼叫中心的技術背景。 學習完本指南后,您將能夠創建振鈴組分機(即“虛擬軟件電話”),該分機可用于根據預定義的振鈴組策略將傳入呼叫轉移到選定的呼叫中心座席之一。
下載源代碼:
Softphone-RingGroup-RingOneByOne.zip
內容
主題的重要性
呼叫中心或呼叫中心是一個集中式辦公室,用于通過電話接收或傳輸大量請求[1]。 換句話說:“呼叫中心是一個物理場所,通常由一定數量的計算機自動化來由組織來處理客戶和其他電話” [2]。 先前定義中的“ 通常具有一定程度的計算機自動化 ”部分表示,成功的呼叫中心基于人工和某種技術設備的合作。 因此,適當的IT基礎架構至關重要。
專家們說, 精心挑選員工是成功建立呼叫中心的第一步。 但是大多數技能都是可以學習的。 在第一線支持,服務臺和服務臺培訓期間 ,講師經常提請操作員注意以下幾點:
這些是成功呼叫中心必不可少的非常重要的人為因素。 另外,某些管理工具,例如績效評估和對員工的定期反饋,獎勵,積極的強化等,是有效呼叫中心不可或缺的要素。 但是如果沒有適當的技術,所有人類知識都是徒勞的。 無論是小型呼叫中心還是大型呼叫中心,要獲得更多利潤并建立客戶忠誠度,員工都必須使用先進的硬件和軟件設備 。
在外包的印度呼叫中心和大型的美國呼叫中心中,有什么共同點( 圖1 )? 是的,有很多東西,但是最重要的是核心技術。 在這兩個地方,運營商都使用VoIP臺式電話,軟電話,頭戴式耳機,以及:具有典型呼叫中心功能(例如,呼叫轉移,振鈴組,呼叫記錄,呼叫排隊,電話會議等)的高級PBX系統,可以使某些任務更多由于自動化,操作簡單。
圖1:技術設備也是小型和大型呼叫中心的關鍵因素[3]
有關呼叫中心的技術背景的更多信息
呼叫中心可以同時處理大量呼叫 ,并將其轉發給負責處理這些呼叫的人員。 但是,在當今快速發展的世界中,VoIP技術,高性能計算機和智能軟件應用程序幾乎是必不可少的。 如今的呼叫中心使用的是比老式的巨大“盒子”更多的最新設備 ,而老式的“盒子”卻有很多電線伸出來。
我們可以將呼叫中心分為兩種主要類型,如下所示[1]:
如果使用虛擬呼叫中心,則功能是固定的,不能無限擴展它們。 但是,在基于內部的呼叫中心的情況下,您將有機會通過開發所需的功能來改進系統。 實際上,只有想象力(當然還有公司的需求和條件)限制了這種改進。
環組開發簡介
在本文中,您將看到后一種情況的示例。 確切地說,本指南逐步演示了環組的創建。 該鈴聲組將是安裝在集團電話中的“虛擬” 分機 。 此分機基于軟件電話 (因此,即已注冊到PBX)。 PBX為該分機分配電話號碼。 呼叫時,該分機嘗試將來電轉移到其他分機之一(即轉移到先前已在PBX上注冊的振鈴組成員之一)。 振鈴組策略確定哪個成員將接受呼叫。
恐怕以前的線程包含一些新的(或令人恐懼的)表達式……不管是不是,為了完整起見,讓我澄清一下此解決方案的基本要素。
基于以上內容,讓我們總結一下該項目需要哪些開發:
因此,我使用了以下3類 : Softphone.cs:它介紹了如何使用可注冊到PBX的C#開發軟件電話。 Program.cs:此類負責處理用戶事件。 它介紹了如何將軟件電話注冊到PBX。 RingGroupCallHandler.cs:此類說明如何創建必要的鈴聲組策略。 由于此類分開,因此應用程序可以使用其實例同時處理多個傳入呼叫。
首先,您將需要一個新的Visual C#控制臺應用程序 ,因為該應用程序實際上是“虛擬軟件電話”,因此一個控制臺應用程序就足夠了。 為了更好地理解,我將本文的其余部分分為三個主要部分。 所有這三個部分都逐步介紹了一個類的實現。
現在,已經用盡了理論和發展背景,“保持冷靜,開始編程!” :)
開發/第1部分:構建軟件電話
你準備好了嗎? 當然! :)好吧,讓我們從Softphone.cs類開始。 第一步,使用以下行添加所需的內容。 (作為C#.NET VoIP庫,已經使用了Ozeki VoIP SIP SDK- 請記住,此代碼是使用此SDK的預先編寫的VoIP組件編寫的。因此,如果要嘗試使用它們,則需要安裝它的免費試用版。 [7]) using?System; using?Ozeki.VoIP; using?Ozeki.VoIP.SDK;現在需要軟件電話和電話線對象。 您可以從ISoftPhone和IPhoneLine接口獲取它們,如下所示: ISoftPhone?_softphone; IPhoneLine?_phoneLine;在構造函數中,您需要使用默認參數來初始化此軟件電話。 5000和10000參數是指端口范圍:第一個數字是最小端口號( minPortRange ),另一個是最大端口號( maxPortRange )。 為了實現對來電的持續監控,您需要訂閱IncomingCall事件。 請查看以下上述步驟: public?Softphone(){_softphone?=?SoftPhoneFactory.CreateSoftPhone(5000,?10000);_softphone.IncomingCall?+=?softphone_IncomingCall;}通過訂閱IncomigCall事件,將通知您是否有來電。 如果電話線的狀態發生更改,則PhoneLineStateChange事件(當然會在訂閱后)通知您。 請查看以下上述步驟: public?event?EventHandler<VoIPEventArgs<IPhoneCall>>?IncomigCall; public?event?EventHandler<RegistrationStateChangedArgs>?PhoneLineStateChanged;
通過使用Register方法,可以將軟件電話注冊到PBX。 為此,應創建一個需要SIP帳戶的電話線。 為了能夠為您的軟件電話創建SIP帳戶,您需要指定以下參數:
創建必要的SIP帳戶后,您可以創建電話線以能夠與PBX通信。 還需要收聽電話線狀態的變化。 最后, RegisterPhoneLine方法可用于將電話線注冊到軟件電話。
請查看以下上述步驟: public?void?Register(bool?registrationRequired,?string?displayName,?string?userName,?string?authenticationId,?string?registerPassword,?string?domainHost,?int?domainPort){try{var?account?=?new?SIPAccount(registrationRequired,?displayName,?userName,?authenticationId,?registerPassword,?domainHost,?domainPort);Console.WriteLine("\nCreating?SIP?account?{0}",?account);?_phoneLine?=?_softphone.CreatePhoneLine(account);Console.WriteLine("Phoneline?created.");?_phoneLine.RegistrationStateChanged?+=?_phoneLine_RegistrationStateChanged;?_softphone.RegisterPhoneLine(_phoneLine);}catch?(Exception?ex){Console.WriteLine("Error?during?SIP?registration"?+?ex.ToString());}}電話線的注冊狀態更改時將調用此方法: void?_phoneLine_RegistrationStateChanged(object?sender,?RegistrationStateChangedArgs?e) {var?handler?=?PhoneLineStateChanged;if?(handler?!=?null)handler(this,?e); }這將在有來電時被調用: void?softphone_IncomingCall(object?sender,?VoIPEventArgs<IPhoneCall>?e) {var?handler?=?IncomigCall;if?(handler?!=?null)handler(this,?e); }調用時,可以使用以下代碼片段創建調用對象: public?IPhoneCall?CreateCall(string?member) {return?_softphone.CreateCallObject(_phoneLine,?member); }這是Softphone.cs的實現的結尾。 我了解您是否現在想休息一下。 休息一下,讓我們繼續編碼Program.cs ! :)
開發/第2部分:構建其他功能
完成軟件電話的創建后,讓我們繼續使用Program.cs進行編程。 此類介紹了Softphone對象的用法,處理控制臺事件,與用戶進行交互以及使用Softphone類提供的機會。 (您將看到,所有內容都是在相互通信的單獨方法中處理的。)
在Main方法下面可以看到您需要通過調用初始化方法來初始化軟件電話的位置。 (代碼段末尾的BlockExit方法-不允許應用程序退出。) static?List<RingGroupCallHandler>?_callHandlers; static?List<String>?_members; static?int?_memberCount; static?Softphone?_softphone;? static?void?Main(string[]?args) {_softphone?=?new?Softphone();?ShowHelp();?SipAccountInitialization(_softphone);?_callHandlers?=?new?List<RingGroupCallHandler>();???_softphone.IncomigCall?+=?softphone_IncomigCall;_softphone.PhoneLineStateChanged?+=?softphone_PhoneLineStateChanged;??BlockExit(); }現在,有一個新的軟件電話可以監視電話線的狀態。 您已訂閱以獲取有關電話線狀態已更改的通知。 在下面,您可以看到Softphone_PhoneLineStateChanged方法確定每種狀態的處理方式。 電話線的注冊狀態更改時將調用此方法: static?void?softphone_PhoneLineStateChanged(object?sender,?RegistrationStateChangedArgs?e) {Console.WriteLine("Phone?line?state?changed?to:?{0}",?e.State);?if?(e.State?==?RegState.RegistrationSucceeded)MemberAdder(); }關于該類也訂閱了電話線路的事件這一事實,當電話線路的狀態成功注冊時會通知該類,并且它開始向用戶詢問振鈴組:成員編號,他們的電話號碼。 這些被存儲在字符串列表中: private?static?void?MemberAdder(){_members?=?new?List<String>();?while?(_memberCount?==?0?||?_memberCount?==?null){Console.Write("\nThe?number?of?the?RingGroup?members:?");try{_memberCount?=?Int32.Parse(Console.ReadLine());}catch{Console.WriteLine("Wrong?input!");}}?for?(int?i?=?0;?i?<?_memberCount;?i++){Console.Write("{0}.?member's?phone?number:?",?i?+?1);string?member?=?Console.ReadLine();_members.Add(member);}?Console.WriteLine("\nWaiting?for?incoming?calls...");}當Program.cs通過控制臺窗口與用戶通信時; 值得為用戶提供簡短的介紹信息: static?void?ShowHelp(){Console.WriteLine("Hello!?This?is?a?brief?introduction?to?this?project.");Console.WriteLine("This?project?is?about?to?introduce?the?creation?of?a?RingGroup?(with?the?one?by?one?strategy),?which?works?on?the?following?way:");Console.WriteLine("1.,?the?application?asks?the?user?about?the?number?of?the?RingGroup?members");Console.WriteLine("2.,?also?asks?the?user,?to?enter?the?members'?phone?numbers");Console.WriteLine("3.,?waits?for?an?incoming?call");Console.WriteLine("4.,?starts?to?ring?the?first?RingGroup?on?the?list?of?members");Console.WriteLine("5.,?if?the?member?is?busy?or?cannot?be?reached,?it?starts?to?ring?an?other?member");Console.WriteLine("6.,?if?the?call?is?being?answered,?it?transfers?the?call?and?stops?ringing?the?other?members");Console.WriteLine("-------------------------------------------------------------------------------");Console.WriteLine();}如果有傳入呼叫,則將通知用戶,應用程序將為該呼叫創建一個新的RingGroupCallHandler對象,訂閱其Completed方法,然后將該對象添加到處理程序列表中。 此后,它調用對象的Start方法: static?void?softphone_IncomigCall(object?sender,?VoIPEventArgs<IPhoneCall>?e){Console.WriteLine("\nIncoming?call?from?\"{0}\"!\n",?e.Item.DialInfo.Dialed);?var?callHandler?=?new?RingGroupCallHandler(e.Item,?_softphone,?_members);callHandler.Completed?+=?callHandler_Completed;?lock?(_callHandlers)_callHandlers.Add(callHandler);?callHandler.Start();}當傳入呼叫不再可用時(例如,由于已轉移或呼叫者完成了呼叫等), 應從呼叫處理程序的列表中刪除RingGroupCallHandler對象: static?void?callHandler_Completed(object?sender,?EventArgs?e){lock?(_callHandlers)_callHandlers.Remove((RingGroupCallHandler)sender);}為了能夠使用先前創建的軟件電話進行通信,需要設置一個SIP帳戶 。 因此,應用程序應要求用戶輸入有效的SIP帳戶詳細信息,然后嘗試注冊到PBX。 有關詳細說明,請查看以下代碼庫中的注釋: static?void?SipAccountInitialization(Softphone?softphone){?var?registrationRequired?=?true;Console.WriteLine("\nPlease?set?up?Your?SIP?account:\n");?//?Asks,?if?a?registration?is?required?to?the?PBX.?The?default?value?is?true.Console.Write("Please?set?if?the?registration?is?required?(true/false)?(default:?true):?");var?regRequired?=?Read("Registration?required",?false);if?(regRequired.ToLower()?==?"false"?||?regRequired.ToLower()?==?"no"?||regRequired.ToLower()?==?"n"){registrationRequired?=?false;}else{Console.WriteLine("Registration?set?to?required.");}??//?The?SIP?account?needs?and?authentication?ID,?and?some?names?as?well.Console.Write("Please?set?Your?authentication?ID:?");var?authenticationId?=?Read("Authentication?ID",?true);?//?If?the?user?only?presses?the?Enter?button,?the?username?will?be?the?same?as?the?authentication?IDConsole.Write("Please?set?Your?username?(default:?"?+?authenticationId?+?"):?");var?userName?=?Read("Username",?false);if?(string.IsNullOrEmpty(userName))userName?=?authenticationId;?//?If?the?user?only?presses?the?Enter?button,?the?display?name?will?be?the?same?as?the?authentication?IDConsole.Write("Please?set?Your?name?to?be?displayed?(default:?"?+?authenticationId?+?"):?");var?displayName?=?Read("Display?name",?false);if?(string.IsNullOrEmpty(displayName))displayName?=?authenticationId;?//?The?registration?password?needs?to?be?entered.Console.Write("Please?set?Your?registration?password:?");var?registerPassword?=?Read("Password",?true);?//?Domain?name?as?a?string,?for?example?an?IP?adress.Console.Write("Please?set?the?domain?name:?");var?domainHost?=?Read("Domain?name",?true);?//?Port?number?with?the?as?5060?default?value.Console.Write("Please?set?the?port?number?(default:?5060):?");int?domainPort;string?port?=?Read("Port",?false);if?(string.IsNullOrEmpty(port)){domainPort?=?5060;}else{domainPort?=?Int32.Parse(port);}??Console.WriteLine("\nCreating?SIP?account?and?trying?to?register...\n");softphone.Register(registrationRequired,?displayName,?userName,?authenticationId,?registerPassword,?domainHost,?domainPort);?}之后,需要一種幫助方法來讀取輸入: private?static?string?Read(string?inputName,?bool?readWhileEmpty){while?(true){string?input?=?Console.ReadLine();?if?(!readWhileEmpty){return?input;}?if?(!string.IsNullOrEmpty(input)){return?input;}?Console.WriteLine(inputName?+?"?cannot?be?empty!");Console.WriteLine(inputName?+?":?");}}最后,您需要使用BlockExit方法。 這不會讓應用程序退出: private?static?void?BlockExit(){while?(true){Thread.Sleep(10);}}是的,現在我們也可以使用Softphone.cs和Program.cs 。 如果需要,請在開始最后一個主要部分之前稍作休息! :)
開發/第3部分:使用“一對一”策略構建環網組
現在是最后一個主要部分,它演示了RingGroupCallHandler類的實現。 此類負責等待傳入的呼叫,開始根據首選的振鈴組策略呼叫振鈴組的成員以及將呼叫轉移到適當的成員。
來電時應調用Start方法。 Start方法接受呼叫,然后通過調用StartOutgoingCalls方法開始通知振鈴組成員。 IPhoneCall?_incomingCall; Softphone?_softPhone; List<IPhoneCall>?_calls; ist<String>?_members;? object?_sync;? public?RingGroupCallHandler(IPhoneCall?incomingCall,?Softphone?softPhone,?List<String>?members) {_sync?=?new?object();_incomingCall?=?incomingCall;_softPhone?=?softPhone;_members?=?members;?_calls?=?new?List<IPhoneCall>(); }? public?event?EventHandler<VoIPEventArgs<CallError>>?CallErrorOccured; public?event?EventHandler?Completed;? public?void?Start() {_incomingCall.Answer();_incomingCall.CallStateChanged?+=?call_CallStateChanged;StartOutgoingCalls(); }該StartOutgoingCalls方法創建呼叫對象到每個構件和CallSequencer方法將被調用。 需要訂閱CallStateChanged事件。 事件發生時,應用程序將調用OutgoingCallStateChanged方法。 (當接聽來電時將使用它,并且應將其轉移到振鈴組中的座席。) void?StartOutgoingCalls(){foreach?(var?member?in?_members){var?call?=?_softPhone.CreateCall(member);call.CallStateChanged?+=?OutgoingCallStateChanged;_calls.Add(call);}?CallSequencer();}如果呼叫列表中有呼叫,則CallSequencer方法會逐個振鈴成員(從列表中的第一個成員開始)。 如果呼叫列表中沒有任何呼叫,則該方法掛斷來電: private?void?CallSequencer(){if?(_calls.Count?>?0){var?call?=?_calls[0];Console.WriteLine("Ringing?phone?number?\"{0}\"",?call.DialInfo.Dialed);call.Start();}else{Console.WriteLine("No?available?RingGroup?member?has?been?found.");_incomingCall.HangUp();}}OutgoingCallStateChanged方法通過使用AttendedTransfer方法將該呼叫者(即傳入呼叫)轉移到環組成員之一。 OutgoingCallStateChanged方法還負責從調用列表中刪除該調用,然后調用OnCompleted方法。 當被叫忙或無法接通時 ,應用程序將再次調用CallSequencer方法。 void?OutgoingCallStateChanged(object?sender,?CallStateChangedArgs?e){var?call?=?(IPhoneCall)sender;?if?(e.State?==?CallState.Answered){Console.WriteLine("\nCall?has?been?accepted?by?{0}.",?call.DialInfo.Dialed);Console.WriteLine("Call?from?\"{0}\"?is?being?transferred?to?\"{1}\".\n",?_incomingCall.DialInfo.Dialed,?call.DialInfo.Dialed);_incomingCall.AttendedTransfer(call);?lock?(_sync){_calls.Remove(call);OnCompleted();}}?if?(e.State?==?CallState.Busy?||?e.State?==?CallState.Error){lock?(_sync){if?(call?!=?null){Console.WriteLine("Ringing?phone?number?\"{0}\"?ends.",?call.DialInfo.Dialed);call.HangUp();?_calls.Remove(call);CallSequencer();}}}}應答呼叫并將其轉移到振鈴組成員后,將調用OnCompleted方法。 OnCompleted方法調用HangupOutgoingCalls方法,并設置Completed事件。 這表明呼叫轉移已成功,或者呼叫方已在轉移之前掛斷了呼叫。 Completed事件還通知應用程序,呼叫處理程序無事可做。 (當傳入呼叫在傳輸之前完成時,也會調用OnCompleted方法。) void?call_CallStateChanged(object?sender,?CallStateChangedArgs?e) {if?(e.State.IsCallEnded()){OnCompleted();} }? void?OnCompleted() {HangupOutgoingCalls();?var?handler?=?Completed;if?(handler?!=?null)handler(this,?EventArgs.Empty); }HangupOutgoingCalls方法用于掛斷所有呼出電話并將其從列表中清除: void?HangupOutgoingCalls() {foreach?(var?call?in?_calls){call.HangUp();}_calls.Clear(); }? void?call_CallErrorOccured(object?sender,?VoIPEventArgs<CallError>?e) {Console.WriteLine("Error?occured?at?{0}:?{1}",?((IPhoneCall)sender).DialInfo.Dialed,?e.Item);?var?handler?=?CallErrorOccured;if?(handler?!=?null)handler(this,?e); }這就是開發的終點! 祝賀您的新申請! 你的工作快結束了。 現在,您只需要使用新的虛擬軟件電話來測試振鈴組。
測試
讓我們運行該應用程序以測試新虛擬虛擬電話的振鈴組功能。 請注意, 某些SIP帳戶 (無論是臺式VoIP電話,軟件電話還是移動分機)應事先安裝在PBX中,以便能夠將成員添加到環組中。 例如,我在PBX中注冊了三個SIP帳戶(1000、2000、3000)。 (兩個將成為一個環網組成員,而我將使用第三個來進行測試通話。)
按F5后,可以在控制臺應用程序中看到以前編寫的介紹 。 介紹之后,該應用程序會要求您設置您的SIP帳戶 ( 圖2 )。
圖2:環組作為控制臺應用程序運行后
現在,您需要配置虛擬軟件電話和振鈴組本身。 首先, 通過輸入“ true”將注冊設置為必填 ( 圖3 )。 此后,您需要在PBX中創建一個新的SIP帳戶,該帳戶將用作虛擬軟件電話分機以用于振鈴組(在我的情況下,這是編號為“ 4000”的SIP帳戶)。 現在,通過輸入所需的數據,在新的控制臺應用程序中提供相同的SIP帳戶詳細信息 。 域名是集團電話的IP地址, 端口號是它的端口號(通常是5060)。 提供必要的SIP帳戶詳細信息后,應用程序將嘗試注冊到PBX 。 如果提供的信息正確,則電話線狀態將更改為RegistrationSucceeded 。 此后,應用程序要求您輸入環組成員的數量并定義一個序列 。 此順序確定當某人撥打響鈴組的電話號碼時哪個響鈴(即哪個電話)將響鈴。 (應用程序會將來電轉移到第一個成員。如果該呼叫不可用,則呼叫將轉發到第二個成員,依此類推。)現在,配置完成,應用程序正在等待來電 ( 圖3 )。
圖3:環組控制臺應用程序運行后
我的測試電話可以在下面看到( 圖4 )。 我用“ 3000”打了一個電話。 我撥打了“ 4000”(這是振鈴組的電話號碼–它可以用作公司的中央電話號碼)。 此后,第一個振鈴組成員,編號為“ 2000”的分機開始振鈴。 但是,此分機忙,因此第二個成員“ 1000”開始響鈴。 此分機已接受呼叫,因此來電已從“ 4000”(虛擬軟件電話)轉移到“ 1000”(臺式VoIP電話)。
圖4:測試電話
這意味著您的應用程序運行良好! 恭喜您成功! :)
結論
在當今瞬息萬變的世界中,公司在所有業務領域中也都需要最新的軟件和硬件設備,因此在與客戶和其他合作伙伴進行交流時也是如此。 呼叫中心應配備有助于自動化的有效解決方案。 如果使用VoIP技術(和IP PBX),則呼叫轉移,振鈴組,呼叫記錄,呼叫排隊,電話會議等只是可以實現的一些重要功能。 在本教程中,我想通過振鈴組的示例來說明,自己開發缺少的功能以改善呼叫中心有多么容易。 我希望你喜歡它!
進一步的閱讀和參考
為了撰寫有關構建環網組的C#教程,我使用了以下知識庫: http://en.wikipedia.org/wiki/Call_centre http://searchcrm.techtarget.com/definition/call-center http://www.imdb.com/title/tt1593756/...f_=tt_pv_mi_sm http://elderlymedicalalertsystems.co...-alert-systems http://en.wikipedia.org/wiki/Extension_%28telephone%29 http://en.wikipedia.org/wiki/Call_transfer http://voip-sip-sdk.com/p_21-downloa...-sdk-voip.html 附加圖片
下載源代碼:
Softphone-RingGroup-RingOneByOne.zip
內容
- 主題的重要性
- 有關呼叫中心的技術背景的更多信息
- 環組開發簡介
- 開發/第1部分:構建軟件電話
- 開發/第2部分:構建其他功能
- 開發/第3部分:使用“一對一”策略構建環網組
- 測試
- 結論
- 進一步的閱讀和參考
主題的重要性
呼叫中心或呼叫中心是一個集中式辦公室,用于通過電話接收或傳輸大量請求[1]。 換句話說:“呼叫中心是一個物理場所,通常由一定數量的計算機自動化來由組織來處理客戶和其他電話” [2]。 先前定義中的“ 通常具有一定程度的計算機自動化 ”部分表示,成功的呼叫中心基于人工和某種技術設備的合作。 因此,適當的IT基礎架構至關重要。
專家們說, 精心挑選員工是成功建立呼叫中心的第一步。 但是大多數技能都是可以學習的。 在第一線支持,服務臺和服務臺培訓期間 ,講師經常提請操作員注意以下幾點:
- 要有禮貌和禮貌
- 主動
- 首先把事情放在首位–讓優先事項推動舉措
- 雙贏思考–對您的客戶有利的對您也有利,等等。
這些是成功呼叫中心必不可少的非常重要的人為因素。 另外,某些管理工具,例如績效評估和對員工的定期反饋,獎勵,積極的強化等,是有效呼叫中心不可或缺的要素。 但是如果沒有適當的技術,所有人類知識都是徒勞的。 無論是小型呼叫中心還是大型呼叫中心,要獲得更多利潤并建立客戶忠誠度,員工都必須使用先進的硬件和軟件設備 。
在外包的印度呼叫中心和大型的美國呼叫中心中,有什么共同點( 圖1 )? 是的,有很多東西,但是最重要的是核心技術。 在這兩個地方,運營商都使用VoIP臺式電話,軟電話,頭戴式耳機,以及:具有典型呼叫中心功能(例如,呼叫轉移,振鈴組,呼叫記錄,呼叫排隊,電話會議等)的高級PBX系統,可以使某些任務更多由于自動化,操作簡單。
圖1:技術設備也是小型和大型呼叫中心的關鍵因素[3]
有關呼叫中心的技術背景的更多信息
呼叫中心可以同時處理大量呼叫 ,并將其轉發給負責處理這些呼叫的人員。 但是,在當今快速發展的世界中,VoIP技術,高性能計算機和智能軟件應用程序幾乎是必不可少的。 如今的呼叫中心使用的是比老式的巨大“盒子”更多的最新設備 ,而老式的“盒子”卻有很多電線伸出來。
我們可以將呼叫中心分為兩種主要類型,如下所示[1]:
- 虛擬呼叫中心:在這種呼叫中心模型中,運營商通常向在其自己的數據中心托管呼叫中心電話設備的供應商支付月費或年費。 代理商通過傳統的PSTN電話線或VoIP(互聯網協議語音)連接到供應商的設備。
- 基于前提的呼叫中心:此模型與先前的模型相反。 在這種情況下,呼叫中心已建立在PBX(專用分支交換)中,該交換機由呼叫中心運營商自己擁有,托管和維護。 該PBX提供高級功能,例如呼叫排隊,ACD(自動呼叫分配),IVR(交互式語音響應)或SBR(基于技能的路由)–這是一種呼叫輔助策略,用于將呼入呼叫分配給最適合的座席,而不是簡單地選擇下一個可用的代理)。
如果使用虛擬呼叫中心,則功能是固定的,不能無限擴展它們。 但是,在基于內部的呼叫中心的情況下,您將有機會通過開發所需的功能來改進系統。 實際上,只有想象力(當然還有公司的需求和條件)限制了這種改進。
環組開發簡介
在本文中,您將看到后一種情況的示例。 確切地說,本指南逐步演示了環組的創建。 該鈴聲組將是安裝在集團電話中的“虛擬” 分機 。 此分機基于軟件電話 (因此,即已注冊到PBX)。 PBX為該分機分配電話號碼。 呼叫時,該分機嘗試將來電轉移到其他分機之一(即轉移到先前已在PBX上注冊的振鈴組成員之一)。 振鈴組策略確定哪個成員將接受呼叫。
恐怕以前的線程包含一些新的(或令人恐懼的)表達式……不管是不是,為了完整起見,讓我澄清一下此解決方案的基本要素。
- 什么是振鈴組:在現代電信時代,呼叫路由允許在有來電時有多個分機振鈴。 它稱為環組。 環組通常在PBX中顯示為“虛擬分機”。 在配置振鈴組時,可以定義哪些電話分機屬于該振鈴組。 這樣,當有“虛擬分機”的來電時,所有所屬分機將在振鈴組內同時或依次振鈴。
- “分機”的含義是什么:在商務電話中,電話分機可能是指與PBX相連的內部電話網絡中的電話。 在集團電話內,用戶僅需撥打分機號碼即可直接與其他任何用戶聯系。 對于呼入電話,撥打公司電話號碼后,總機接線員或自動值班人員可以要求首選分機的號碼。 如果將外部號碼分配給各個分機[5],則也可以通過直接呼入完成呼叫。
- 什么是軟件電話:軟件電話是一種特殊的計算機軟件,可以充當虛擬電話,使人們可以使用其PC或筆記本電腦通過Internet與他人進行通信。
- 什么是呼叫轉移:呼叫轉移是一種電信機制,允許用戶通過使用轉移按鈕或開關掛鉤并撥打所需的位置[6],將現有電話轉移到另一個電話或話務臺。
- 什么是振鈴組策略:振鈴組策略確定在有來電時如何通知組成員的方式。 在呼叫轉移之前,環網組需要通知組成員需要進行呼叫轉移。 要選擇在振鈴組內接受呼叫的成員,可以使用幾種策略。 讓我們看一些示例:可以配置為同時呼叫振鈴組中的所有分機(即所有成員的電話)。 還可以實現的是,環群分機將以預先設置的順序一一呼叫成員。 另一種常用的策略是當環組的延伸以隨機順序被調用時的情況。 (在該項目中,將演示“一對一”策略。)
基于以上內容,讓我們總結一下該項目需要哪些開發:
- 構建能夠接收電話的虛擬(或控制臺)軟件電話
- 構建所需的附加功能,例如呼叫轉移和多個呼叫管理
- 用“一對一”策略建立環網組
因此,我使用了以下3類 :
首先,您將需要一個新的Visual C#控制臺應用程序 ,因為該應用程序實際上是“虛擬軟件電話”,因此一個控制臺應用程序就足夠了。 為了更好地理解,我將本文的其余部分分為三個主要部分。 所有這三個部分都逐步介紹了一個類的實現。
現在,已經用盡了理論和發展背景,“保持冷靜,開始編程!” :)
開發/第1部分:構建軟件電話
你準備好了嗎? 當然! :)好吧,讓我們從Softphone.cs類開始。 第一步,使用以下行添加所需的內容。 (作為C#.NET VoIP庫,已經使用了Ozeki VoIP SIP SDK- 請記住,此代碼是使用此SDK的預先編寫的VoIP組件編寫的。因此,如果要嘗試使用它們,則需要安裝它的免費試用版。 [7]) using?System; using?Ozeki.VoIP; using?Ozeki.VoIP.SDK;現在需要軟件電話和電話線對象。 您可以從ISoftPhone和IPhoneLine接口獲取它們,如下所示: ISoftPhone?_softphone; IPhoneLine?_phoneLine;在構造函數中,您需要使用默認參數來初始化此軟件電話。 5000和10000參數是指端口范圍:第一個數字是最小端口號( minPortRange ),另一個是最大端口號( maxPortRange )。 為了實現對來電的持續監控,您需要訂閱IncomingCall事件。 請查看以下上述步驟: public?Softphone(){_softphone?=?SoftPhoneFactory.CreateSoftPhone(5000,?10000);_softphone.IncomingCall?+=?softphone_IncomingCall;}通過訂閱IncomigCall事件,將通知您是否有來電。 如果電話線的狀態發生更改,則PhoneLineStateChange事件(當然會在訂閱后)通知您。 請查看以下上述步驟: public?event?EventHandler<VoIPEventArgs<IPhoneCall>>?IncomigCall; public?event?EventHandler<RegistrationStateChangedArgs>?PhoneLineStateChanged;
通過使用Register方法,可以將軟件電話注冊到PBX。 為此,應創建一個需要SIP帳戶的電話線。 為了能夠為您的軟件電話創建SIP帳戶,您需要指定以下參數:
- registrationRequired:為了能夠接收來電,您需要為此參數設置“ true”值。
- displayName:這是要在被叫客戶端上顯示的名稱。
- userName:這是要呼叫此軟件電話的其他客戶端要撥打的號碼。
- authenticationId:這是集團電話的標識符(如登錄名)。
- registerPassword:這是用于注冊到PBX的authenticationId的密碼。
- domainHost:這是一個域名,即您要注冊的集團電話的IP地址。
- domainPort:這是您要注冊的集團電話的端口號。
創建必要的SIP帳戶后,您可以創建電話線以能夠與PBX通信。 還需要收聽電話線狀態的變化。 最后, RegisterPhoneLine方法可用于將電話線注冊到軟件電話。
請查看以下上述步驟: public?void?Register(bool?registrationRequired,?string?displayName,?string?userName,?string?authenticationId,?string?registerPassword,?string?domainHost,?int?domainPort){try{var?account?=?new?SIPAccount(registrationRequired,?displayName,?userName,?authenticationId,?registerPassword,?domainHost,?domainPort);Console.WriteLine("\nCreating?SIP?account?{0}",?account);?_phoneLine?=?_softphone.CreatePhoneLine(account);Console.WriteLine("Phoneline?created.");?_phoneLine.RegistrationStateChanged?+=?_phoneLine_RegistrationStateChanged;?_softphone.RegisterPhoneLine(_phoneLine);}catch?(Exception?ex){Console.WriteLine("Error?during?SIP?registration"?+?ex.ToString());}}電話線的注冊狀態更改時將調用此方法: void?_phoneLine_RegistrationStateChanged(object?sender,?RegistrationStateChangedArgs?e) {var?handler?=?PhoneLineStateChanged;if?(handler?!=?null)handler(this,?e); }這將在有來電時被調用: void?softphone_IncomingCall(object?sender,?VoIPEventArgs<IPhoneCall>?e) {var?handler?=?IncomigCall;if?(handler?!=?null)handler(this,?e); }調用時,可以使用以下代碼片段創建調用對象: public?IPhoneCall?CreateCall(string?member) {return?_softphone.CreateCallObject(_phoneLine,?member); }這是Softphone.cs的實現的結尾。 我了解您是否現在想休息一下。 休息一下,讓我們繼續編碼Program.cs ! :)
開發/第2部分:構建其他功能
完成軟件電話的創建后,讓我們繼續使用Program.cs進行編程。 此類介紹了Softphone對象的用法,處理控制臺事件,與用戶進行交互以及使用Softphone類提供的機會。 (您將看到,所有內容都是在相互通信的單獨方法中處理的。)
在Main方法下面可以看到您需要通過調用初始化方法來初始化軟件電話的位置。 (代碼段末尾的BlockExit方法-不允許應用程序退出。) static?List<RingGroupCallHandler>?_callHandlers; static?List<String>?_members; static?int?_memberCount; static?Softphone?_softphone;? static?void?Main(string[]?args) {_softphone?=?new?Softphone();?ShowHelp();?SipAccountInitialization(_softphone);?_callHandlers?=?new?List<RingGroupCallHandler>();???_softphone.IncomigCall?+=?softphone_IncomigCall;_softphone.PhoneLineStateChanged?+=?softphone_PhoneLineStateChanged;??BlockExit(); }現在,有一個新的軟件電話可以監視電話線的狀態。 您已訂閱以獲取有關電話線狀態已更改的通知。 在下面,您可以看到Softphone_PhoneLineStateChanged方法確定每種狀態的處理方式。 電話線的注冊狀態更改時將調用此方法: static?void?softphone_PhoneLineStateChanged(object?sender,?RegistrationStateChangedArgs?e) {Console.WriteLine("Phone?line?state?changed?to:?{0}",?e.State);?if?(e.State?==?RegState.RegistrationSucceeded)MemberAdder(); }關于該類也訂閱了電話線路的事件這一事實,當電話線路的狀態成功注冊時會通知該類,并且它開始向用戶詢問振鈴組:成員編號,他們的電話號碼。 這些被存儲在字符串列表中: private?static?void?MemberAdder(){_members?=?new?List<String>();?while?(_memberCount?==?0?||?_memberCount?==?null){Console.Write("\nThe?number?of?the?RingGroup?members:?");try{_memberCount?=?Int32.Parse(Console.ReadLine());}catch{Console.WriteLine("Wrong?input!");}}?for?(int?i?=?0;?i?<?_memberCount;?i++){Console.Write("{0}.?member's?phone?number:?",?i?+?1);string?member?=?Console.ReadLine();_members.Add(member);}?Console.WriteLine("\nWaiting?for?incoming?calls...");}當Program.cs通過控制臺窗口與用戶通信時; 值得為用戶提供簡短的介紹信息: static?void?ShowHelp(){Console.WriteLine("Hello!?This?is?a?brief?introduction?to?this?project.");Console.WriteLine("This?project?is?about?to?introduce?the?creation?of?a?RingGroup?(with?the?one?by?one?strategy),?which?works?on?the?following?way:");Console.WriteLine("1.,?the?application?asks?the?user?about?the?number?of?the?RingGroup?members");Console.WriteLine("2.,?also?asks?the?user,?to?enter?the?members'?phone?numbers");Console.WriteLine("3.,?waits?for?an?incoming?call");Console.WriteLine("4.,?starts?to?ring?the?first?RingGroup?on?the?list?of?members");Console.WriteLine("5.,?if?the?member?is?busy?or?cannot?be?reached,?it?starts?to?ring?an?other?member");Console.WriteLine("6.,?if?the?call?is?being?answered,?it?transfers?the?call?and?stops?ringing?the?other?members");Console.WriteLine("-------------------------------------------------------------------------------");Console.WriteLine();}如果有傳入呼叫,則將通知用戶,應用程序將為該呼叫創建一個新的RingGroupCallHandler對象,訂閱其Completed方法,然后將該對象添加到處理程序列表中。 此后,它調用對象的Start方法: static?void?softphone_IncomigCall(object?sender,?VoIPEventArgs<IPhoneCall>?e){Console.WriteLine("\nIncoming?call?from?\"{0}\"!\n",?e.Item.DialInfo.Dialed);?var?callHandler?=?new?RingGroupCallHandler(e.Item,?_softphone,?_members);callHandler.Completed?+=?callHandler_Completed;?lock?(_callHandlers)_callHandlers.Add(callHandler);?callHandler.Start();}當傳入呼叫不再可用時(例如,由于已轉移或呼叫者完成了呼叫等), 應從呼叫處理程序的列表中刪除RingGroupCallHandler對象: static?void?callHandler_Completed(object?sender,?EventArgs?e){lock?(_callHandlers)_callHandlers.Remove((RingGroupCallHandler)sender);}為了能夠使用先前創建的軟件電話進行通信,需要設置一個SIP帳戶 。 因此,應用程序應要求用戶輸入有效的SIP帳戶詳細信息,然后嘗試注冊到PBX。 有關詳細說明,請查看以下代碼庫中的注釋: static?void?SipAccountInitialization(Softphone?softphone){?var?registrationRequired?=?true;Console.WriteLine("\nPlease?set?up?Your?SIP?account:\n");?//?Asks,?if?a?registration?is?required?to?the?PBX.?The?default?value?is?true.Console.Write("Please?set?if?the?registration?is?required?(true/false)?(default:?true):?");var?regRequired?=?Read("Registration?required",?false);if?(regRequired.ToLower()?==?"false"?||?regRequired.ToLower()?==?"no"?||regRequired.ToLower()?==?"n"){registrationRequired?=?false;}else{Console.WriteLine("Registration?set?to?required.");}??//?The?SIP?account?needs?and?authentication?ID,?and?some?names?as?well.Console.Write("Please?set?Your?authentication?ID:?");var?authenticationId?=?Read("Authentication?ID",?true);?//?If?the?user?only?presses?the?Enter?button,?the?username?will?be?the?same?as?the?authentication?IDConsole.Write("Please?set?Your?username?(default:?"?+?authenticationId?+?"):?");var?userName?=?Read("Username",?false);if?(string.IsNullOrEmpty(userName))userName?=?authenticationId;?//?If?the?user?only?presses?the?Enter?button,?the?display?name?will?be?the?same?as?the?authentication?IDConsole.Write("Please?set?Your?name?to?be?displayed?(default:?"?+?authenticationId?+?"):?");var?displayName?=?Read("Display?name",?false);if?(string.IsNullOrEmpty(displayName))displayName?=?authenticationId;?//?The?registration?password?needs?to?be?entered.Console.Write("Please?set?Your?registration?password:?");var?registerPassword?=?Read("Password",?true);?//?Domain?name?as?a?string,?for?example?an?IP?adress.Console.Write("Please?set?the?domain?name:?");var?domainHost?=?Read("Domain?name",?true);?//?Port?number?with?the?as?5060?default?value.Console.Write("Please?set?the?port?number?(default:?5060):?");int?domainPort;string?port?=?Read("Port",?false);if?(string.IsNullOrEmpty(port)){domainPort?=?5060;}else{domainPort?=?Int32.Parse(port);}??Console.WriteLine("\nCreating?SIP?account?and?trying?to?register...\n");softphone.Register(registrationRequired,?displayName,?userName,?authenticationId,?registerPassword,?domainHost,?domainPort);?}之后,需要一種幫助方法來讀取輸入: private?static?string?Read(string?inputName,?bool?readWhileEmpty){while?(true){string?input?=?Console.ReadLine();?if?(!readWhileEmpty){return?input;}?if?(!string.IsNullOrEmpty(input)){return?input;}?Console.WriteLine(inputName?+?"?cannot?be?empty!");Console.WriteLine(inputName?+?":?");}}最后,您需要使用BlockExit方法。 這不會讓應用程序退出: private?static?void?BlockExit(){while?(true){Thread.Sleep(10);}}是的,現在我們也可以使用Softphone.cs和Program.cs 。 如果需要,請在開始最后一個主要部分之前稍作休息! :)
開發/第3部分:使用“一對一”策略構建環網組
現在是最后一個主要部分,它演示了RingGroupCallHandler類的實現。 此類負責等待傳入的呼叫,開始根據首選的振鈴組策略呼叫振鈴組的成員以及將呼叫轉移到適當的成員。
來電時應調用Start方法。 Start方法接受呼叫,然后通過調用StartOutgoingCalls方法開始通知振鈴組成員。 IPhoneCall?_incomingCall; Softphone?_softPhone; List<IPhoneCall>?_calls; ist<String>?_members;? object?_sync;? public?RingGroupCallHandler(IPhoneCall?incomingCall,?Softphone?softPhone,?List<String>?members) {_sync?=?new?object();_incomingCall?=?incomingCall;_softPhone?=?softPhone;_members?=?members;?_calls?=?new?List<IPhoneCall>(); }? public?event?EventHandler<VoIPEventArgs<CallError>>?CallErrorOccured; public?event?EventHandler?Completed;? public?void?Start() {_incomingCall.Answer();_incomingCall.CallStateChanged?+=?call_CallStateChanged;StartOutgoingCalls(); }該StartOutgoingCalls方法創建呼叫對象到每個構件和CallSequencer方法將被調用。 需要訂閱CallStateChanged事件。 事件發生時,應用程序將調用OutgoingCallStateChanged方法。 (當接聽來電時將使用它,并且應將其轉移到振鈴組中的座席。) void?StartOutgoingCalls(){foreach?(var?member?in?_members){var?call?=?_softPhone.CreateCall(member);call.CallStateChanged?+=?OutgoingCallStateChanged;_calls.Add(call);}?CallSequencer();}如果呼叫列表中有呼叫,則CallSequencer方法會逐個振鈴成員(從列表中的第一個成員開始)。 如果呼叫列表中沒有任何呼叫,則該方法掛斷來電: private?void?CallSequencer(){if?(_calls.Count?>?0){var?call?=?_calls[0];Console.WriteLine("Ringing?phone?number?\"{0}\"",?call.DialInfo.Dialed);call.Start();}else{Console.WriteLine("No?available?RingGroup?member?has?been?found.");_incomingCall.HangUp();}}OutgoingCallStateChanged方法通過使用AttendedTransfer方法將該呼叫者(即傳入呼叫)轉移到環組成員之一。 OutgoingCallStateChanged方法還負責從調用列表中刪除該調用,然后調用OnCompleted方法。 當被叫忙或無法接通時 ,應用程序將再次調用CallSequencer方法。 void?OutgoingCallStateChanged(object?sender,?CallStateChangedArgs?e){var?call?=?(IPhoneCall)sender;?if?(e.State?==?CallState.Answered){Console.WriteLine("\nCall?has?been?accepted?by?{0}.",?call.DialInfo.Dialed);Console.WriteLine("Call?from?\"{0}\"?is?being?transferred?to?\"{1}\".\n",?_incomingCall.DialInfo.Dialed,?call.DialInfo.Dialed);_incomingCall.AttendedTransfer(call);?lock?(_sync){_calls.Remove(call);OnCompleted();}}?if?(e.State?==?CallState.Busy?||?e.State?==?CallState.Error){lock?(_sync){if?(call?!=?null){Console.WriteLine("Ringing?phone?number?\"{0}\"?ends.",?call.DialInfo.Dialed);call.HangUp();?_calls.Remove(call);CallSequencer();}}}}應答呼叫并將其轉移到振鈴組成員后,將調用OnCompleted方法。 OnCompleted方法調用HangupOutgoingCalls方法,并設置Completed事件。 這表明呼叫轉移已成功,或者呼叫方已在轉移之前掛斷了呼叫。 Completed事件還通知應用程序,呼叫處理程序無事可做。 (當傳入呼叫在傳輸之前完成時,也會調用OnCompleted方法。) void?call_CallStateChanged(object?sender,?CallStateChangedArgs?e) {if?(e.State.IsCallEnded()){OnCompleted();} }? void?OnCompleted() {HangupOutgoingCalls();?var?handler?=?Completed;if?(handler?!=?null)handler(this,?EventArgs.Empty); }HangupOutgoingCalls方法用于掛斷所有呼出電話并將其從列表中清除: void?HangupOutgoingCalls() {foreach?(var?call?in?_calls){call.HangUp();}_calls.Clear(); }? void?call_CallErrorOccured(object?sender,?VoIPEventArgs<CallError>?e) {Console.WriteLine("Error?occured?at?{0}:?{1}",?((IPhoneCall)sender).DialInfo.Dialed,?e.Item);?var?handler?=?CallErrorOccured;if?(handler?!=?null)handler(this,?e); }這就是開發的終點! 祝賀您的新申請! 你的工作快結束了。 現在,您只需要使用新的虛擬軟件電話來測試振鈴組。
測試
讓我們運行該應用程序以測試新虛擬虛擬電話的振鈴組功能。 請注意, 某些SIP帳戶 (無論是臺式VoIP電話,軟件電話還是移動分機)應事先安裝在PBX中,以便能夠將成員添加到環組中。 例如,我在PBX中注冊了三個SIP帳戶(1000、2000、3000)。 (兩個將成為一個環網組成員,而我將使用第三個來進行測試通話。)
按F5后,可以在控制臺應用程序中看到以前編寫的介紹 。 介紹之后,該應用程序會要求您設置您的SIP帳戶 ( 圖2 )。
圖2:環組作為控制臺應用程序運行后
現在,您需要配置虛擬軟件電話和振鈴組本身。 首先, 通過輸入“ true”將注冊設置為必填 ( 圖3 )。 此后,您需要在PBX中創建一個新的SIP帳戶,該帳戶將用作虛擬軟件電話分機以用于振鈴組(在我的情況下,這是編號為“ 4000”的SIP帳戶)。 現在,通過輸入所需的數據,在新的控制臺應用程序中提供相同的SIP帳戶詳細信息 。 域名是集團電話的IP地址, 端口號是它的端口號(通常是5060)。 提供必要的SIP帳戶詳細信息后,應用程序將嘗試注冊到PBX 。 如果提供的信息正確,則電話線狀態將更改為RegistrationSucceeded 。 此后,應用程序要求您輸入環組成員的數量并定義一個序列 。 此順序確定當某人撥打響鈴組的電話號碼時哪個響鈴(即哪個電話)將響鈴。 (應用程序會將來電轉移到第一個成員。如果該呼叫不可用,則呼叫將轉發到第二個成員,依此類推。)現在,配置完成,應用程序正在等待來電 ( 圖3 )。
圖3:環組控制臺應用程序運行后
我的測試電話可以在下面看到( 圖4 )。 我用“ 3000”打了一個電話。 我撥打了“ 4000”(這是振鈴組的電話號碼–它可以用作公司的中央電話號碼)。 此后,第一個振鈴組成員,編號為“ 2000”的分機開始振鈴。 但是,此分機忙,因此第二個成員“ 1000”開始響鈴。 此分機已接受呼叫,因此來電已從“ 4000”(虛擬軟件電話)轉移到“ 1000”(臺式VoIP電話)。
圖4:測試電話
這意味著您的應用程序運行良好! 恭喜您成功! :)
結論
在當今瞬息萬變的世界中,公司在所有業務領域中也都需要最新的軟件和硬件設備,因此在與客戶和其他合作伙伴進行交流時也是如此。 呼叫中心應配備有助于自動化的有效解決方案。 如果使用VoIP技術(和IP PBX),則呼叫轉移,振鈴組,呼叫記錄,呼叫排隊,電話會議等只是可以實現的一些重要功能。 在本教程中,我想通過振鈴組的示例來說明,自己開發缺少的功能以改善呼叫中心有多么容易。 我希望你喜歡它!
進一步的閱讀和參考
為了撰寫有關構建環網組的C#教程,我使用了以下知識庫:
| 1-call-center-little-outsourced-vs-large-call-centres.jpg (50.4 KB,2801瀏覽) | |
| 7-call-center-voip-ring-group-test.jpg (53.8 KB,1217視圖) | |
| 8-call-中心-voip-ring-group-test-setup-sip-account-ring-group-members.jpg (52.3 KB,1400視圖) | |
| 9-call-center-voip-ring-group-test-call.jpg (26.3 KB,1182視圖) |
From: https://bytes.com/topic/c-sharp/insights/960550-building-virtual-softphone-c-works-ring-group-your-call-center
總結
以上是生活随笔為你收集整理的在C#中构建一个虚拟软件电话,该软件电话可以在您的呼叫中心中作为振铃组...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 志宇-Jenkins学习
- 下一篇: 基于改进NSGA-Ⅱ算法的开关磁阻电机再