{"id":18107812,"url":"https://github.com/danbarua/neventsocket","last_synced_at":"2025-04-13T22:31:30.470Z","repository":{"id":24725217,"uuid":"28137309","full_name":"danbarua/NEventSocket","owner":"danbarua","description":"A reactive FreeSwitch eventsocket library for Modern .Net","archived":false,"fork":false,"pushed_at":"2022-08-10T20:04:27.000Z","size":4164,"stargazers_count":74,"open_issues_count":18,"forks_count":36,"subscribers_count":15,"default_branch":"master","last_synced_at":"2024-04-29T20:05:34.350Z","etag":null,"topics":["event-socket","freeswitch"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danbarua.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-12-17T12:56:28.000Z","updated_at":"2024-04-16T18:07:30.000Z","dependencies_parsed_at":"2022-08-22T22:20:40.775Z","dependency_job_id":null,"html_url":"https://github.com/danbarua/NEventSocket","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danbarua%2FNEventSocket","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danbarua%2FNEventSocket/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danbarua%2FNEventSocket/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danbarua%2FNEventSocket/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danbarua","download_url":"https://codeload.github.com/danbarua/NEventSocket/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248790616,"owners_count":21162056,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["event-socket","freeswitch"],"created_at":"2024-10-31T23:13:37.067Z","updated_at":"2025-04-13T22:31:29.981Z","avatar_url":"https://github.com/danbarua.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"NEventSocket \n============\n[![NuGet Badge](https://buildstats.info/nuget/NEventSocket)](https://www.nuget.org/packages/NEventSocket/) [![MyGet Badge](https://buildstats.info/myget/neventsocket-prerelease/NEventSocket)](https://www.myget.org/feed/neventsocket-prerelease/package/nuget/NEventSocket)\n[![Join the chat at https://gitter.im/danbarua/NEventSocket](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/danbarua/NEventSocket?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n| Windows / .NET | Linux / Mono\n| --- | ---\n| [![Build status](https://ci.appveyor.com/api/projects/status/0d28m5hxdd55243q/branch/master?svg=true)](https://ci.appveyor.com/project/danbarua/neventsocket/branch/master)| [![Build Status](https://travis-ci.org/danbarua/NEventSocket.svg?branch=master)](https://travis-ci.org/danbarua/NEventSocket)\n\nNEventSocket is a FreeSwitch [event socket](https://freeswitch.org/confluence/display/FREESWITCH/mod_event_socket) client/[server](https://freeswitch.org/confluence/display/FREESWITCH/Event+Socket+Outbound) library for .Net 4.5.\n\nSince I no longer work in the telecoms industry, this library is no longer maintained. There is a DotNetCore 3 fork of the project here: https://github.com/iamkinetic/NEventSocket\n\nInstalling Release builds\n---\nPackage Manager Console: `Install-Package NEventSocket` \n\nCommandLine: `nuget install NEventSocket`\n\nInstalling Pre-Release builds\n---\nNuGet v3 (VS2015): `nuget install NEventSocket -PreRelease -Source \"https://www.myget.org/F/neventsocket-prerelease/api/v3/index.json\"`\n\nNuGet v2 (VS2012): `nuget install NEventSocket -PreRelease -Source \"https://www.myget.org/F/neventsocket-prerelease/api/v2\"` \n\nInbound Socket Client\n--------------\n\nAn ```InboundSocket``` connects and authenticates to a FreeSwitch server (inbound from the point of view of FreeSwitch) and can listen for all events going on in the system and issue commands to control calls.\nYou can use ReactiveExtensions to filter events using LINQ queries and extension methods.\nAll methods are async and awaitable.\n\n```csharp\nusing System;\nusing System.Reactive.Linq;\nusing System.Threading.Tasks;\n\nusing NEventSocket;\nusing NEventSocket.FreeSwitch;\n\nusing (var socket = await InboundSocket.Connect(\"localhost\", 8021, \"ClueCon\"))\n{\n  var apiResponse = await socket.SendApi(\"status\");\n  Console.WriteLine(apiResponse.BodyText);\n\n  //Tell FreeSwitch which events we are interested in\n  await socket.SubscribeEvents(EventName.ChannelAnswer);\n\n  //Handle events as they come in using Rx\n  socket.ChannelEvents.Where(x =\u003e x.EventName == EventName.ChannelAnswer)\n        .Subscribe(async x =\u003e\n            {\n                Console.WriteLine(\"Channel Answer Event \" +  x.UUID);\n\n                //we have a channel id, now we can control it\n                await socket.Play(x.UUID, \"misc/8000/misc-freeswitch_is_state_of_the_art.wav\");\n            });\n\n  Console.WriteLine(\"Press [Enter] to exit.\");\n  Console.ReadLine();\n}\n```\n\nOutbound Socket Server\n---------------\nAn ```OutboundListener``` listens on a TCP port for socket connections (outbound from the point of view of FreeSwitch) when the FreeSwitch dialplan is setup to route calls to the EventSocket.\nAn ```OutboundSocket``` receives events for one particular channel, the API is the same as for an ```InboundSocket```, so you will need to pass in the channel UUID to issue commands for it.\n\nDon't forget to use the ```async``` and ```full``` flags in your dialplan.\n````async```` means that applications will not block (e.g. a bridge will block until the channel hangs up and completes the call) and ````full```` gives the socket access to the full EventSocket api (without this you will see `-ERR Unknown Command` responses)\n````xml\n\u003caction application=\"socket\" data=\"127.0.0.1:8084 async full\"/\u003e\n````\n\n```csharp\nusing System;\nusing System.Reactive.Linq;\nusing System.Threading.Tasks;\n\nusing NEventSocket;\nusing NEventSocket.FreeSwitch;\n\nusing (var listener = new OutboundListener(8084))\n{\n  listener.Connections.Subscribe(\n    async socket =\u003e {\n      await socket.Connect();\n\n      //after calling .Connect(), socket.ChannelData\n      //is populated with all the headers and variables of the channel\n\n      var uuid = socket.ChannelData.Headers[HeaderNames.UniqueId];\n      Console.WriteLine(\"OutboundSocket connected for channel \" + uuid);\n\n      await socket.SubscribeEvents(EventName.ChannelHangup);\n\n      socket.ChannelEvents\n            .Where(x =\u003e x.EventName == EventName.ChannelHangup \u0026\u0026 x.UUID == uuid)\n            .Take(1)\n            .Subscribe(async x =\u003e {\n                  Console.WriteLine(\"Hangup Detected on \" + x.UUID);\n                  await socket.Exit();\n            });\n      \n      \n      //if we use 'full' in our FS dialplan, we'll get events for ALL channels in FreeSwitch\n      //this is not desirable here - so we'll filter in for our unique id only\n      //cases where this is desirable is in the channel api where we want to catch other channels bridging to us\n      await socket.Filter(HeaderNames.UniqueId, uuid);\n      \n      //tell FreeSwitch not to end the socket on hangup, we'll catch the hangup event and .Exit() ourselves\n      await socket.Linger();\n      \n      await socket.ExecuteApplication(uuid, \"answer\");\n      await socket.Play(uuid, \"misc/8000/misc-freeswitch_is_state_of_the_art.wav\");\n      await socket.Hangup(uuid, HangupCause.NormalClearing);\n    });\n\n  listener.Start();\n\n  Console.WriteLine(\"Press [Enter] to exit.\");\n  Console.ReadLine();\n}\n```\n\nError Handling\n--------------\nNEventSocket makes a best effort to handle errors gracefully, there is one scenario that you do need to handle in your application code. In a realtime async application, there may be a situation where we are trying to write to a socket when FreeSwitch has already hung up and disconnected the socket. In this case, NEventSocket will throw a ```TaskCanceledException``` (Note incorrect spelling of ```Cancelled```) which you can catch in order to do any clean up.\n\nIt's a good idea to wrap any ```IObservable.Subscribe(() =\u003e {})``` callbacks in a try/catch block.\n\n```csharp\ntry {\n  await socket.Connect();\n\n  var uuid = socket.ChannelData.Headers[HeaderNames.UniqueId];\n  Console.WriteLine(\"OutboundSocket connected for channel \" + uuid);\n\n  await socket.SubscribeEvents(EventName.Dtmf);\n\n  socket.ChannelEvents.Where(x =\u003e x.UUID == uuid \u0026\u0026 x.EventName == EventName.Dtmf)\n        .Subscribe(async e =\u003e {\n          try {\n            Console.WriteLine(e.Headers[HeaderNames.DtmfDigit]);\n           //speak the number to the caller\n            await client.Say(\n                  uuid,\n                  new SayOptions()\n                  {\n                    Text = e.Headers[HeaderNames.DtmfDigit],\n                    Type = SayType.Number,\n                    Method = SayMethod.Iterated\n                    });\n           }\n           catch(TaskCanceledException ex){\n            //channel hungup\n           }\n      ));\n}\ncatch (TaskCanceledException ex) {\n  //FreeSwitch disconnected, do any clean up here.\n}\n\n```\n\nChannel API\n---------------\nWhilst the ```InboundSocket``` and ```OutboundSocket``` give you a close-to-the-metal experience with the EventSocket interface, the Channel API is a high level abstraction built on top of these.\nA Channel object maintains its own state by subscribing to events from FreeSwitch and allows us to control calls in a more object oriented manner without having to pass channel UUIDs around as strings.\n\nAlthough the InboundSocket and OutboundSocket APIs are reasonably stable, the Channel API is a work in progress  with the goal of providing a pleasant, easy to use, strongly-typed API on top of the EventSocket.\n\nThere is an in-depth example in the examples/Channels folder.\n\n```csharp\nusing System;\nusing System.Reactive.Linq;\nusing System.Threading.Tasks;\n\nusing NEventSocket;\nusing NEventSocket.Channels;\nusing NEventSocket.FreeSwitch;\nusing NEventSocket.Util;\n\nusing (var listener = new OutboundListener(8084))\n{\n  listener.Channels.Subscribe(\n    async channel =\u003e {\n      try\n      {\n          await channel.Answer();\n          await channel.PlayFile(\"ivr/8000/ivr-call_being_transferred.wav\");\n          await channel.StartDetectingInbandDtmf();\n\n          var bridgeOptions = \n                  new BridgeOptions()\n                      {\n                        IgnoreEarlyMedia = true,\n                        RingBack =\n                            \"tone_stream://%(400,200,400,450);%(400,2000,400,450);loops=-1\",\n                        ContinueOnFail = true,\n                        HangupAfterBridge = true,\n                        TimeoutSeconds = 60,\n                        //can get variables from a channel using the indexer\n                        CallerIdName = channel[\"effective_caller_id_name\"], \n                        CallerIdNumber = channel[\"effective_caller_id_number\"],\n                      };\n\n          //attempt a bridge to user/1001, write to the console when it starts ringing\n          await channel.BridgeTo(\"user/1001\", \n                                  bridgeOptions,\n                                  (evt) =\u003e Console.WriteLine(\"B-Leg is ringing...\"))\n\n          //channel.Bridge represents the bridge status\n          if (!channel.Bridge.IsBridged)\n          {\n              //we can inspect the HangupCause to determine why it failed\n              Console.WriteLine(\"Bridge Failed - {0}\".Fmt(channel.Bridge.HangupCause));\n              await channel.PlayFile(\"ivr/8000/ivr-call_rejected.wav\");\n              await channel.Hangup(HangupCause.NormalTemporaryFailure);\n              return;\n          }\n              \n          Console.WriteLine(\"Bridge success - {0}\".Fmt(channel.Bridge.ResponseText));\n\n          //the bridged channel maintains its own state\n          //and handles a subset of full Channel operations\n          channel.Bridge.Channel.HangupCallBack = \n            (e) =\u003e ColorConsole.WriteLine(\"Hangup Detected on B-Leg {0} {1}\"\n                  .Fmt(e.Headers[HeaderNames.CallerUniqueId],\n                    e.Headers[HeaderNames.HangupCause]));\n\n          //we'll listen out for the feature code #9\n          //on the b-leg to do an attended transfer\n          channel.Bridge.Channel.FeatureCodes(\"#\")\n            .Subscribe(async x =\u003e\n            {\n              switch (x)\n              {\n                case \"#9\":\n                  Console.WriteLine(\"Getting the extension to do an attended transfer to...\");\n\n                  //play a dial tone to the b-leg and get 4 digits to dial\n                  var digits = await channel.Bridge.Channel.Read(\n                                    new ReadOptions {\n                                        MinDigits = 3,\n                                        MaxDigits = 4, \n                                        Prompt = \"tone_stream://%(10000,0,350,440)\",\n                                        TimeoutMs = 30000,\n                                        Terminators = \"#\" });\n\n                  if (digits.Result == ReadResultStatus.Success \u0026\u0026 digits.Digits.Length == 4)\n                  {\n                    await channel.Bridge.Channel\n                      .PlayFile(\"ivr/8000/ivr-please_hold_while_party_contacted.wav\");\n                    \n                    var xfer = await channel.Bridge.Channel\n                      .AttendedTransfer(\"user/{0}\".Fmt(digits));\n\n                    //attended transfers are a work-in-progress at the moment\n                    if (xfer.Status == AttendedTransferResultStatus.Failed)\n                    {\n                      if (xfer.HangupCause == HangupCause.CallRejected)\n                      {\n                          //we can play audio into the b-leg via the a-leg channel\n                          await channel\n                            .PlayFile(\"ivr/8000/ivr-call-rejected.wav\", Leg.BLeg);\n                      }\n                      else if (xfer.HangupCause == HangupCause.NoUserResponse \n                                || xfer.HangupCause == HangupCause.NoAnswer)\n                      {\n                          //or we can play audio on the b-leg channel object\n                          await channel.Bridge.Channel\n                            .PlayFile(\"ivr/8000/ivr-no_user_response.wav\");\n                      }\n                      else if (xfer.HangupCause == HangupCause.UserBusy)\n                      {\n                          await channel.Bridge.Channel\n                            .PlayFile(\"ivr/8000/ivr-user_busy.wav\");\n                      }\n                    }\n                    else\n                    {\n                      //otherwise the a-leg is now connected to either\n                      // 1) the c-leg\n                      //    in this case, channel.Bridge.Channel is now the c-leg channel\n                      // 2) the b-leg and the c-leg in a 3-way chat\n                      //    in this case, if the b-leg hangs up, then channel.Bridge.Channel\n                      //    will become the c-leg\n                      await channel\n                      .PlayFile(\"ivr/8000/ivr-call_being_transferred.wav\", Leg.ALeg);\n                    }\n                  }\n                break;\n              }\n            });\n      }\n      catch(TaskCanceledException)\n      {\n          Console.WriteLine(\"Channel {0} hungup\".Fmt(channel.UUID));\n      }\n    }\n    });\n\n  listener.Start();\n\n  Console.WriteLine(\"Press [Enter] to exit.\");\n  Console.ReadLine();\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanbarua%2Fneventsocket","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanbarua%2Fneventsocket","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanbarua%2Fneventsocket/lists"}