danbarua / neventsocket Goto Github PK
View Code? Open in Web Editor NEWA reactive FreeSwitch eventsocket library for Modern .Net
License: Mozilla Public License 2.0
A reactive FreeSwitch eventsocket library for Modern .Net
License: Mozilla Public License 2.0
Using the DtmfTest() merthod as an example, if I add
await Task.Delay(1000);
right after the InboundSocket.Connect() call, then when the Task.Deplay call begins, the log shows
2016-02-08 04:16:22 TID[21] TRACE Buffer Read Observable Completed 0 chunks left.
and any additional calls fail because "received" is disposed and null.
Is this not a supported scenario? The application I am trying to use the library with involves opening an inbound socket and subscribing to events then just sleeping until events are received on the socket or an external trigger requires an origination call to be made. In this scenario, the socket opens and authenticates, then immediately dies (which I am simulating with the Task.Delay() call in the sample application.)
-ERR no reply
should probably not be treated as an error in ApiResponse
. I'd suggest to set Success
to true
and add another property IsNoReply
that indicates this specific response state.
As discussed in #41 one of the next steps for NEventSocket is to separate the Channels API. The main reasons are:
So I've tried to separate them and tinkered around until the project was buildable again, it went straight forward:
InterlockedBoolean
, which I suggest to either make them public
or temporarily add an InternalsVisibleTo
to AssemblyInfo.cs
in NEventSocket.dll
.Channels
folder, the files OutputListener.cs
and OutboundSocketExtensions.cs
clearly belong to the Channels API. I'd suggest to keep them in the NEventSocket
namespace. It's quite common to add new classes to namespaces that were introduced in dependent assemblies.Then there is the packaging. it's probably best to introduce a second NuGet package named NEventSocket.Channels
. To make that work, I guess that the Rx dependencies should be referred instead of ilmerged into NEventSocket.dll
. I am not sure what was the initial reason to use ilmerge, but I am pretty sure that it will open a can of wormes if it'll be left that way.
Regarding tests, I have no idea yet :( I hope Dan has some ideas.
What do you think? Can we proceed with the separation, or is it just too early? Is another branch a good way to figure out if we can resolve all these issues, or is it better to just tag what's there with v2
and continue to work on the master branch.
Running the KitchenSink demo, with the DtmfTest() method, my socket connects fine and the "await originate" call triggers a dial/park on my extension, but the originate call never returns- it hangs on the await. Any thoughts as to why this would be? I am running against the most recent dev build of FreeSWITCH (1.7) on Windows.
Any help would be greatly appreciated.
I would like to set a regex for the accepted dtmf input, so I can set a certain logic, for instance where the code has to begin with either a 0 or a 1 and then some numbers and then ends with a 9 or a 0.
I have now used reflection to set the private field digitsRegex, which works as expected, but it would be nicer if the field was just public to begin with.
Runnning in production at $dayJob for some time now, seems stable enough.
I don't see a Transfer method on InboundSocket. I'm basically trying to convert an ongoing call into a conference, so that I can 'conference in' another person. I plan on doing it by transfering both legs into a conference room first. I'm assuming the specific 'Transfer()' function has not been implemented, but the workaround can be running a transfer application via another command? Thanks.
Also - how do I label this issue as a Question (since it's not a bug)?
Hi Dan,
I added this to your code for scheduled hangup and I thought maybe you'd like to add something similar to your version as I find it handy:
--in apiextension.cs
public static Task<ApiResponse> ScheduleHangup(
this EventSocket eventSocket,
string uuid,
string value,
HangupCause hangupCause = HangupCause.NormalClearing)
{
return eventSocket.SendApi("sched_hangup +{0} {1} {2}".Fmt(
value, uuid, hangupCause.ToString().ToUpperWithUnderscores()));
}
--in basicchannel.cs
public Task ScheduleHangup(HangupCause hangupCause = FreeSwitch.HangupCause.NormalClearing)
{
return
RunIfAnswered(
() =>
eventSocket.SendApi("sched_hangup +10 {0} {1}".Fmt(UUID, hangupCause.ToString().ToUpperWithUnderscores())),
true);
}
Hi!
I would kindly like to request you update LibLog so that I can silcence logging from NEventSocket.
I took a stab at it myself but the ColouredConsoleLogger has since been removed; I tried adding it back in but all sorts of other problems arose and, unfortunately, I don't have the time right now (not that I expect you to fix this anytime soon!). I will try again later and submit a PR if I get around to it.
The bridging api is a bit clunky at the moment:
await channel.BridgeTo(
"user/1003", //destination
bridgeOptions, //options
(e) => ColorConsole.WriteLine("Bridge Progress Ringing...".DarkGreen()) // optionally notify on ringing
);
if (!channel.Bridge.IsBridged)
{
// handle failure
}
else
{
channel.Bridge.Channel.DoStuff();
}
In particular, we need a better way to handle externally initiated bridges, possibly via attended transfer, or maybe via a command from another EventSocket client.
For example, my use case is this:
When a channel is bridged to another channel,
I need to subscribe to Feature Codes on the b-leg (agent side) channel
So that I can pause/resume recording and initiate attended transfers
Give I have transferred the caller to another agent
When the caller is connected
Then the Feature Codes should be enabled on that agent's channel
The use of the word When
in the above example implies that we can and should be handling this in a more functionally reactive manner. Instead of interrogating the Channel.Bridge
status object, we can follow Tell Don't Ask by letting the Channel tell us when we have been bridged.
The proposed API could look like this:
channel.Bridge.Subscribe(async bridgedChannel =>
{
bridgedChannel.DoStuff();
});
await channel.BridgeTo("destination");
So that's the happy path, nice and clean.
We need some way to notify of failure, and there are a few options for this.
Channel.Bridge
observablechannel.Bridge.Subscribe(
async bridgedChannel => { bridgedChannel.DoStuff(); },
ex => { //handle ex.HangupCause etc }
);
Channel.BridgeTo
Task: csharp
try
{
await channel.BridgeTo("destination");
}
catch(BridgeFailedException ex)
{
//handle ex.HangupCause etc
}
I am not such a fan of (2) and (3) as currently, the only error handling that consumers of the library need to be aware of is `OperationCanceledException`, and the aim of this library is to allow people to write telephony applications with nice clean C# code. I do not want to be littering my code with ``try/catch` any more than the bare minimum.
3) Expose `LastBridgeHangupCause` on the Channel for interrogation, so the proposed api would look like this:
``` csharp
channel.Bridge.Subscribe(
async bridgedChannel => { bridgedChannel.DoStuff(); }
);
await channel.BridgeTo("destination");
if (!channel.IsBridged)
{
Console.WriteLine(channel.LastBridgeHangupCause);
//otherwise success is handled in the Subscription callback
}
It looks like MS updated reactive library nuget package to 3.0. With this reference neventsocket project cannot be build. Any plans to support new version of reactive library?
here is the main code.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'
// using static socket.
public static InboundSocket socket;
static void Main(string[] args)
{
socket = await InboundSocket.Connect("127.0.0.1", 8021, "ClueCon", 0);
//subscribe events......
socket.Events.Where(x => x.EventName == EventName.ChannelAnswer).Subscribe(async y =>
{
});
}
//in thread auto out call
while (true)
{
.....
InBoundAutoCall(list_ky[j], m_num.Tele, m_num.Id, op);
Thread.Sleep(50);
.....
}
public static async void InBoundAutoCall(string callerno, string tele, int pool_id, OriginateOptions op)
{
try
{
op.CallerIdNumber = callerno;
op.CallerIdName = callerno;
op.HangupAfterBridge = true;
op.TimeoutSeconds = 20;
op.ReturnRingReady = true;
op.ChannelVariables.Add("callerno", callerno);
op.ChannelVariables.Add("calledno", tele);
var result = await socket.Originate("sofia/gateway/" + callerno + "/" + tele, op);
if (result.Success)
{
}
else
{
}
}
catch (Exception ex)
{
}
}`
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
When the number of concurrent calls, at around 100, there will be a socket problem. Originate returns false. Then I look at the number of freeswitch channels, but the number of channels has been 0. After we close the program, reopen the socket connection, socket can work for a while, but once the amount reached 100 or so, it will appear the above situation.Is something error with us?
regards.
When I send a playback command to FreeSWITCH with a non-existing file as a parameter, I will see an error in the console (logging), but the task object has Exception = NULL after awaiting it and no exception is thrown.
I think it should throw an exception when trying to playback a file that does not exist.
I am developing IVR application. I want to make multiple concurrent calls through gsm gateway. should i create multiple inbound socket instances to make concurrent calls or can i make concurrent calls through single socket instance and play audio files based on call id? could you please give any sample code?
Here is my sample code for make one call at time
using (var socket = await InboundSocket.Connect("localhost", 8021, "ClueCon"))
{
await socket.SubscribeEvents(EventName.ChannelHangupComplete, EventName.ChannelAnswer, EventName.Dtmf);
List<String> mobileNos = new List<String>();
mobileNos.Add("8940703144");
mobileNos.Add("8754006462");
foreach (var item in mobileNos)
{
var originate = await socket.Originate("sofia/internal/" + item + "@192.168.1.10",
new OriginateOptions
{
CallerIdName = "HSS",
IgnoreEarlyMedia = true
});
if (originate.Success)
{
var uuid = originate.ChannelData.UUID;
var playOptions = new PlayGetDigitsOptions()
{
MinDigits = 1,
MaxDigits = 1,
MaxTries = 3,
TimeoutMs = 6000,
PromptAudioFile = IvrMenuFilePath,
ValidDigits = "1,2,3"
};
playOptions.BadInputAudioFile = InvalidPhraseFilePath;
var playGetDigitsResult = await socket.PlayGetDigits(uuid, playOptions);
}
}
}
Is there a reason that the BridgeTo method swallows the result of the bridge instead of returning it? This makes getting the results of the bridge attempt challenging.
Hello,
I am trying to use this library for the windows service project at my work, but getting the following error:
Public member 'Where' on type 'Select(Of BasicMessage,EventMessage)' not found.
I am using an example for the Inbound Socket Client from the main page. The code looks like this:
Private Async Function SocketSetup() As Task
goSocket = Await InboundSocket.Connect(ConfigurationManager.AppSettings("Phone_Server_IpAddress"), System.Configuration.ConfigurationManager.AppSettings("Phone_Port"), "ClueCon")
Dim ApiResponse = Await goSocket.SendApi("status")
WriteTraceFile(ApiResponse.BodyText, 0)
Await goSocket.SubscribeEvents(EventName.ChannelHangupComplete)
goSocket.Events.Where(Function(x) x.EventName = EventName.ChannelHangupComplete).Subscribe(Async Sub(x)
Await ProcessCall(x.UUID, x.HangupCause)
End Sub)
End Function
goSocket is a global object. The error happens where it subscribes to events. Am I missing something? I tried in .NET 4.5 and 4.6.1. I got the latest version of System.Reactive.Linq. Can't figure it out.
Though the event socket docs show usages of api calls (uuid_foo
etc), most have a dialplan application that would be better suited (get channel execute complete events instead of fire + forget)
I'm trying to bridge a channel using an OutboundListener
but am having, so far, not much luck. As soon as I pick up the ringing phone the channel is gone. While looking for a solution I came across this:
Note about async mode
If you are using async, you need to pay attention to potential race condition. The commands you send may not execute in sequential order. You may force the command to wait:sendmsg call-command: set execute-app-name: foo=bar\n\n event-lock:true
And:
Q: Can I bridge a call with an Outbound socket?
Yes you can, simple execute a 'Bridge'sendmsg call-command: execute execute-app-name: bridge execute-app-arg: {ignore_early_media=true}sofia/gateway/myGW/177808 event-lock: true
The event-lock is key here, if you don't have it set, other events you might have sent can terminate the call, even if you've sent them before the bridge command. Remember that if you want to hangup the call when the bridge completes, either look for the event [CHANNEL_UNBRIDGE], or send a hangup event after the bridge.
Looking at the bridge implementation it seems to me that the eventlock
argument is not passed (e.g. defaults to false
) to the ExecuteApplication
method.
Could it be that this is my problem? If not, could you maybe show a simple example on how to bridge a channel with an OutboundListener?
I'll post my code as soon as I have created a stripped-down version to easily reproduce; may take a bit though ๐
This will be a big breaking change for v2 but a better solution than the cludge implemented here: #47 (comment)
When Play method called just after hang up occurs then there is no response from method.
I've found the following log for this call.
2015-03-07 15:40:26.9263|DEBUG|NEventSocket.InboundSocket|CommandReply received
[-ERR invalid session id [c1cdaeae-ebb0-4f3f-8f75-0f673bfbc046]] for [sendmsg c1
cdaeae-ebb0-4f3f-8f75-0f673bfbc046
Event-UUID: 0cbc2a67-8591-49bc-8f64-abe414744c4f
call-command: execute
execute-app-name: playback
content-type: text/plain
content-length: 29
I think there is a problem in ExecuteApplication method. it only returns response after receiving ChannelExecuteComplete event.
Hi
When user call FS and it open socket to manage the call , if call close befor init and calling Channels.Subscribe , it throw exception .
I figure out problem and trace source .
The errors occur in following classes :
Now tell me , my works is OK or should change . I'm afraid of unexpected behaviors . So follow me in correct way , and also I use it in running application and the errors i mentioned closed the socket , and i should restart app . I'm also looking for exception handling which if socket close and dispose , it restart it self again .
sorry for my poor english
I am able to reproduce this issue against the stable and latest codebases. Open an inbound socket and originate to a phone. Using Channel API, get channel for successfully originated call. Wait 10 seconds and call BridgeTo to bridge to a different phone number. Bridge is successful. Hangup bridged phone, and detect the hangup in Channel API. Wait 10 seconds and call BridgeTo to bridge to the phone number again. Bridge is successful. Wait 10 seconds and call BridgeTo a 3rd time. BridgeTo hangs and never returns, fs_cli shows no indication that it received the bridge command. Channel remains hung until you hangup the originally originated call, then BridgeTo returns and fs_cli shows the bridge command failing (obviously) because of the hangup. I have tried this countless times and it always hangs on the 3rd BridgeTo. I also tried setting the event-lock option on the bridge (as recommended by the FS docs for an outbound socket bridge, but it made no difference. Also, my original version of the code used only the inbound socket and no Channel API, but I had the same problem (which prompted me to try outbound sockets/Channel API).
This is a showstopper for me and I am at a loss, not being very fluent in the reactive extensions (I can only debug so far before I get in over my head). Any help is greatly appreciated.
Below is a console app to reproduce (.NET Core console app)- obviously substitute your own FS config and phone numbers.
Dial Plan:
<extension name="bridge_test">
<condition field="destination_number" expression="^5557$">
<action application="set" data="park_after_bridge=true" />
<action application="pre_answer" />
<action application="playback" data="{loops=2}tone_stream://%(300,25,440)" />
<action application="answer" />
<action application="socket" data="127.0.0.1:8084 async full" />
</condition>
</extension>
project.json:
{
"version": "1.0.0-*",
"buildOptions": {
"emitEntryPoint": true
},
"dependencies": {
"NETStandard.Library": "1.6.0",
"NEventSocket": "1.1.0",
"Rx-Main": "2.2.5"
},
"frameworks": {
"net452": {
}
}
}
Program.cs:
using NEventSocket;
using NEventSocket.Channels;
using NEventSocket.FreeSwitch;
using System;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace FSBridgeTest
{
public class Program
{
public static void Main(string[] args)
{
try
{
var cancellationTokenSource = new CancellationTokenSource();
Console.WriteLine("Starting bridge test- hit any key abort...");
var task = RunBridgeTest(cancellationTokenSource.Token);
Console.ReadKey();
Console.WriteLine("Shutting down...");
cancellationTokenSource.Cancel();
Console.WriteLine("Shutdown complete.");
task.Wait();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
Console.WriteLine("Hit any key exit...");
Console.ReadKey();
}
}
static async Task<bool> BridgeToPhone(Channel station, string phone)
{
var lineUUID = Guid.NewGuid().ToString("N");
var bridgeOptions = new BridgeOptions
{
CallerIdNumber = "1003",
CallerIdName = "Bridge Test",
HangupAfterBridge = true,
IgnoreEarlyMedia = false,
TimeoutSeconds = 30,
UUID = lineUUID
};
string endpoint = $"sofia/gateway/my-gateway/{phone}";
Console.WriteLine($"Bridging {phone} to station channel {station.UUID}...");
await station.BridgeTo(
endpoint,
bridgeOptions);
if (!station.IsBridged)
{
Console.WriteLine($"Bridge failed.");
return false;
}
else
{
Console.WriteLine($"Bridge successful.");
return true;
}
}
static Task RunBridgeTest(CancellationToken token)
{
var bridgeTestCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token);
return Task.Run(async () =>
{
try
{
Console.WriteLine("Bridge test starting...");
using (var client = await InboundSocket.Connect())
{
using (var listener = new OutboundListener(8084))
{
listener.Channels.Subscribe(
async station =>
{
try
{
Console.WriteLine($"Outbound socket connected for station channel {station?.UUID}.");
station.HangupCallBack = (evt) =>
{
var hangupCause = evt.HangupCause?.ToString() ?? "Unknown";
Console.WriteLine($"Station hangup detected for station channel {station?.UUID}- {hangupCause}.");
};
station.BridgedChannels.Subscribe(
line =>
{
try
{
Console.WriteLine($"Outbound socket connected for line channel {line?.UUID}.");
// Handle line hangup.
line.HangupCallBack = async (evt) =>
{
var hangupCause = evt.HangupCause?.ToString() ?? "Unknown";
Console.WriteLine($"Line hangup detected for line channel {line?.UUID}- {hangupCause}.");
await Task.Delay(10000);
await BridgeToPhone(station, "12155551212"); // Your cellphone here
};
}
catch (OperationCanceledException)
{
Console.WriteLine($"Outbound socket disconnected for line channel {line?.UUID}.");
}
});
await Task.Delay(10000);
await BridgeToPhone(station, "12155551212"); // Your cellphone here.
}
catch (OperationCanceledException)
{
Console.WriteLine($"Outbound socket disconnected for station channel {station?.UUID}.");
}
});
listener.Start();
var stationUUID = Guid.NewGuid().ToString("N");
var originateOptions = new OriginateOptions
{
CallerIdNumber = "1003",
CallerIdName = $"Station 1003",
HangupAfterBridge = false,
TimeoutSeconds = 20,
UUID = stationUUID
};
Console.WriteLine($"Originating to station channel {stationUUID}.");
var originate = await client.Originate(
"user/1003",
"5557",
"XML",
"default",
originateOptions);
if (!originate.Success)
{
var hangupCause = originate.HangupCause?.ToString() ?? "Unknown";
throw new Exception($"Originate failed: {hangupCause}");
}
Console.WriteLine($"Origination to station channel {stationUUID} complete.");
await Task.Delay(Timeout.Infinite, bridgeTestCancellationTokenSource.Token).ConfigureAwait(false);
}
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine("Bridge test complete.");
});
}
}
}
Hi guys,
We started using another freeswitch server and getting this error upon InboundSocket.Connect()
What could be the issue?
Thanks,
hi:
when I use this command"sofia status profile internal reg", socket will be disconnect.
var socket = await InboundSocket.Connect(freeswitchAddr, freeswitchESPort, freeswitchESPassword);
socket .Disposed += socket _Disposed;
socket .SendApi("sofia status profile internal reg")
//when run finish this ,socket _Disposed will be trigger.
sorry,my english is not goodใโฆ^~^
I think the returned ErrorMessage should be either trimmed, or this behavior should be documented.
For example uuid_break [channel]
returns -ERR no reply\n
which gets converted to no reply\n
.
I have a windows service that is constantly connecting to FreeSwitch and monitoring the servers. Unfortunately, when the FreeSwitch boxes fail over or are rebooted I keep hitting an ArgumentNullException in the GuardAgainstNullLogger method. I am getting logs out of log4net. It is only when it calls the dispose method that it fails and throws this error. Any ideas?
I need to send instances of classes like OriginateOptions
and BridgeOptions
over Akka, but their don't serialize completely. I see two way:
parameters
public like ChannelVariables
.P.S. Sorry for my english :)
I would like to propose that a HangupCompleteCallback handler be added to the Channel API, moving the Dispose() call here from the HangupCallback() handler. The reason being, in my particular application I need to log CDR data from within the app, and this data is presented during the ChannelHangupComplete event. Unfortunately, as currently coded, the channel is disposed when the ChannelHangup event is detected which causes the channel to be disposed before the ChannelHangupComplete event can be handled.
We have another issue. We managed to run our service on another freeswitch server and we are getting HangupCause.UnallocatedNumber for all calls. This is probably freeswitch setup issue but was curious if guys have seen this before.
Thanks,
I took this code from your repo, just added NLog package. When Connect
method is called, an exception is thrown (System.TimeoutException, Message = "No Auth Request received within the specified timeout of 00:00:05.")
If I remove NLog it works just fine.
Sometimes if I call any log method befor calling Connect
it also works.
I tested this behavior on 2 computers.
VS 2015, .NET 4.5 / 4.5.1 / 4.5.2
public class Program
{
static void Main(string[] args)
{
ApiTest();
Console.ReadLine();
}
private static async void ApiTest()
{
using (var client = await InboundSocket.Connect("192.168.21.100", 8021, "mypassword"))
{
Console.WriteLine((await client.SendApi("status")));
Console.WriteLine((await client.SendApi("blah")));
Console.WriteLine((await client.SendApi("status")));
}
}
I thought it might be something wrong with the time needed to initialize the logger for the first time, that is why I created dummy logger with static ctor with Thread.Sleep(2000) inside, but with this one it works just fine.
I hope this explanation is enough and maybe you can help me fixing this ;)
Is there a reason why BridgedChannels/OtherLeg are not updated appropriately (i.e. remove channel from BridgedChannels/set OtherLeg to null) when a channel is unbridged? In my application, I have a channel generated from an origination to a user, and this channel remains connected indefinitely. While connected, the channel will periodically call BridgeTo to initiate a bridge with an external phone number allowing the user to handle the call. When the call ends (i.e. the external phone hangs up), BridgeTo is called again to connect with a different external phone. This goes on indefinitely without the original user channel hanging up. In this scenario, BridgedChannels will just grow continually, and OtherLeg remains populated even when there is no longer an active bridge. I am really only running into an issue bevcause I am throw an exception if OtherLeg is not null when the user attempts to start a new bridge (since I incorrectly assume one is already in progress). I could probably work around this, but the perpetually growing BridgedChannels concerns me. Wont this also prevent those previously bridged channels from getting garbage collected?
Allow bridging calls in a pre-answer state.
@Anber did you want to open a pull request to merge in your changes for this?
Hi!
Tried NEventSocket and I am amazed. It is just that on Inbound connection I get no subscribe events. Tried with simple example on read me, but I cannot get any event. Otherwise sending commands works ok.
Can you please tell me how should I found out what is wrong?
Related to #8
If the socket closes down or errors, then any message observables should complete. It's likely that this will happen before FreeSwitch fails to send a message in due time.
Will keep this ticket open and wait for feedback from users.
In scriptcs, where async/await is not available, calling .Wait() on a long-running dial plan application such as conference
or refer
in the connections observable subscription blocks new connections.
Example:
using (var listener = new OutboundListener(8084))
{
var sub = listener.Connections.SubscribeOn(TaskPoolScheduler.Default).Subscribe(
socket => {
socket.Connect().Wait();
var uuid = socket.ChannelData.Headers[HeaderNames.UniqueId];
socket.SubscribeEvents().Wait();
socket.ExecuteApplication(uuid, "answer").Wait();
socket.ExecuteApplication(uuid, "conference", "test+1234").Wait(); //blocks
//socket.ExecuteApplication(uuid, "conference", "test+1234"); //does not block
});
listener.Start();
Console.WriteLine("Press [Enter] to exit.");
Console.ReadLine();
sub.Dispose();
}
The Outbound Socket Server example in the README has the following code:
socket.Events.Where(x => x.EventName == EventName.ChannelHangup)
.Take(1)
.Subscribe(x => {
Console.WriteLine("Hangup Detected on " + x.UUID);
socket.Exit();
});
It took me a while to figure out why, when I set up two calls, both hung up if I hung up either of them. In the end the solution couldn't be simpler but, as said, it took me a while to figure out what it was. I think the code should read:
socket.Events.Where(x => x.EventName == EventName.ChannelHangup && x.UUID == uuid)
.Take(1)
.Subscribe(x => {
Console.WriteLine("Hangup Detected on " + x.UUID);
socket.Exit();
});
Notice the Where
clause where I added && x.UUID == uuid
. (Maybe use a string.CompareOrdinal
or equivalent, but you get the idea).
Also: the "await await" in await await socket.SubscribeEvents();
should, I guess, read: await Task.Run(() => socket.SubscribeEvents());
. If I use "await await" the compiler says: Cannot await void
on that particular line and also says Cannot convert lambda expression to type 'System.IObserver<NEventSocket.OutboundSocket>
because it is not a delegate type*" on the async socket =>
line.
Please correct me if I'm wrong.
"No Auth Request received within the specified timeout of 00:00:05" exception thrown when using NLog with database logging. If i remove NLog then everything works fine.
var socket = new InboundSocket(host, port, timeout);
await
socket.Messages.Where(x => x.ContentType == ContentTypes.AuthRequest)
.Take(1)
.Timeout(
socket.ResponseTimeOut,
Observable.Throw(
new TimeoutException(
"No Auth Request received within the specified timeout of {0}.".Fmt(socket.ResponseTimeOut))))
.Do(_ => Log.Trace(() => "Received Auth Request"), ex => Log.ErrorException("Error waiting for AuthRequest.", ex))
.ToTask();
In these statements, If there is no delay in execution between these two statements it just works fine. and if there is a delay executing the second statement then it always fails.
for example, if we add the "System.Threading.Thread.Sleep(50);" in between the above two statements then second statement always fails and throws "No Auth Request received within the specified timeout of 00:00:05" exception.
DEBUG 15/02/03 20:00:54 079 [7] - NEventSocket.Channels.Channel:Channel 576fd745-b4af-49ef-8048-4c677f0864d2 is attempting a bridge to user/oper_1
DEBUG 15/02/03 20:00:54 104 [7] - NEventSocket.OutboundSocket:Sending [sendmsg 576fd745-b4af-49ef-8048-4c677f0864d2
Event-UUID: 659bb4bd-316f-4094-8667-b901fe02cb88
call-command: execute
execute-app-name: bridge
content-type: text/plain
content-length: 68{origination_uuid='d126f06d-9bc7-4b80-b1f4-b88c0910d293'}user/oper_1
]
A-leg hangup here
DEBUG 15/02/03 20:01:08 763 [26] - NEventSocket.OutboundSocket:Messages Received [command/reply].
DEBUG 15/02/03 20:01:08 775 [26] - NEventSocket.OutboundSocket:CommandReply received [+OK] for [sendmsg 576fd745-b4af-49ef-8048-4c677f0864d2
Event-UUID: 659bb4bd-316f-4094-8667-b901fe02cb88
call-command: execute
execute-app-name: bridge
content-type: text/plain
content-length: 68{origination_uuid='d126f06d-9bc7-4b80-b1f4-b88c0910d293'}user/oper_1
]
DEBUG 15/02/03 20:01:08 805 [26] - NEventSocket.OutboundSocket:Messages Received [text/event-plain].
DEBUG 15/02/03 20:01:08 811 [26] - NEventSocket.OutboundSocket:Events Received [d126f06d-9bc7-4b80-b1f4-b88c0910d293] [ChannelCreate]
FreeSWITCH version: 1.4.15~64bit ( 64bit)
Currently an EventSocket
subscribes to a few events for convenience:
private readonly HashSet<EventName> subscribedEvents = new HashSet<EventName>()
{
EventName.ChannelExecuteComplete,
EventName.BackgroundJob,
EventName.ChannelHangup,
EventName.ChannelAnswer,
EventName.ChannelProgress,
EventName.ChannelProgressMedia,
EventName.ChannelBridge,
EventName.ChannelUnbridge
};
In high-traffic/high-performance scenarios, users may wish to subscribe to only the minimum events.
Requirement: an EventSocket
should subscribe to events lazily as and when they are required, for example, when I do an Originate
it should, if necessary, subscribe to the BackgroundJob
and ChannelExecuteComplete
events if not already done so.
This is just a suggestion and it would make it a bit easier (for me) to contribute. As I can see, the CI scripts run an explicit NuGet restore
and Visual Studio 2015 is now also able to restore the NuGet packages automatically, so I would suggest to remove them from the repository. I am happy to provide a pull request, if this is acceptable.
CchannelState variable can be null. Header doesn't have value in CUSTOM events
Can you plese set ChannelState property to nullable like AnswerState.
Thank you!
Can you please add Reporting to ChannelState enum in Channelstate.cs
Best regards, Gregor
I'm slowly getting up to speed using the library. During some initial testing, I've come across the following error message, which happens to pop up quite often:
TRACEOBJECTPOOLLEAKS_BEGIN
Pool detected potential leaking of System.Text.StringBuilder.
Is it a known issue?
I was basically hanging up a SIP call using the following socket message:
await _socket.Hangup(_callUuid);
The hangup worked, but I did get those potential leak warnings. Here's the complete log:
NLOG: EventSocket initialized
NLOG: NEventSocket.InboundSocket Worker Thread 1 started
NLOG: Messages Received [auth/request].
NLOG: Received Auth Request
NLOG: Sending [auth ClueCon]
NLOG: Messages Received [command/reply].
NLOG: CommandReply received [+OK accepted] for [auth ClueCon]
NLOG: InboundSocket authentication succeeded.
NLOG: Sending [api status]
NLOG: Messages Received [api/response].
NLOG: ApiResponse received [UP 0 years, 0 days, 4 hours, 30 minutes, 13 seconds, 314 milliseconds, 867 microsecondsFreeSWITCH (Version 1.7.0 64bit) is ready23 session(s) since startup2 session(s) - peak 4, last 5min 2 0 session(s) per Sec out of max 30, peak 2, last 5min 0 1000 session(s) maxmin idle cpu 0.00/96.26] for [status]
UP 0 years, 0 days, 4 hours, 30 minutes, 13 seconds, 314 milliseconds, 867 microseconds
FreeSWITCH (Version 1.7.0 64bit) is ready
23 session(s) since startup
2 session(s) - peak 4, last 5min 2
0 session(s) per Sec out of max 30, peak 2, last 5min 0
1000 session(s) max
min idle cpu 0.00/96.26
NLOG: Sending [event plain CHANNEL_ANSWER]
NLOG: Messages Received [command/reply].
NLOG: CommandReply received [+OK event listener enabled plain] for [event plain CHANNEL_ANSWER]
NLOG: Messages Received [text/event-plain].
Channel Answer Event 667c1091-c7fe-4657-83a7-9c82c2c6f490
NLOG: Messages Received [text/event-plain].
Channel Answer Event 44b1b7c2-ccb9-4d50-8955-f63a04cd39e1
NLOG: Sending [sendmsg 44b1b7c2-ccb9-4d50-8955-f63a04cd39e1
call-command: hangup
hangup-cause: NORMAL_CLEARING]
NLOG: Messages Received [command/reply].
NLOG: CommandReply received [+OK] for [sendmsg 44b1b7c2-ccb9-4d50-8955-f63a04cd39e1
call-command: hangup
hangup-cause: NORMAL_CLEARING]
TRACEOBJECTPOOLLEAKS_BEGIN
Pool detected potential leaking of System.Text.StringBuilder.
Location of the leak:
at NEventSocket.Util.ObjectPooling.ObjectPool`1.Allocate()
at NEventSocket.Util.ObjectPooling.SharedPoolExtensions.AllocateAndClear(ObjectPool`1 pool)
at NEventSocket.Util.ObjectPooling.StringBuilderPool.Allocate()
at NEventSocket.Sockets.Parser..ctor()
at NEventSocket.Sockets.EventSocket.<>c.<.ctor>b__7_1()
at NEventSocket.Util.ObservableExtensions.<>c__DisplayClass0_1`2.<AggregateUntil>b__1(TSource value)
at System.Reactive.AnonymousSafeObserver`1.OnNext(T value)
at System.Reactive.Linq.ObservableImpl.SelectMany`2.NoSelectorImpl.OnNext(TSource value)
at System.Reactive.Linq.ObservableImpl.Defer`1._.OnNext(TValue value)
at System.Reactive.Linq.ObservableImpl.AsObservable`1._.OnNext(TSource value)
at System.Reactive.Subjects.Subject`1.OnNext(T value)
at NEventSocket.Sockets.ObservableSocket.<>c__DisplayClass10_0.<<-ctor>b__1>d.MoveNext()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(Object stateMachine)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run()
at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(Action action, Boolean allowInlining, Task& currentTask)
at System.Threading.Tasks.Task.FinishContinuations()
at System.Threading.Tasks.Task.FinishStageThree()
at System.Threading.Tasks.Task`1.TrySetResult(TResult result)
at System.Threading.Tasks.TaskFactory`1.FromAsyncTrimPromise`1.Complete(TInstance thisRef, Func`3 endMethod, IAsyncResult asyncResult, Boolean requiresSynchronization)
at System.Threading.Tasks.TaskFactory`1.FromAsyncTrimPromise`1.CompleteFromAsyncResult(IAsyncResult asyncResult)
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.ContextAwareResult.CompleteCallback(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Net.ContextAwareResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
TRACEOBJECTPOOLLEAKS_END
TRACEOBJECTPOOLLEAKS_BEGIN
Pool detected potential leaking of System.Text.StringBuilder.
Location of the leak:
at NEventSocket.Util.ObjectPooling.ObjectPool`1.Allocate()
at NEventSocket.Util.ObjectPooling.SharedPoolExtensions.AllocateAndClear(ObjectPool`1 pool)
at NEventSocket.Util.ObjectPooling.StringBuilderPool.Allocate()
at NEventSocket.Sockets.Parser..ctor()
at NEventSocket.Sockets.EventSocket.<>c.<.ctor>b__7_1()
at NEventSocket.Util.ObservableExtensions.<>c__DisplayClass0_1`2.<AggregateUntil>b__1(TSource value)
at System.Reactive.AnonymousSafeObserver`1.OnNext(T value)
at System.Reactive.Linq.ObservableImpl.SelectMany`2.NoSelectorImpl.OnNext(TSource value)
at System.Reactive.Linq.ObservableImpl.Defer`1._.OnNext(TValue value)
at System.Reactive.Linq.ObservableImpl.AsObservable`1._.OnNext(TSource value)
at System.Reactive.Subjects.Subject`1.OnNext(T value)
at NEventSocket.Sockets.ObservableSocket.<>c__DisplayClass10_0.<<-ctor>b__1>d.MoveNext()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(Object stateMachine)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run()
at System.Threading.Tasks.AwaitTaskContinuation.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
TRACEOBJECTPOOLLEAKS_END
TRACEOBJECTPOOLLEAKS_BEGIN
Pool detected potential leaking of System.Text.StringBuilder.
Location of the leak:
at NEventSocket.Util.ObjectPooling.ObjectPool`1.Allocate()
at NEventSocket.Util.ObjectPooling.SharedPoolExtensions.AllocateAndClear(ObjectPool`1 pool)
at NEventSocket.Util.ObjectPooling.StringBuilderPool.Allocate()
at NEventSocket.Sockets.Parser..ctor()
at NEventSocket.Sockets.EventSocket.<>c.<.ctor>b__7_1()
at NEventSocket.Util.ObservableExtensions.<>c__DisplayClass0_1`2.<AggregateUntil>b__1(TSource value)
at System.Reactive.AnonymousSafeObserver`1.OnNext(T value)
at System.Reactive.Linq.ObservableImpl.SelectMany`2.NoSelectorImpl.OnNext(TSource value)
at System.Reactive.Linq.ObservableImpl.Defer`1._.OnNext(TValue value)
at System.Reactive.Linq.ObservableImpl.AsObservable`1._.OnNext(TSource value)
at System.Reactive.Subjects.Subject`1.OnNext(T value)
at NEventSocket.Sockets.ObservableSocket.<>c__DisplayClass10_0.<<-ctor>b__1>d.MoveNext()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(Object stateMachine)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run()
at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(Action action, Boolean allowInlining, Task& currentTask)
at System.Threading.Tasks.Task.FinishContinuations()
at System.Threading.Tasks.Task.FinishStageThree()
at System.Threading.Tasks.Task`1.TrySetResult(TResult result)
at System.Threading.Tasks.TaskFactory`1.FromAsyncTrimPromise`1.Complete(TInstance thisRef, Func`3 endMethod, IAsyncResult asyncResult, Boolean requiresSynchronization)
at System.Threading.Tasks.TaskFactory`1.FromAsyncTrimPromise`1.CompleteFromAsyncResult(IAsyncResult asyncResult)
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.ContextAwareResult.CompleteCallback(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Net.ContextAwareResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
TRACEOBJECTPOOLLEAKS_END
The thread 0x1af0 has exited with code 0 (0x0).
NLOG: Messages Received [text/event-plain].
Channel Answer Event 32aad671-f9d3-447e-b705-4542bf2d85bb
NLOG: Messages Received [text/event-plain].
Channel Answer Event 9fb3b24a-acfa-4df5-9312-a6f2bbb77bfc
NLOG: Sending [sendmsg 9fb3b24a-acfa-4df5-9312-a6f2bbb77bfc
call-command: hangup
hangup-cause: NORMAL_CLEARING]
NLOG: Messages Received [command/reply].
NLOG: CommandReply received [+OK] for [sendmsg 9fb3b24a-acfa-4df5-9312-a6f2bbb77bfc
call-command: hangup
hangup-cause: NORMAL_CLEARING]
NLOG: Messages Received [text/event-plain].
NLOG: Messages Received [text/event-plain].
Channel Answer Event 3c2fa28a-1986-493e-94da-89b93b9f596d
Channel Answer Event f36f7863-bee6-4aa6-ae2f-cf3a4cb84c8c
NLOG: Sending [sendmsg f36f7863-bee6-4aa6-ae2f-cf3a4cb84c8c
call-command: hangup
hangup-cause: NORMAL_CLEARING]
NLOG: Messages Received [command/reply].
NLOG: CommandReply received [+OK] for [sendmsg f36f7863-bee6-4aa6-ae2f-cf3a4cb84c8c
call-command: hangup
hangup-cause: NORMAL_CLEARING]
The program '[12788] FsSockets.Gui.Wpf.vshost.exe' has exited with code -1 (0xffffffff).
I'm trying to test the library with FreeSWITCH. I'm using the sample code, just subscribing to EventName.All, and checking if I can see the events. I noticed that I would only get an event or two, and then nothing (despite me placing SIP calls across FreeSWITCH, which should generate plenty).
To see what's going on, I added NLog to my test application, which resulted in the following output (pasted below).
It looks like the library throws 'Failed to parse body of event' exception and closes the socket (relevant class is here:
https://github.com/danbarua/NEventSocket/blob/38bcd41c3ae6443f73067967b226488112ee01a1/src/NEventSocket/FreeSwitch/EventMessage.cs)
Any ideas?
`NLOG: TEST CLICKED!!!
'FsSockets.Gui.Wpf.vshost.exe' (CLR v4.0.30319: FsSockets.Gui.Wpf.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Dynamic\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Dynamic.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'FsSockets.Gui.Wpf.vshost.exe' (CLR v4.0.30319: FsSockets.Gui.Wpf.vshost.exe): Loaded 'Anonymously Hosted DynamicMethods Assembly'.
NLOG: EventSocket initialized
NLOG: NEventSocket.InboundSocket Worker Thread 1 started
NLOG: Messages Received [auth/request].
NLOG: Received Auth Request
NLOG: Sending [auth ClueCon]
NLOG: Messages Received [command/reply].
NLOG: CommandReply received [+OK accepted] for [auth ClueCon]
NLOG: InboundSocket authentication succeeded.
NLOG: Sending [api status]
NLOG: NEventSocket.InboundSocket Worker Thread 1 started
NLOG: Messages Received [api/response].
NLOG: ApiResponse received [UP 0 years, 0 days, 0 hours, 56 minutes, 8 seconds, 345 milliseconds, 987 microsecondsFreeSWITCH (Version 1.7.0 64bit) is ready18 session(s) since startup2 session(s) - peak 3, last 5min 2 0 session(s) per Sec out of max 30, peak 2, last 5min 0 1000 session(s) maxmin idle cpu 0.00/96.94] for [status]
UP 0 years, 0 days, 0 hours, 56 minutes, 8 seconds, 345 milliseconds, 987 microseconds
FreeSWITCH (Version 1.7.0 64bit) is ready
18 session(s) since startup
2 session(s) - peak 3, last 5min 2
0 session(s) per Sec out of max 30, peak 2, last 5min 0
1000 session(s) max
min idle cpu 0.00/96.94
NLOG: Sending [event plain ALL]
NLOG: NEventSocket.InboundSocket Worker Thread 1 started
NLOG: Messages Received [command/reply].
NLOG: CommandReply received [+OK event listener enabled plain] for [event plain ALL]
NLOG: NEventSocket.InboundSocket Worker Thread 1 started
NLOG: Messages Received [text/event-plain].
NLOG: EVENT: RecvRtcpMessage
NLOG: Messages Received [text/event-plain].
NLOG: EVENT: RecvRtcpMessage
NLOG: Messages Received [text/event-plain].
NLOG: EVENT: RecvRtcpMessage
NLOG: Messages Received [text/event-plain].
NLOG: Messages Received [].
NLOG: Messages Received [text/event-plain].
NLOG: Failed to parse body of event
NLOG: Unexpected Error reading from stream
NLOG: NEventSocket.InboundSocket Worker Thread 1 completed
NLOG: Disposing NEventSocket.InboundSocket (disposing:True)
NLOG: TcpClient closed
NLOG: NEventSocket.InboundSocket Worker Thread 1 completed
NLOG: NEventSocket.InboundSocket Worker Thread 1 completed
NLOG: NEventSocket.InboundSocket (1) Disposed
NLOG:
NLOG: Unexpected Error reading from stream
NLOG: NEventSocket.InboundSocket Worker Thread 1 completed`
Todo: throw it up on NuGet
Consider breaking Channels out into a separate package.
This is probably more of a FS question but might be related to NEventSocket. Is it possible not to receive all the CHANNEL_HANGUP_COMPLETE events when subscribing using goSocket.Events.Where(Function(x) x.EventName = EventName.ChannelHangupComplete)? Basically we have a windows service, which continually dials records from a DB as they come in. It updates them when we receive CHANNEL_HANGUP_COMPLETE by checking for the variable_uuid, which is a record id that we are sending via bgapi string.
So we send a bunch of bgapi strings using socket.SendCommand() up to specified limit as to not overload FS. We store the record ID in the list and it gets removed when we receive a response from FS. Some of those variable_uuid do not come in and we have been trying a bunch of solutions, including NEventSocket.
I am having an issue where hang up is not being detected. I have added a class to your Examples project, based on your VoiceBlaster sample, which simply dials an internal station (VOIP softphone), plays a tone on answer, and then simply looks for a hang up. Hanging up the softphone seems to do nothing and I am at a loss. I don't know if I am doing something wrong, or if there is some issue detecting the hang up. If I just dial a number via the softphone and answer that call, then hang up the softphone, FreeSWITCH detects the hang up and ends the call, so I know that at least my basic setup seems ok. Any help would be greatly appreciated. Below is the class I added to the examples:
namespace NEventSocket.Examples.Examples
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
using ColoredConsole;
using Net.CommandLine;
using NEventSocket.FreeSwitch;
public class MyTest : ICommandLineTask, IDisposable
{
private int currentCallCount = 0;
public async Task Run(CancellationToken cancellationToken)
{
var ourCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
using (var client = await InboundSocket.Connect())
{
Console.WriteLine("Authenticated!");
await client.SubscribeEvents(EventName.ChannelHangup, EventName.BackgroundJob);
client.Events.Where(x => x.EventName == EventName.ChannelHangup)
.Subscribe(x => { Console.WriteLine($"Hangup Detected : {x.GetVariable("ccs_station_id")} {x.HangupCause}"); });
using (var listener = new OutboundListener(8084))
{
listener.Connections.Subscribe(
async socket =>
{
try
{
await socket.Connect();
var uuid = socket.ChannelData.UUID;
await socket.Filter(HeaderNames.UniqueId, uuid);
Console.WriteLine($"OutboundSocket connected for channel {uuid} {socket.ChannelData.GetVariable("ccs_station_id")}");
await socket.ExecuteApplication(uuid, "pre_answer");
await socket.Play(uuid, "{loops=2}tone_stream://%(300,25,440)");
await socket.ExecuteApplication(uuid, "answer");
}
catch (OperationCanceledException)
{
//hangup - freeswitch disconnected from us
}
});
listener.Start();
var checkCallCount = new Task(
async () =>
{
try
{
while (!ourCancellationToken.IsCancellationRequested)
{
var res = await client.SendApi("show calls count");
Console.WriteLine("Current Calls Count " + Convert.ToInt32(res.BodyText.Split(' ')[0]));
currentCallCount = Convert.ToInt32(res.BodyText.Split(' ')[0]);
await Task.Delay(5000);
}
}
catch (OperationCanceledException)
{
//shutdown
}
});
checkCallCount.Start();
try
{
await RegisterAgent(client, "1001");
}
catch (OperationCanceledException)
{
//shutdown
}
ColorConsole.WriteLine("Press [Enter] to exit.".Green());
await Util.WaitForEnterKeyPress(cancellationToken);
ourCancellationToken.Cancel();
listener.Dispose();
}
}
}
private async Task RegisterAgent(InboundSocket client, string stationId)
{
Console.WriteLine("Registering agent : " + stationId);
var originateOptions = new OriginateOptions
{
CallerIdNumber = "8560000000",
CallerIdName = "My CID",
HangupAfterBridge = false,
IgnoreEarlyMedia = true,
TimeoutSeconds = 20
};
originateOptions.ChannelVariables["ccs_station_id"] = stationId;
var originateResult =
await
client.Originate(
$"user/{stationId}",
originateOptions,
"socket",
"127.0.0.1:8084 async full");
if (!originateResult.Success)
{
Console.WriteLine($"Registration failed to initiate : {stationId} {originateResult.ResponseText}");
}
else
{
Console.WriteLine($"Agent successfully registered : {stationId}");
}
}
public void Dispose()
{
}
}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.