在C#中构建一个虚拟软件电话,该软件电话可以在您的呼叫中心中作为振铃组...
生活随笔
收集整理的這篇文章主要介紹了
在C#中构建一个虚拟软件电话,该软件电话可以在您的呼叫中心中作为振铃组...
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
本文重點介紹C#中環(huán)組的開發(fā)。 任何呼叫中心的有效性不僅取決于運營商的行為,還取決于呼叫中心的技術(shù)背景。 學(xué)習(xí)完本指南后,您將能夠創(chuàng)建振鈴組分機(即“虛擬軟件電話”),該分機可用于根據(jù)預(yù)定義的振鈴組策略將傳入呼叫轉(zhuǎn)移到選定的呼叫中心座席之一。
下載源代碼:
Softphone-RingGroup-RingOneByOne.zip
內(nèi)容
主題的重要性
呼叫中心或呼叫中心是一個集中式辦公室,用于通過電話接收或傳輸大量請求[1]。 換句話說:“呼叫中心是一個物理場所,通常由一定數(shù)量的計算機自動化來由組織來處理客戶和其他電話” [2]。 先前定義中的“ 通常具有一定程度的計算機自動化 ”部分表示,成功的呼叫中心基于人工和某種技術(shù)設(shè)備的合作。 因此,適當(dāng)?shù)腎T基礎(chǔ)架構(gòu)至關(guān)重要。
專家們說, 精心挑選員工是成功建立呼叫中心的第一步。 但是大多數(shù)技能都是可以學(xué)習(xí)的。 在第一線支持,服務(wù)臺和服務(wù)臺培訓(xùn)期間 ,講師經(jīng)常提請操作員注意以下幾點:
這些是成功呼叫中心必不可少的非常重要的人為因素。 另外,某些管理工具,例如績效評估和對員工的定期反饋,獎勵,積極的強化等,是有效呼叫中心不可或缺的要素。 但是如果沒有適當(dāng)?shù)募夹g(shù),所有人類知識都是徒勞的。 無論是小型呼叫中心還是大型呼叫中心,要獲得更多利潤并建立客戶忠誠度,員工都必須使用先進的硬件和軟件設(shè)備 。
在外包的印度呼叫中心和大型的美國呼叫中心中,有什么共同點( 圖1 )? 是的,有很多東西,但是最重要的是核心技術(shù)。 在這兩個地方,運營商都使用VoIP臺式電話,軟電話,頭戴式耳機,以及:具有典型呼叫中心功能(例如,呼叫轉(zhuǎn)移,振鈴組,呼叫記錄,呼叫排隊,電話會議等)的高級PBX系統(tǒng),可以使某些任務(wù)更多由于自動化,操作簡單。
圖1:技術(shù)設(shè)備也是小型和大型呼叫中心的關(guān)鍵因素[3]
有關(guān)呼叫中心的技術(shù)背景的更多信息
呼叫中心可以同時處理大量呼叫 ,并將其轉(zhuǎn)發(fā)給負(fù)責(zé)處理這些呼叫的人員。 但是,在當(dāng)今快速發(fā)展的世界中,VoIP技術(shù),高性能計算機和智能軟件應(yīng)用程序幾乎是必不可少的。 如今的呼叫中心使用的是比老式的巨大“盒子”更多的最新設(shè)備 ,而老式的“盒子”卻有很多電線伸出來。
我們可以將呼叫中心分為兩種主要類型,如下所示[1]:
如果使用虛擬呼叫中心,則功能是固定的,不能無限擴展它們。 但是,在基于內(nèi)部的呼叫中心的情況下,您將有機會通過開發(fā)所需的功能來改進系統(tǒng)。 實際上,只有想象力(當(dāng)然還有公司的需求和條件)限制了這種改進。
環(huán)組開發(fā)簡介
在本文中,您將看到后一種情況的示例。 確切地說,本指南逐步演示了環(huán)組的創(chuàng)建。 該鈴聲組將是安裝在集團電話中的“虛擬” 分機 。 此分機基于軟件電話 (因此,即已注冊到PBX)。 PBX為該分機分配電話號碼。 呼叫時,該分機嘗試將來電轉(zhuǎn)移到其他分機之一(即轉(zhuǎn)移到先前已在PBX上注冊的振鈴組成員之一)。 振鈴組策略確定哪個成員將接受呼叫。
恐怕以前的線程包含一些新的(或令人恐懼的)表達式……不管是不是,為了完整起見,讓我澄清一下此解決方案的基本要素。
基于以上內(nèi)容,讓我們總結(jié)一下該項目需要哪些開發(fā):
因此,我使用了以下3類 : Softphone.cs:它介紹了如何使用可注冊到PBX的C#開發(fā)軟件電話。 Program.cs:此類負(fù)責(zé)處理用戶事件。 它介紹了如何將軟件電話注冊到PBX。 RingGroupCallHandler.cs:此類說明如何創(chuàng)建必要的鈴聲組策略。 由于此類分開,因此應(yīng)用程序可以使用其實例同時處理多個傳入呼叫。
首先,您將需要一個新的Visual C#控制臺應(yīng)用程序 ,因為該應(yīng)用程序?qū)嶋H上是“虛擬軟件電話”,因此一個控制臺應(yīng)用程序就足夠了。 為了更好地理解,我將本文的其余部分分為三個主要部分。 所有這三個部分都逐步介紹了一個類的實現(xiàn)。
現(xiàn)在,已經(jīng)用盡了理論和發(fā)展背景,“保持冷靜,開始編程!” :)
開發(fā)/第1部分:構(gòu)建軟件電話
你準(zhǔn)備好了嗎? 當(dāng)然! :)好吧,讓我們從Softphone.cs類開始。 第一步,使用以下行添加所需的內(nèi)容。 (作為C#.NET VoIP庫,已經(jīng)使用了Ozeki VoIP SIP SDK- 請記住,此代碼是使用此SDK的預(yù)先編寫的VoIP組件編寫的。因此,如果要嘗試使用它們,則需要安裝它的免費試用版。 [7]) using?System; using?Ozeki.VoIP; using?Ozeki.VoIP.SDK;現(xiàn)在需要軟件電話和電話線對象。 您可以從ISoftPhone和IPhoneLine接口獲取它們,如下所示: ISoftPhone?_softphone; IPhoneLine?_phoneLine;在構(gòu)造函數(shù)中,您需要使用默認(rèn)參數(shù)來初始化此軟件電話。 5000和10000參數(shù)是指端口范圍:第一個數(shù)字是最小端口號( minPortRange ),另一個是最大端口號( maxPortRange )。 為了實現(xiàn)對來電的持續(xù)監(jiān)控,您需要訂閱IncomingCall事件。 請查看以下上述步驟: public?Softphone(){_softphone?=?SoftPhoneFactory.CreateSoftPhone(5000,?10000);_softphone.IncomingCall?+=?softphone_IncomingCall;}通過訂閱IncomigCall事件,將通知您是否有來電。 如果電話線的狀態(tài)發(fā)生更改,則PhoneLineStateChange事件(當(dāng)然會在訂閱后)通知您。 請查看以下上述步驟: public?event?EventHandler<VoIPEventArgs<IPhoneCall>>?IncomigCall; public?event?EventHandler<RegistrationStateChangedArgs>?PhoneLineStateChanged;
通過使用Register方法,可以將軟件電話注冊到PBX。 為此,應(yīng)創(chuàng)建一個需要SIP帳戶的電話線。 為了能夠為您的軟件電話創(chuàng)建SIP帳戶,您需要指定以下參數(shù):
創(chuàng)建必要的SIP帳戶后,您可以創(chuàng)建電話線以能夠與PBX通信。 還需要收聽電話線狀態(tài)的變化。 最后, 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());}}電話線的注冊狀態(tài)更改時將調(diào)用此方法: void?_phoneLine_RegistrationStateChanged(object?sender,?RegistrationStateChangedArgs?e) {var?handler?=?PhoneLineStateChanged;if?(handler?!=?null)handler(this,?e); }這將在有來電時被調(diào)用: void?softphone_IncomingCall(object?sender,?VoIPEventArgs<IPhoneCall>?e) {var?handler?=?IncomigCall;if?(handler?!=?null)handler(this,?e); }調(diào)用時,可以使用以下代碼片段創(chuàng)建調(diào)用對象: public?IPhoneCall?CreateCall(string?member) {return?_softphone.CreateCallObject(_phoneLine,?member); }這是Softphone.cs的實現(xiàn)的結(jié)尾。 我了解您是否現(xiàn)在想休息一下。 休息一下,讓我們繼續(xù)編碼Program.cs ! :)
開發(fā)/第2部分:構(gòu)建其他功能
完成軟件電話的創(chuàng)建后,讓我們繼續(xù)使用Program.cs進行編程。 此類介紹了Softphone對象的用法,處理控制臺事件,與用戶進行交互以及使用Softphone類提供的機會。 (您將看到,所有內(nèi)容都是在相互通信的單獨方法中處理的。)
在Main方法下面可以看到您需要通過調(diào)用初始化方法來初始化軟件電話的位置。 (代碼段末尾的BlockExit方法-不允許應(yīng)用程序退出。) 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(); }現(xiàn)在,有一個新的軟件電話可以監(jiān)視電話線的狀態(tài)。 您已訂閱以獲取有關(guān)電話線狀態(tài)已更改的通知。 在下面,您可以看到Softphone_PhoneLineStateChanged方法確定每種狀態(tài)的處理方式。 電話線的注冊狀態(tài)更改時將調(diào)用此方法: static?void?softphone_PhoneLineStateChanged(object?sender,?RegistrationStateChangedArgs?e) {Console.WriteLine("Phone?line?state?changed?to:?{0}",?e.State);?if?(e.State?==?RegState.RegistrationSucceeded)MemberAdder(); }關(guān)于該類也訂閱了電話線路的事件這一事實,當(dāng)電話線路的狀態(tài)成功注冊時會通知該類,并且它開始向用戶詢問振鈴組:成員編號,他們的電話號碼。 這些被存儲在字符串列表中: 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...");}當(dāng)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();}如果有傳入呼叫,則將通知用戶,應(yīng)用程序?qū)樵摵艚袆?chuàng)建一個新的RingGroupCallHandler對象,訂閱其Completed方法,然后將該對象添加到處理程序列表中。 此后,它調(diào)用對象的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();}當(dāng)傳入呼叫不再可用時(例如,由于已轉(zhuǎn)移或呼叫者完成了呼叫等), 應(yīng)從呼叫處理程序的列表中刪除RingGroupCallHandler對象: static?void?callHandler_Completed(object?sender,?EventArgs?e){lock?(_callHandlers)_callHandlers.Remove((RingGroupCallHandler)sender);}為了能夠使用先前創(chuàng)建的軟件電話進行通信,需要設(shè)置一個SIP帳戶 。 因此,應(yīng)用程序應(yīng)要求用戶輸入有效的SIP帳戶詳細(xì)信息,然后嘗試注冊到PBX。 有關(guān)詳細(xì)說明,請查看以下代碼庫中的注釋: 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方法。 這不會讓應(yīng)用程序退出: private?static?void?BlockExit(){while?(true){Thread.Sleep(10);}}是的,現(xiàn)在我們也可以使用Softphone.cs和Program.cs 。 如果需要,請在開始最后一個主要部分之前稍作休息! :)
開發(fā)/第3部分:使用“一對一”策略構(gòu)建環(huán)網(wǎng)組
現(xiàn)在是最后一個主要部分,它演示了RingGroupCallHandler類的實現(xiàn)。 此類負(fù)責(zé)等待傳入的呼叫,開始根據(jù)首選的振鈴組策略呼叫振鈴組的成員以及將呼叫轉(zhuǎn)移到適當(dāng)?shù)某蓡T。
來電時應(yīng)調(diào)用Start方法。 Start方法接受呼叫,然后通過調(diào)用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方法創(chuàng)建呼叫對象到每個構(gòu)件和CallSequencer方法將被調(diào)用。 需要訂閱CallStateChanged事件。 事件發(fā)生時,應(yīng)用程序?qū)⒄{(diào)用OutgoingCallStateChanged方法。 (當(dāng)接聽來電時將使用它,并且應(yīng)將其轉(zhuǎn)移到振鈴組中的座席。) 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方法將該呼叫者(即傳入呼叫)轉(zhuǎn)移到環(huán)組成員之一。 OutgoingCallStateChanged方法還負(fù)責(zé)從調(diào)用列表中刪除該調(diào)用,然后調(diào)用OnCompleted方法。 當(dāng)被叫忙或無法接通時 ,應(yīng)用程序?qū)⒃俅握{(diào)用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();}}}}應(yīng)答呼叫并將其轉(zhuǎn)移到振鈴組成員后,將調(diào)用OnCompleted方法。 OnCompleted方法調(diào)用HangupOutgoingCalls方法,并設(shè)置Completed事件。 這表明呼叫轉(zhuǎn)移已成功,或者呼叫方已在轉(zhuǎn)移之前掛斷了呼叫。 Completed事件還通知應(yīng)用程序,呼叫處理程序無事可做。 (當(dāng)傳入呼叫在傳輸之前完成時,也會調(diào)用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); }這就是開發(fā)的終點! 祝賀您的新申請! 你的工作快結(jié)束了。 現(xiàn)在,您只需要使用新的虛擬軟件電話來測試振鈴組。
測試
讓我們運行該應(yīng)用程序以測試新虛擬虛擬電話的振鈴組功能。 請注意, 某些SIP帳戶 (無論是臺式VoIP電話,軟件電話還是移動分機)應(yīng)事先安裝在PBX中,以便能夠?qū)⒊蓡T添加到環(huán)組中。 例如,我在PBX中注冊了三個SIP帳戶(1000、2000、3000)。 (兩個將成為一個環(huán)網(wǎng)組成員,而我將使用第三個來進行測試通話。)
按F5后,可以在控制臺應(yīng)用程序中看到以前編寫的介紹 。 介紹之后,該應(yīng)用程序會要求您設(shè)置您的SIP帳戶 ( 圖2 )。
圖2:環(huán)組作為控制臺應(yīng)用程序運行后
現(xiàn)在,您需要配置虛擬軟件電話和振鈴組本身。 首先, 通過輸入“ true”將注冊設(shè)置為必填 ( 圖3 )。 此后,您需要在PBX中創(chuàng)建一個新的SIP帳戶,該帳戶將用作虛擬軟件電話分機以用于振鈴組(在我的情況下,這是編號為“ 4000”的SIP帳戶)。 現(xiàn)在,通過輸入所需的數(shù)據(jù),在新的控制臺應(yīng)用程序中提供相同的SIP帳戶詳細(xì)信息 。 域名是集團電話的IP地址, 端口號是它的端口號(通常是5060)。 提供必要的SIP帳戶詳細(xì)信息后,應(yīng)用程序?qū)L試注冊到PBX 。 如果提供的信息正確,則電話線狀態(tài)將更改為RegistrationSucceeded 。 此后,應(yīng)用程序要求您輸入環(huán)組成員的數(shù)量并定義一個序列 。 此順序確定當(dāng)某人撥打響鈴組的電話號碼時哪個響鈴(即哪個電話)將響鈴。 (應(yīng)用程序會將來電轉(zhuǎn)移到第一個成員。如果該呼叫不可用,則呼叫將轉(zhuǎn)發(fā)到第二個成員,依此類推。)現(xiàn)在,配置完成,應(yīng)用程序正在等待來電 ( 圖3 )。
圖3:環(huán)組控制臺應(yīng)用程序運行后
我的測試電話可以在下面看到( 圖4 )。 我用“ 3000”打了一個電話。 我撥打了“ 4000”(這是振鈴組的電話號碼–它可以用作公司的中央電話號碼)。 此后,第一個振鈴組成員,編號為“ 2000”的分機開始振鈴。 但是,此分機忙,因此第二個成員“ 1000”開始響鈴。 此分機已接受呼叫,因此來電已從“ 4000”(虛擬軟件電話)轉(zhuǎn)移到“ 1000”(臺式VoIP電話)。
圖4:測試電話
這意味著您的應(yīng)用程序運行良好! 恭喜您成功! :)
結(jié)論
在當(dāng)今瞬息萬變的世界中,公司在所有業(yè)務(wù)領(lǐng)域中也都需要最新的軟件和硬件設(shè)備,因此在與客戶和其他合作伙伴進行交流時也是如此。 呼叫中心應(yīng)配備有助于自動化的有效解決方案。 如果使用VoIP技術(shù)(和IP PBX),則呼叫轉(zhuǎn)移,振鈴組,呼叫記錄,呼叫排隊,電話會議等只是可以實現(xiàn)的一些重要功能。 在本教程中,我想通過振鈴組的示例來說明,自己開發(fā)缺少的功能以改善呼叫中心有多么容易。 我希望你喜歡它!
進一步的閱讀和參考
為了撰寫有關(guān)構(gòu)建環(huán)網(wǎng)組的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
內(nèi)容
- 主題的重要性
- 有關(guān)呼叫中心的技術(shù)背景的更多信息
- 環(huán)組開發(fā)簡介
- 開發(fā)/第1部分:構(gòu)建軟件電話
- 開發(fā)/第2部分:構(gòu)建其他功能
- 開發(fā)/第3部分:使用“一對一”策略構(gòu)建環(huán)網(wǎng)組
- 測試
- 結(jié)論
- 進一步的閱讀和參考
主題的重要性
呼叫中心或呼叫中心是一個集中式辦公室,用于通過電話接收或傳輸大量請求[1]。 換句話說:“呼叫中心是一個物理場所,通常由一定數(shù)量的計算機自動化來由組織來處理客戶和其他電話” [2]。 先前定義中的“ 通常具有一定程度的計算機自動化 ”部分表示,成功的呼叫中心基于人工和某種技術(shù)設(shè)備的合作。 因此,適當(dāng)?shù)腎T基礎(chǔ)架構(gòu)至關(guān)重要。
專家們說, 精心挑選員工是成功建立呼叫中心的第一步。 但是大多數(shù)技能都是可以學(xué)習(xí)的。 在第一線支持,服務(wù)臺和服務(wù)臺培訓(xùn)期間 ,講師經(jīng)常提請操作員注意以下幾點:
- 要有禮貌和禮貌
- 主動
- 首先把事情放在首位–讓優(yōu)先事項推動舉措
- 雙贏思考–對您的客戶有利的對您也有利,等等。
這些是成功呼叫中心必不可少的非常重要的人為因素。 另外,某些管理工具,例如績效評估和對員工的定期反饋,獎勵,積極的強化等,是有效呼叫中心不可或缺的要素。 但是如果沒有適當(dāng)?shù)募夹g(shù),所有人類知識都是徒勞的。 無論是小型呼叫中心還是大型呼叫中心,要獲得更多利潤并建立客戶忠誠度,員工都必須使用先進的硬件和軟件設(shè)備 。
在外包的印度呼叫中心和大型的美國呼叫中心中,有什么共同點( 圖1 )? 是的,有很多東西,但是最重要的是核心技術(shù)。 在這兩個地方,運營商都使用VoIP臺式電話,軟電話,頭戴式耳機,以及:具有典型呼叫中心功能(例如,呼叫轉(zhuǎn)移,振鈴組,呼叫記錄,呼叫排隊,電話會議等)的高級PBX系統(tǒng),可以使某些任務(wù)更多由于自動化,操作簡單。
圖1:技術(shù)設(shè)備也是小型和大型呼叫中心的關(guān)鍵因素[3]
有關(guān)呼叫中心的技術(shù)背景的更多信息
呼叫中心可以同時處理大量呼叫 ,并將其轉(zhuǎn)發(fā)給負(fù)責(zé)處理這些呼叫的人員。 但是,在當(dāng)今快速發(fā)展的世界中,VoIP技術(shù),高性能計算機和智能軟件應(yīng)用程序幾乎是必不可少的。 如今的呼叫中心使用的是比老式的巨大“盒子”更多的最新設(shè)備 ,而老式的“盒子”卻有很多電線伸出來。
我們可以將呼叫中心分為兩種主要類型,如下所示[1]:
- 虛擬呼叫中心:在這種呼叫中心模型中,運營商通常向在其自己的數(shù)據(jù)中心托管呼叫中心電話設(shè)備的供應(yīng)商支付月費或年費。 代理商通過傳統(tǒng)的PSTN電話線或VoIP(互聯(lián)網(wǎng)協(xié)議語音)連接到供應(yīng)商的設(shè)備。
- 基于前提的呼叫中心:此模型與先前的模型相反。 在這種情況下,呼叫中心已建立在PBX(專用分支交換)中,該交換機由呼叫中心運營商自己擁有,托管和維護。 該PBX提供高級功能,例如呼叫排隊,ACD(自動呼叫分配),IVR(交互式語音響應(yīng))或SBR(基于技能的路由)–這是一種呼叫輔助策略,用于將呼入呼叫分配給最適合的座席,而不是簡單地選擇下一個可用的代理)。
如果使用虛擬呼叫中心,則功能是固定的,不能無限擴展它們。 但是,在基于內(nèi)部的呼叫中心的情況下,您將有機會通過開發(fā)所需的功能來改進系統(tǒng)。 實際上,只有想象力(當(dāng)然還有公司的需求和條件)限制了這種改進。
環(huán)組開發(fā)簡介
在本文中,您將看到后一種情況的示例。 確切地說,本指南逐步演示了環(huán)組的創(chuàng)建。 該鈴聲組將是安裝在集團電話中的“虛擬” 分機 。 此分機基于軟件電話 (因此,即已注冊到PBX)。 PBX為該分機分配電話號碼。 呼叫時,該分機嘗試將來電轉(zhuǎn)移到其他分機之一(即轉(zhuǎn)移到先前已在PBX上注冊的振鈴組成員之一)。 振鈴組策略確定哪個成員將接受呼叫。
恐怕以前的線程包含一些新的(或令人恐懼的)表達式……不管是不是,為了完整起見,讓我澄清一下此解決方案的基本要素。
- 什么是振鈴組:在現(xiàn)代電信時代,呼叫路由允許在有來電時有多個分機振鈴。 它稱為環(huán)組。 環(huán)組通常在PBX中顯示為“虛擬分機”。 在配置振鈴組時,可以定義哪些電話分機屬于該振鈴組。 這樣,當(dāng)有“虛擬分機”的來電時,所有所屬分機將在振鈴組內(nèi)同時或依次振鈴。
- “分機”的含義是什么:在商務(wù)電話中,電話分機可能是指與PBX相連的內(nèi)部電話網(wǎng)絡(luò)中的電話。 在集團電話內(nèi),用戶僅需撥打分機號碼即可直接與其他任何用戶聯(lián)系。 對于呼入電話,撥打公司電話號碼后,總機接線員或自動值班人員可以要求首選分機的號碼。 如果將外部號碼分配給各個分機[5],則也可以通過直接呼入完成呼叫。
- 什么是軟件電話:軟件電話是一種特殊的計算機軟件,可以充當(dāng)虛擬電話,使人們可以使用其PC或筆記本電腦通過Internet與他人進行通信。
- 什么是呼叫轉(zhuǎn)移:呼叫轉(zhuǎn)移是一種電信機制,允許用戶通過使用轉(zhuǎn)移按鈕或開關(guān)掛鉤并撥打所需的位置[6],將現(xiàn)有電話轉(zhuǎn)移到另一個電話或話務(wù)臺。
- 什么是振鈴組策略:振鈴組策略確定在有來電時如何通知組成員的方式。 在呼叫轉(zhuǎn)移之前,環(huán)網(wǎng)組需要通知組成員需要進行呼叫轉(zhuǎn)移。 要選擇在振鈴組內(nèi)接受呼叫的成員,可以使用幾種策略。 讓我們看一些示例:可以配置為同時呼叫振鈴組中的所有分機(即所有成員的電話)。 還可以實現(xiàn)的是,環(huán)群分機將以預(yù)先設(shè)置的順序一一呼叫成員。 另一種常用的策略是當(dāng)環(huán)組的延伸以隨機順序被調(diào)用時的情況。 (在該項目中,將演示“一對一”策略。)
基于以上內(nèi)容,讓我們總結(jié)一下該項目需要哪些開發(fā):
- 構(gòu)建能夠接收電話的虛擬(或控制臺)軟件電話
- 構(gòu)建所需的附加功能,例如呼叫轉(zhuǎn)移和多個呼叫管理
- 用“一對一”策略建立環(huán)網(wǎng)組
因此,我使用了以下3類 :
首先,您將需要一個新的Visual C#控制臺應(yīng)用程序 ,因為該應(yīng)用程序?qū)嶋H上是“虛擬軟件電話”,因此一個控制臺應(yīng)用程序就足夠了。 為了更好地理解,我將本文的其余部分分為三個主要部分。 所有這三個部分都逐步介紹了一個類的實現(xiàn)。
現(xiàn)在,已經(jīng)用盡了理論和發(fā)展背景,“保持冷靜,開始編程!” :)
開發(fā)/第1部分:構(gòu)建軟件電話
你準(zhǔn)備好了嗎? 當(dāng)然! :)好吧,讓我們從Softphone.cs類開始。 第一步,使用以下行添加所需的內(nèi)容。 (作為C#.NET VoIP庫,已經(jīng)使用了Ozeki VoIP SIP SDK- 請記住,此代碼是使用此SDK的預(yù)先編寫的VoIP組件編寫的。因此,如果要嘗試使用它們,則需要安裝它的免費試用版。 [7]) using?System; using?Ozeki.VoIP; using?Ozeki.VoIP.SDK;現(xiàn)在需要軟件電話和電話線對象。 您可以從ISoftPhone和IPhoneLine接口獲取它們,如下所示: ISoftPhone?_softphone; IPhoneLine?_phoneLine;在構(gòu)造函數(shù)中,您需要使用默認(rèn)參數(shù)來初始化此軟件電話。 5000和10000參數(shù)是指端口范圍:第一個數(shù)字是最小端口號( minPortRange ),另一個是最大端口號( maxPortRange )。 為了實現(xiàn)對來電的持續(xù)監(jiān)控,您需要訂閱IncomingCall事件。 請查看以下上述步驟: public?Softphone(){_softphone?=?SoftPhoneFactory.CreateSoftPhone(5000,?10000);_softphone.IncomingCall?+=?softphone_IncomingCall;}通過訂閱IncomigCall事件,將通知您是否有來電。 如果電話線的狀態(tài)發(fā)生更改,則PhoneLineStateChange事件(當(dāng)然會在訂閱后)通知您。 請查看以下上述步驟: public?event?EventHandler<VoIPEventArgs<IPhoneCall>>?IncomigCall; public?event?EventHandler<RegistrationStateChangedArgs>?PhoneLineStateChanged;
通過使用Register方法,可以將軟件電話注冊到PBX。 為此,應(yīng)創(chuàng)建一個需要SIP帳戶的電話線。 為了能夠為您的軟件電話創(chuàng)建SIP帳戶,您需要指定以下參數(shù):
- registrationRequired:為了能夠接收來電,您需要為此參數(shù)設(shè)置“ true”值。
- displayName:這是要在被叫客戶端上顯示的名稱。
- userName:這是要呼叫此軟件電話的其他客戶端要撥打的號碼。
- authenticationId:這是集團電話的標(biāo)識符(如登錄名)。
- registerPassword:這是用于注冊到PBX的authenticationId的密碼。
- domainHost:這是一個域名,即您要注冊的集團電話的IP地址。
- domainPort:這是您要注冊的集團電話的端口號。
創(chuàng)建必要的SIP帳戶后,您可以創(chuàng)建電話線以能夠與PBX通信。 還需要收聽電話線狀態(tài)的變化。 最后, 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());}}電話線的注冊狀態(tài)更改時將調(diào)用此方法: void?_phoneLine_RegistrationStateChanged(object?sender,?RegistrationStateChangedArgs?e) {var?handler?=?PhoneLineStateChanged;if?(handler?!=?null)handler(this,?e); }這將在有來電時被調(diào)用: void?softphone_IncomingCall(object?sender,?VoIPEventArgs<IPhoneCall>?e) {var?handler?=?IncomigCall;if?(handler?!=?null)handler(this,?e); }調(diào)用時,可以使用以下代碼片段創(chuàng)建調(diào)用對象: public?IPhoneCall?CreateCall(string?member) {return?_softphone.CreateCallObject(_phoneLine,?member); }這是Softphone.cs的實現(xiàn)的結(jié)尾。 我了解您是否現(xiàn)在想休息一下。 休息一下,讓我們繼續(xù)編碼Program.cs ! :)
開發(fā)/第2部分:構(gòu)建其他功能
完成軟件電話的創(chuàng)建后,讓我們繼續(xù)使用Program.cs進行編程。 此類介紹了Softphone對象的用法,處理控制臺事件,與用戶進行交互以及使用Softphone類提供的機會。 (您將看到,所有內(nèi)容都是在相互通信的單獨方法中處理的。)
在Main方法下面可以看到您需要通過調(diào)用初始化方法來初始化軟件電話的位置。 (代碼段末尾的BlockExit方法-不允許應(yīng)用程序退出。) 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(); }現(xiàn)在,有一個新的軟件電話可以監(jiān)視電話線的狀態(tài)。 您已訂閱以獲取有關(guān)電話線狀態(tài)已更改的通知。 在下面,您可以看到Softphone_PhoneLineStateChanged方法確定每種狀態(tài)的處理方式。 電話線的注冊狀態(tài)更改時將調(diào)用此方法: static?void?softphone_PhoneLineStateChanged(object?sender,?RegistrationStateChangedArgs?e) {Console.WriteLine("Phone?line?state?changed?to:?{0}",?e.State);?if?(e.State?==?RegState.RegistrationSucceeded)MemberAdder(); }關(guān)于該類也訂閱了電話線路的事件這一事實,當(dāng)電話線路的狀態(tài)成功注冊時會通知該類,并且它開始向用戶詢問振鈴組:成員編號,他們的電話號碼。 這些被存儲在字符串列表中: 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...");}當(dāng)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();}如果有傳入呼叫,則將通知用戶,應(yīng)用程序?qū)樵摵艚袆?chuàng)建一個新的RingGroupCallHandler對象,訂閱其Completed方法,然后將該對象添加到處理程序列表中。 此后,它調(diào)用對象的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();}當(dāng)傳入呼叫不再可用時(例如,由于已轉(zhuǎn)移或呼叫者完成了呼叫等), 應(yīng)從呼叫處理程序的列表中刪除RingGroupCallHandler對象: static?void?callHandler_Completed(object?sender,?EventArgs?e){lock?(_callHandlers)_callHandlers.Remove((RingGroupCallHandler)sender);}為了能夠使用先前創(chuàng)建的軟件電話進行通信,需要設(shè)置一個SIP帳戶 。 因此,應(yīng)用程序應(yīng)要求用戶輸入有效的SIP帳戶詳細(xì)信息,然后嘗試注冊到PBX。 有關(guān)詳細(xì)說明,請查看以下代碼庫中的注釋: 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方法。 這不會讓應(yīng)用程序退出: private?static?void?BlockExit(){while?(true){Thread.Sleep(10);}}是的,現(xiàn)在我們也可以使用Softphone.cs和Program.cs 。 如果需要,請在開始最后一個主要部分之前稍作休息! :)
開發(fā)/第3部分:使用“一對一”策略構(gòu)建環(huán)網(wǎng)組
現(xiàn)在是最后一個主要部分,它演示了RingGroupCallHandler類的實現(xiàn)。 此類負(fù)責(zé)等待傳入的呼叫,開始根據(jù)首選的振鈴組策略呼叫振鈴組的成員以及將呼叫轉(zhuǎn)移到適當(dāng)?shù)某蓡T。
來電時應(yīng)調(diào)用Start方法。 Start方法接受呼叫,然后通過調(diào)用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方法創(chuàng)建呼叫對象到每個構(gòu)件和CallSequencer方法將被調(diào)用。 需要訂閱CallStateChanged事件。 事件發(fā)生時,應(yīng)用程序?qū)⒄{(diào)用OutgoingCallStateChanged方法。 (當(dāng)接聽來電時將使用它,并且應(yīng)將其轉(zhuǎn)移到振鈴組中的座席。) 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方法將該呼叫者(即傳入呼叫)轉(zhuǎn)移到環(huán)組成員之一。 OutgoingCallStateChanged方法還負(fù)責(zé)從調(diào)用列表中刪除該調(diào)用,然后調(diào)用OnCompleted方法。 當(dāng)被叫忙或無法接通時 ,應(yīng)用程序?qū)⒃俅握{(diào)用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();}}}}應(yīng)答呼叫并將其轉(zhuǎn)移到振鈴組成員后,將調(diào)用OnCompleted方法。 OnCompleted方法調(diào)用HangupOutgoingCalls方法,并設(shè)置Completed事件。 這表明呼叫轉(zhuǎn)移已成功,或者呼叫方已在轉(zhuǎn)移之前掛斷了呼叫。 Completed事件還通知應(yīng)用程序,呼叫處理程序無事可做。 (當(dāng)傳入呼叫在傳輸之前完成時,也會調(diào)用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); }這就是開發(fā)的終點! 祝賀您的新申請! 你的工作快結(jié)束了。 現(xiàn)在,您只需要使用新的虛擬軟件電話來測試振鈴組。
測試
讓我們運行該應(yīng)用程序以測試新虛擬虛擬電話的振鈴組功能。 請注意, 某些SIP帳戶 (無論是臺式VoIP電話,軟件電話還是移動分機)應(yīng)事先安裝在PBX中,以便能夠?qū)⒊蓡T添加到環(huán)組中。 例如,我在PBX中注冊了三個SIP帳戶(1000、2000、3000)。 (兩個將成為一個環(huán)網(wǎng)組成員,而我將使用第三個來進行測試通話。)
按F5后,可以在控制臺應(yīng)用程序中看到以前編寫的介紹 。 介紹之后,該應(yīng)用程序會要求您設(shè)置您的SIP帳戶 ( 圖2 )。
圖2:環(huán)組作為控制臺應(yīng)用程序運行后
現(xiàn)在,您需要配置虛擬軟件電話和振鈴組本身。 首先, 通過輸入“ true”將注冊設(shè)置為必填 ( 圖3 )。 此后,您需要在PBX中創(chuàng)建一個新的SIP帳戶,該帳戶將用作虛擬軟件電話分機以用于振鈴組(在我的情況下,這是編號為“ 4000”的SIP帳戶)。 現(xiàn)在,通過輸入所需的數(shù)據(jù),在新的控制臺應(yīng)用程序中提供相同的SIP帳戶詳細(xì)信息 。 域名是集團電話的IP地址, 端口號是它的端口號(通常是5060)。 提供必要的SIP帳戶詳細(xì)信息后,應(yīng)用程序?qū)L試注冊到PBX 。 如果提供的信息正確,則電話線狀態(tài)將更改為RegistrationSucceeded 。 此后,應(yīng)用程序要求您輸入環(huán)組成員的數(shù)量并定義一個序列 。 此順序確定當(dāng)某人撥打響鈴組的電話號碼時哪個響鈴(即哪個電話)將響鈴。 (應(yīng)用程序會將來電轉(zhuǎn)移到第一個成員。如果該呼叫不可用,則呼叫將轉(zhuǎn)發(fā)到第二個成員,依此類推。)現(xiàn)在,配置完成,應(yīng)用程序正在等待來電 ( 圖3 )。
圖3:環(huán)組控制臺應(yīng)用程序運行后
我的測試電話可以在下面看到( 圖4 )。 我用“ 3000”打了一個電話。 我撥打了“ 4000”(這是振鈴組的電話號碼–它可以用作公司的中央電話號碼)。 此后,第一個振鈴組成員,編號為“ 2000”的分機開始振鈴。 但是,此分機忙,因此第二個成員“ 1000”開始響鈴。 此分機已接受呼叫,因此來電已從“ 4000”(虛擬軟件電話)轉(zhuǎn)移到“ 1000”(臺式VoIP電話)。
圖4:測試電話
這意味著您的應(yīng)用程序運行良好! 恭喜您成功! :)
結(jié)論
在當(dāng)今瞬息萬變的世界中,公司在所有業(yè)務(wù)領(lǐng)域中也都需要最新的軟件和硬件設(shè)備,因此在與客戶和其他合作伙伴進行交流時也是如此。 呼叫中心應(yīng)配備有助于自動化的有效解決方案。 如果使用VoIP技術(shù)(和IP PBX),則呼叫轉(zhuǎn)移,振鈴組,呼叫記錄,呼叫排隊,電話會議等只是可以實現(xiàn)的一些重要功能。 在本教程中,我想通過振鈴組的示例來說明,自己開發(fā)缺少的功能以改善呼叫中心有多么容易。 我希望你喜歡它!
進一步的閱讀和參考
為了撰寫有關(guān)構(gòu)建環(huán)網(wǎng)組的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
總結(jié)
以上是生活随笔為你收集整理的在C#中构建一个虚拟软件电话,该软件电话可以在您的呼叫中心中作为振铃组...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 志宇-Jenkins学习
- 下一篇: 基于改进NSGA-Ⅱ算法的开关磁阻电机再