How do you request user input using Rx? - c#

I am writing a class that encapsulates a state machine that represents my applications attempts to communicate with my own web service. Basic states are disconnected, connecting, connected, and requiring credentials. I thought for good learning experience about using Rx to publish state changes with a BehaviorSubject (hopefully this in itself is good usage?). One of my states for this subject is the 'Requiring Credentials' where I need to prompt the user to provide a username and password before continuing. For the purposes of this exercise let us assume that the class is hosted in a Windows Console Application and that I want to prompt the user with
Console.WriteLine("Enter username");
var userName = Console.ReadLine();
Console.WriteLine("Enter password");
var password = Console.ReadLine();
As a result of my Rx BehaviorSubject hitting the state 'RequiresCredentials' and it should hit this every time this state is hit.
I have exposed the BehaviorSubject as itself (i.e. haven't hidden it behind an Observable interface or anything)
public BehaviorSubject<ConnectionState> State { get; }
and want this request for credentials to be blocking.
What process should I use for subscribing to this subject correctly, waiting for this input and finally returning control back to the calling thread?

If you're not using tasks or await, or anything asynchronous, Rx subscription code will observe on the same thread, and block if you're using blocking code. If you don't care about the input, then you can do a straight Subscribe. If you want to use the inputted username/password, then you can use Select. So this would work:
class Program
{
static void Main(string[] args)
{
var subject = new BehaviorSubject<ConnectionState>(ConnectionState.Disconnected);
var getCredentials = subject.Where(cs => cs == ConnectionState.RequiresCredentials)
.Select(cs =>
{
Console.WriteLine("Enter username");
var userName = Console.ReadLine();
Console.WriteLine("Enter password");
var password = Console.ReadLine();
return Tuple.Create(userName, password);
});
using (var subscription = getCredentials.Subscribe())
{
Console.WriteLine("Changing to Connecting...");
subject.OnNext(ConnectionState.Connecting);
Console.WriteLine("Changing to RequiresCredentials...");
subject.OnNext(ConnectionState.RequiresCredentials);
Console.WriteLine("Connected.");
subject.OnNext(ConnectionState.Connected);
}
}
}
enum ConnectionState
{
Disconnected,
Connected,
Connecting,
RequiresCredentials
}
It produces the following output:
Changing to Connecting...
Changing to RequiresCredentials...
Enter username
ThisIsMyUserName
Enter password
ThisIsMyPassword
Connected.
This is a rather backwards use of Rx though: If you're looking to use Rx in an iterative, procedural way, why don't you just use C# and save yourself complexity?

Related

Multiple Conversations For Direct Line Client

I'm trying to use the Microsoft.Bot.Connector.DirectLine .NET client to connect to my Direct Line Channel. My client application will have many conversations open at once (like 1000+).
What I'm trying to do is efficiently create a single Direct Line client object which can receive messages for all my conversations and NOT have a single client per conversation.
This below code is from:
https://learn.microsoft.com/en-us/azure/bot-service/bot-service-channel-directline-extension-net-client?view=azure-bot-service-4.0
The problem is that to create a new conversation I need to create a new client which I think would eventually exhaust use up a lot of sockets. Does anyone know if I can create a single connection and then listen for multiple conversations?
Thanks
static async Task Main(string[] args)
{
Console.WriteLine("What is your name:");
var UserName = Console.ReadLine();
var tokenClient = new DirectLineClient(
new Uri(endpoint),
new DirectLineClientCredentials(secret));
var conversation = await tokenClient.Tokens.GenerateTokenForNewConversationAsync();
var client = new DirectLineClient(
new Uri(endpoint),
new DirectLineClientCredentials(conversation.Token));
await client.StreamingConversations.ConnectAsync(
conversation.ConversationId,
ReceiveActivities);
var startConversation = await client.StreamingConversations.StartConversationAsync();
var from = new ChannelAccount() { Id = startConversation.ConversationId, Name = UserName };
var message = Console.ReadLine();
while (message != "end")
{
try
{
var response = await client.StreamingConversations.PostActivityAsync(
startConversation.ConversationId,
new Activity()
{
Type = "message",
Text = message,
From = from,
ChannelData = new Common.ChannelData() { FromNumber = "+17081234567"}
});
}
catch (OperationException ex)
{
Console.WriteLine(
$"OperationException when calling PostActivityAsync: ({ex.StatusCode})");
}
message = Console.ReadLine();
}
Console.ReadLine();
}
public static void ReceiveActivities(ActivitySet activitySet)
{
if (activitySet != null)
{
foreach (var a in activitySet.Activities)
{
if (a.Type == ActivityTypes.Message && a.From.Id == "MyBotName")
{
Console.WriteLine($"<Bot>: {a.Text}");
}
}
}
}
I think using the Direct Line streaming extensions would be problematic for your purposes. I'm guessing your custom SMS channel would itself be an app service. Since an app service can (and probably should, in your case) be scaled so that multiple instances are running simultaneously, suppose two SMS messages from the same conversation go to two instances of your channel. In addition to having each instance of your channel using many web sockets to talk to many bots, multiple instances of your channel may use duplicated web sockets to talk to the same bot. There's also the problem of each bot itself needing to support streaming extensions.
Rather than using using Direct Line streaming extensions, you might consider using traditional Direct Line. This would involve receiving activities from the bots by polling a Direct Line endpoint.
Since Direct Line is a channel itself that you'd be using on top of your own channel, you might also consider cutting out Direct Line altogether. That way you wouldn't have two channels between the user and the bot. You could send HTTP requests to each bot's endpoint directly, and the activities the bots would receive would contain the service URL for your channel, allowing your channel to receive messages from the bots.

Console.ReadLine doesn't wait for input

I was writing this code and noticed that the Console.ReadLine() command doesn't wait for input. Those lines are being executed for sure but for some reason the console just closes on me without being able to type.
The class is set to be console application.
public void Register()
{
Console.WriteLine("Enter User Name to Register:");
String nickname = Console.ReadLine();
User newUser = new User(nickname); //todo: check if it doesn't already exist//
this.userList.Add(newUser);
persistantLayer.Saver.saveUser(newUser);
}

Discord C# User Join messages

I'm using Discord.Net in C#, making a bot. My bot works fantastic so far, but I want it to automatically assign users a specific role when they join a specific server. I've never actually learned any C#, only a bit of C++ so I know the basic Grammar. How would I go about this?
I'm assuming I would use UserJoined, but doing this heeds results telling me to use it before or after a += or -+ (Which I understand, but I don't understand it's usefullness in this given scenario)
You gave little information to work with but here is how to do it in all releases (so far):
This is IN the dependency map but below the "handlecommand", CommandHandleAsync or HandleCommandAsync:
client.UserJoined += AnnounceJoinedUser; //Hook into the UserJoined event of the client.
This is under the dependency map:
public async Task AnnounceJoinedUser(SocketGuildUser user) //Welcomes the new user
{
var channel = client.GetChannel(/*/TextChannelID/*/) as SocketTextChannel; // Gets the channel to send the message in
await channel.SendMessageAsync($"Welcome {user.mention} to {channel.Guild.Name}"); //Welcomes the new user
}
In case any of you wanted to send a message directly to the joining user
client.UserJoined += HandleUserJoinedAsync;
private async Task HandleUserJoinedAsync(SocketGuildUser gUser)
{
if (gUser.IsBot || gUser.IsWebhook) return;
var dmChannel = await gUser.GetOrCreateDMChannelAsync();
await dmChannel.SendMessageAsync("Witaj");
}
For all those who need an answer, in this period, I leave you this piece of code, just to send a message to a user's join, (1 line):
Client.UserJoined += join;
private async Task join(SocketGuildUser user)
{
await (user.Guild.DefaultChannel).SendMessageAsync("Text")
return;
}

SignalR Client Trigger Events

I'm trying to replicate behavior like a client browser but in C# (Performance reason). What I'm trying to set out to achieve is that for every new events received, my program should trigger the server side (Hub) which would then notify the client. Rather than having a while loop which would repeatedly hit the hub method every time even if theres no messages, is there a way to treat it as a trigger/detection so that once message is detected then execute Hub method ? Hope this makes sense
Snapshot of Client code below:
IHubProxy _hub;
string url = #"http://localhost:8080/";
var connection = new HubConnection(url);
_hub = connection.CreateHubProxy("PersonHub");
connection.Start().Wait();
//client side method
_hub.On("checkedIn", x => Console.WriteLine(x));
Console.WriteLine("Enter Person Name");
var answer = Console.ReadLine();
while (true) // Better way doing this? trigger or detect new message?
{
//server side method
_hub.Invoke("GetByName", answer).Wait();
}
Snapshot of Hub code below:
[HubName("PersonHub")]
public class PersonHub: Hub
{
public Person GetByName(string name)
{
//logic and etc ...
Clients.All.checkedIn(name);
}
}
By setting the while loop to true this means this will always call the server side method (Hub method) which I dont want to. If theres new events triggered then it should hit the hub method. Is there a way to somehow listen for new message but not to execute if no messages has been detected?
A possible solution is:
string line;
while ((line = Console.ReadLine()) != "exit")
{
_hub.Invoke("GetByName", line).Wait();
}

Reliably identifying and tracking Asterisk calls using C# and Aster.NET

I have been building a WinForms desktop application using C# that interfaces with Asterisk using Aster.NET (formerly/forked from Asterisk.NET). We're having real trouble reliably identifying and tracking calls that are related to an individual extension/user.
The problem we're having is due to the unpredictable/fuzzy nature of the events fired/triggered by Asterisk, with them being massively variable depending on how the call is routed before it hits an extension.
For example, the event sequence/format is different when: a call hits an IVR before getting blind transferred; if a call hits an IVR before it is attended transferred; if a call goes direct to the user's extension.
This is further hampered by the way that Asterisk tracks each side of the call using a different Unique ID (e.g. the incoming side of the call has a different UID than the received side of the call). Whilst we've managed to account for that in the (subsequently ugly!) code, we're still hitting issues with accounting for the different routing paths the call can take.
As such, I'm looking for any advice on how we can do the following:
Reliably identify an incoming call to a user's extension
We need to be able to identify the extension being called and the originating caller ID (after either a blind or attended transfer and direct call from external)
Reliably track the Unique ID for that incoming call as it's used to link to the call recording
Reliably identify an outgoing call from a user's extension
With the same caveats as above in mind
As it stands at the minute we have an extremely complex chain of event handlers that operate differently dependent on the 'current state' of the app.
To give one example: if we detect a NewStateEvent with a ChannelState of 6 ('Up'), we check if there is an ongoing call in process and that the UIDs match, and if so then the current call has been answered. If the UIDs don't match, but other factors do (e.g. channel, connectedlinenum, etc), then we pick this up as being the 'other side' of the call (i.e. the receiving or incoming side).
I'm not sure if the problem lies with the API or with AMI - but whichever it is it's causing us some real headaches.
Any advice greatly appreciated.
Is it possible for you to update to Asterisk 12? The channel names in AMI are now stable in Asterisk 12.
https://wiki.asterisk.org/wiki/display/AST/AMI+v2+Specification
i'm using package Aster.NET in c# . firstly install latest package of aster.net
than check that code .this code work perfect for me .
manager = new ManagerConnection(address, port, user, password);
manager.UnhandledEvent += new ManagerEventHandler(manager_Events);
manager.NewState += new NewStateEventHandler(Monitoring_NewState);
try
{
// Uncomment next 2 line comments to Disable timeout (debug mode)
// manager.DefaultResponseTimeout = 0;
// manager.DefaultEventTimeout = 0;
manager.Login();
if (manager.IsConnected())
{
Console.WriteLine("user name : " + manager.Username);
Console.ReadLine();
}
}
catch (Exception ex)
{
Console.WriteLine("Error connect\n" + ex.Message);
manager.Logoff();
Console.ReadLine();
}
void manager_Events(object sender, ManagerEvent e)
{
Console.WriteLine("Event : " + e.GetType().Name);
}
void Monitoring_NewState(object sender, NewStateEvent e)
{
string state = e.State;
string callerID = e.CallerId;
Console.WriteLine("caller num ...", e.CallerIdNum);
//Console.WriteLine("state =", state);
//Console.WriteLine("callerID =", callerID);
if ((state == "Ringing") | (e.ChannelState == "5"))
{
Console.WriteLine("hello rining your phone now ...");
String connectedLineNum;
String connectedLineName;
Dictionary<String, String> attributes = e.Attributes;
attributes.TryGetValue("connectedlinenum", out connectedLineNum);
attributes.TryGetValue("connectedlinename", out connectedLineName);
// "callerID" - called phone number
// "connectedLineNum" - calling phone number
// CallIn. Incoming call
}
else if ((state == "Ring") | (e.ChannelState == "4"))
{
Console.WriteLine("hello out going your call ...");
// CallOut. Outcoming call
}
else if ((state == "Up") | (e.ChannelState == "6"))
{
String connectedLineNum;
String connectedLineName;
Dictionary<String, String> attributes = e.Attributes;
attributes.TryGetValue("connectedlinenum", out connectedLineNum);
attributes.TryGetValue("connectedlinename", out connectedLineName);
// "callerID" - called phone number
// "connectedLineNum" - calling phone number
// human lifted up the phone right no
Console.WriteLine("human lifted up the phone...");
}
}

Categories