Send email from Xamarin.Forms app using Dependency Service - c#

In my app, for debugging purposes and testing purposes I need to send an email.
How do I present the mail controller, when the class that sends it does not contain a definition for PresentViewController ? The class needs to fire off the email app after the user clicks "yes" from the alert which fires.
public async Task<bool> SendEmail(Exception ex)
{
var result = await SendNotificationToRequestSendingEmail();
if (result)
{
if (MFMailComposeViewController.CanSendMail)
{
mailController = new MFMailComposeViewController();
mailController.SetToRecipients(new string[] { "test#one.com", "test#two.com" });
mailController.SetSubject("Details");
mailController.SetMessageBody($"Message: {ex.Message}" +
$"Exception: {ex.ToString()}"
, false);
mailController.Finished += (object s, MFComposeResultEventArgs args) =>
{
Console.WriteLine(args.Result.ToString());
args.Controller.DismissViewController(true, null);
};
this.PresentViewController(mailController, true, null); //this line causes the error
}
}
return true;
}
How can I fix this problem or get around it? This is called from a Xamarin Forms page.

Please take a look at this answer:
Sending e-mail from Gmail in Xamarin.Forms app
besides that you can also do it with this NuGet package:
https://www.nuget.org/packages/Xam.Plugins.Messaging/

Related

SignalR client (Xamarin) doesn't get messages when sending to a specific user

My problem is:
I have a WPF app and a Xamarin Forms app. I want to create a chat. And it works when I call from WPF to a method that should send a message to every user. But it doesn't when I choose a specific user. In that case the "On" method on the Xamarin site is not fired. Probably I messed up something with logging a user. Here is my code.
Xamarin
var hubConnection = new HubConnection("my_url");
hubConnection.Headers.Add("login", CacheUtils.User.login);
ChatHubProxy = hubConnection.CreateHubProxy("ChatHub");
await hubConnection.Start();
ChatHubProxy.On<string, string>("UpdateChatMessage", (message, username) =>
{
Device.BeginInvokeOnMainThread(() =>
{
MessagingCenter.Send(this, "AddMessage", new Message() { Text = message, User = username });
});
});
string lConnectionId = await ChatHubProxy.Invoke<string>("Login");
hubConnection.ConnectionId = lConnectionId;
So I basically create a connection and subscribe to an "On" method. In the header I pass a user login. As I said before receiving messages works when the message is for everyone.
On the server side I first call a method "Login" which adds user to my ConcurrentDictionary. Then in a method "SendMessageToSpecificUser" I get user from this dictionary I call "UpdateChatMessage".
Server side
private static ConcurrentDictionary<string, string> clients = new ConcurrentDictionary<string, string>();
public void SendMessage(string message, string username)
{
Clients.All.UpdateChatMessage(message, username); //this works
}
public void SendMessageToSpecificUser(string message, string login)
{
string lUserId = clients.Where(x => x.Value == login).FirstOrDefault().Key;
if (!string.IsNullOrEmpty(lUserId))
Clients.User(lUserId).UpdateChatMessage(message, login); //this doesn't work
}
public string Login()
{
string username = Context.Headers.Get("login");
if (clients.ContainsKey(username))
((IDictionary)clients).Remove(username);
clients.TryAdd(Context.ConnectionId, username);
return Context.ConnectionId;
}
Any help appreciated :)
Ok, I found the solution. I should use:
Clients.Client(lUserId).UpdateChatMessage(message, login);
instead of
Clients.User(lUserId).UpdateChatMessage(message, login);

Open browser window on click

I am using botframework 3.9 and for some reasons I can't upgrade now. I would like to know if there is a way to open a new browser window where I can render a page or fire a JavaScript function. Here is how I am opening link:
await context.PostAsync(#"please [click here](http://www.example.com/)");
This does renders the link however, I wanna open a link in JavaScript window so I can close the window programmatically or if possible if I can fire some JavaScript function.
This is actually much easier than you think. If you have a look at the WebChat README you can see there are many ways WebChat can be customized. Pay particular attention to sample 11, which you can demo here. The body of that page looks like this:
<div id="webchat" role="main"></div>
<script>
(async function () {
// In this demo, we are using Direct Line token from MockBot.
// To talk to your bot, you should use the token exchanged using your Direct Line secret.
// You should never put the Direct Line secret in the browser or client app.
// https://learn.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-authentication
const res = await fetch('https://webchat-mockbot.azurewebsites.net/directline/token', { method: 'POST' });
const { token } = await res.json();
// We are creating our own version of Redux store, which include middleware and optional initial state.
const store = window.WebChat.createStore(
{},
({ dispatch }) => next => action => {
if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
// After connected, we will send a message by dispatching a Redux action.
dispatch({ type: 'WEB_CHAT/SEND_MESSAGE', payload: { text: 'sample:backchannel' } });
} else if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
// When receiving an activity of type "event" with name of "sample:backchannel", prompt it out.
const { activity } = action.payload;
if (activity.type === 'event' && activity.name === 'sample:backchannel') {
alert(JSON.stringify(activity, null, 2));
}
}
return next(action);
}
);
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token }),
// We will use a custom version of Redux store, which we added middleware to handle backchannel messages.
store
}, document.getElementById('webchat'));
document.querySelector('#webchat > *').focus();
})().catch(err => console.error(err));
</script>
You can see in this sample that WebChat has been modified to respond to certain activities from the bot by opening a popup window using JavaScript's alert function. The modification is done by creating a store and then passing that store as an argument to renderWebChat.
Rather than opening an alert window, you want to open a window you can close. This could be achieved if you modify the store to look like this:
let windows = {};
const store = window.WebChat.createStore(
{},
({ dispatch }) => next => action => {
if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
const { activity } = action.payload;
if (activity.type === 'event') {
let url = activity.value;
if (activity.name == 'open' && !windows[url]) {
windows[url] = window.open(url);
}
if (activity.name == 'close' && windows[url]) {
windows[url].close();
windows[url] = null;
}
}
}
return next(action);
}
);
You don't have to implement it this way, but I've implemented it so that when WebChat receives an event activity named open it will open a window and when it receives an event activity named close it will close a window. It even keeps track of multiple windows so you can choose which window to close.
I've set up a bot that sends open and close events when the user types "open [url]" or "close [url]". The bot code looks like this:
var connector = new ConnectorClient(new Uri(activity.ServiceUrl));
var text = activity.Text;
var words = text.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
var firstWord = words.FirstOrDefault().ToLower();
var secondWord = words.Length > 1 ? words[1] : "https://stackoverflow.com/";
Activity reply = null;
switch (firstWord)
{
case "open":
case "close":
reply = activity.CreateReply();
reply.Type = ActivityTypes.Event;
reply.Name = firstWord;
reply.Value = secondWord;
break;
default:
reply = activity.CreateReply("Try \"open ...\" or \"close ...\"");
break;
}
await connector.Conversations.SendToConversationAsync(reply);
Hopefully you can use this information and modify it to suit your needs.

Connection issues with Translator Text API version 3.0 - Microsoft Azure QnA chat bot

I want to connect my Azure QnA Chat Bot with the translation layer cognitive system. I am using this page as a reference: https://learn.microsoft.com/en-us/azure/cognitive-services/translator/quickstart-csharp-translate
I am doing it in C# and on the online code editor of Microsoft Azure.
Unfortunately, I can not connect to the translation layer (at least it looks like that).
When I tried to debug it, I can see that it stops at this specific part:
var response = await client.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();
I checked the network timeout errors and there are many (20). All of them say "There was an error sending this message to your bot: HTTP status code GatewayTimeout".
I can "build.cmd" normally, without any errors, and when I try to do Debug.WriteLine or Console.WriteLine, nothing is printed out (I even tried in VS and Emulator)
The only thing that I am doing differently, compared to the above link, is that I defined the "host" and "key" outside of the private method:
private static async Task<string> TranslateQuestionToEnglish (...)
So, I take any word and want to translate it into English.
When I take out those two lines of the code, and test a method with static values, it obviously works (all together with QnA and everything else).
Later on, I am calling this method in "Task MessageReceivedAsync".
I created a translation cognitive service, and the only thing that I took from there is the first key from "Keys" and used it here in this method. Is that the only thing that I need from created cognitive service??
Another thing that I am not sure about, and if that thing is making a problems is that when I go to all resources, I can see that my qnatestbot(web app bot) and translator_test(cognitive services) are of type "global" location, while my qnatestbot(app service) is of type "west europe" location. Can the thing that they are in different regions make a problems? Should I put all of them in West Europe (since I am in Germany)?
Although, now that I look at the translator_test(cognitive services) endpoint, I can see that it is ...api.congitivemicrosft.com/.../v1.0.
But, when I was creating a resource it was automatically created like this, without specifying it from my side? How can I change that?
I hope that somebody successfully came across such an issue and can help me. Thank you in advance
I want to connect my Azure QnA Chat Bot with the translation layer cognitive system. I am using this page as a reference: https://learn.microsoft.com/en-us/azure/cognitive-services/translator/quickstart-csharp-translate
I try to create a sample to achieve your requirement: translate user inputs to English and pass translation text to QnAMaker dialog, the sample works fine both on local and Azure, you can refer to it.
In MessagesController:
[BotAuthentication]
public class MessagesController : ApiController
{
static string uri = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=en";
static string key = "{the_key}";
/// <summary>
/// POST: api/Messages
/// receive a message from a user and send replies
/// </summary>
/// <param name="activity"></param>
[ResponseType(typeof(void))]
public virtual async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
// check if activity is of type message
if (activity.GetActivityType() == ActivityTypes.Message)
{
if (activity.Text != null)
{
var textinEN = await TranslateQuestionToEnglish(activity.Text);
activity.Text = textinEN;
}
await Conversation.SendAsync(activity, () => new RootDialog());
}
else
{
HandleSystemMessage(activity);
}
return new HttpResponseMessage(System.Net.HttpStatusCode.Accepted);
}
private static async Task<string> TranslateQuestionToEnglish(string text)
{
System.Object[] body = new System.Object[] { new { Text = text } };
var requestBody = JsonConvert.SerializeObject(body);
using (var client = new HttpClient())
using (var request = new HttpRequestMessage())
{
request.Method = HttpMethod.Post;
request.RequestUri = new Uri(uri);
request.Content = new StringContent(requestBody, Encoding.UTF8, "application/json");
request.Headers.Add("Ocp-Apim-Subscription-Key", key);
var response = await client.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();
dynamic jsonResponse = JsonConvert.DeserializeObject(responseBody);
var textinen = jsonResponse[0]["translations"][0]["text"].Value;
return textinen;
}
}
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
// Handle conversation state changes, like members being added and removed
// Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
// Not available in all channels
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}
}
In dialog:
[Serializable]
public class RootDialog : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
/* Wait until the first message is received from the conversation and call MessageReceviedAsync
* to process that message. */
context.Wait(this.MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
/* When MessageReceivedAsync is called, it's passed an IAwaitable<IMessageActivity>. To get the message,
* await the result. */
var message = await result;
var qnaAuthKey = GetSetting("QnAAuthKey");
var qnaKBId = Utils.GetAppSetting("QnAKnowledgebaseId");
var endpointHostName = Utils.GetAppSetting("QnAEndpointHostName");
// QnA Subscription Key and KnowledgeBase Id null verification
if (!string.IsNullOrEmpty(qnaAuthKey) && !string.IsNullOrEmpty(qnaKBId))
{
// Forward to the appropriate Dialog based on whether the endpoint hostname is present
if (string.IsNullOrEmpty(endpointHostName))
await context.Forward(new BasicQnAMakerPreviewDialog(), AfterAnswerAsync, message, CancellationToken.None);
else
await context.Forward(new BasicQnAMakerDialog(), AfterAnswerAsync, message, CancellationToken.None);
}
else
{
await context.PostAsync("Please set QnAKnowledgebaseId, QnAAuthKey and QnAEndpointHostName (if applicable) in App Settings. Learn how to get them at https://aka.ms/qnaabssetup.");
}
}
private async Task AfterAnswerAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
// wait for the next user message
context.Wait(MessageReceivedAsync);
}
public static string GetSetting(string key)
{
var value = Utils.GetAppSetting(key);
if (String.IsNullOrEmpty(value) && key == "QnAAuthKey")
{
value = Utils.GetAppSetting("QnASubscriptionKey"); // QnASubscriptionKey for backward compatibility with QnAMaker (Preview)
}
return value;
}
}
// Dialog for QnAMaker Preview service
[Serializable]
public class BasicQnAMakerPreviewDialog : QnAMakerDialog
{
// Go to https://qnamaker.ai and feed data, train & publish your QnA Knowledgebase.
// Parameters to QnAMakerService are:
// Required: subscriptionKey, knowledgebaseId,
// Optional: defaultMessage, scoreThreshold[Range 0.0 – 1.0]
public BasicQnAMakerPreviewDialog() : base(new QnAMakerService(new QnAMakerAttribute(RootDialog.GetSetting("QnAAuthKey"), Utils.GetAppSetting("QnAKnowledgebaseId"), "No good match in FAQ.", 0.5)))
{ }
}
// Dialog for QnAMaker GA service
[Serializable]
public class BasicQnAMakerDialog : QnAMakerDialog
{
// Go to https://qnamaker.ai and feed data, train & publish your QnA Knowledgebase.
// Parameters to QnAMakerService are:
// Required: qnaAuthKey, knowledgebaseId, endpointHostName
// Optional: defaultMessage, scoreThreshold[Range 0.0 – 1.0]
public BasicQnAMakerDialog() : base(new QnAMakerService(new QnAMakerAttribute(RootDialog.GetSetting("QnAAuthKey"), Utils.GetAppSetting("QnAKnowledgebaseId"), "No good match in FAQ.", 0.5, 1, Utils.GetAppSetting("QnAEndpointHostName"))))
{ }
}
Test result:
Note: We can use ConfigurationManager.AppSettings["QnAKnowledgebaseId"]; to access QnAKnowledgebaseId etc settings from web.config if run bot application on local. For more information, please refer to this SO thread.

How to custom message on browser when internet is not connected

I want to show custom message when my web application is failed to connect to internet.
Currently when my internet connectivity fails then the browser shows "unable to connect" or "server not found". so I want to show my custom message on my page.
I have written this :
bool bb = System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable();
if (bb == true)
{
//custom message
}
else
//meesage
But still it is not working.
How can I show that?
I think you are expecting internet connectivity from browser, if so use navigator.onLine;
<script type="text/javascript">
var isOnline = navigator.onLine;
if (!isOnline)
alert("Your custom message for no internet connectivity!!!");
</script>
Periodically call from client to server, and if there is no answer or not expecting answer - show error message.
for web forms:
create handler with implemetation of ProcessRequest like this:
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
context.Response.Write("Ok");
}
for mvc:
create action with simple result like this:
public ActionResult CheckInternetConnection()
{
return Json("Ok");
}
of course, this request handlers should not require any authorization or another pretreatment
then create js timer and method for request
var maxTime = <your interval time>;
var timer;
//write own method for check request
function performRequest() {
var xhr = new XMLHttpRequest();
// reserve 1 sec for timeout function execution
// if we didn't - you can get a situation when
// simultaneously performing more than 1 request
xhr.timeout = maxTime - 1000;
xhr.ontimeout = function {
//waiting to long
alert('<your message>');
window.clearInterval(timer);
};
xhr.open('GET', '<url of your check connection handler>', true);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState != 4)
return;
if (xhr.responseText !== 'OK') {
//Can't access to handler
alert('<your message>');
window.clearInterval(timer);
}
}
}
//after page load start request timer
document.onload += function () {
timer = window.setInterval(performRequest, maxTime);
}
I haven't debug this code.

Post message with Facebook SDK .NET

I have created a facebook page and a facebook application for my website and now I need to post messages onto the facebook page with help of facebook SDK .NET.
This is what I got so far :
public static bool UploadPost(string message)
{
dynamic result;
//https://developers.facebook.com/tools/explorer/
//https://developers.facebook.com/tools/access_token/
FacebookClient client = new FacebookClient("secret access token");
result = client.Get("oauth/access_token", new
{
client_id = "[Client ID number]",
client_secret = "[Client sercret",
grant_type = "client_credentials",
});
result = client.Post("[facebook app Id]/feed", new { message = "Test Message from app" });
//result.id;
result = client.Get("[facebook app Id]");
return false;
}
When running this I get : Additional information: (OAuthException - #200) (#200) The user hasn't authorized the application to perform this action on client.Post. If I remove the client.Post row every thing works good, the correct data is fetched.
I have tried follow some helps on facebook SDK .NET website but it is still not working.
The main problem now is that I get permission exception. I was hoping that my facebook app hade enouth permissions to publish post from my website to the facebook page.
Here is a step wise tutorial to register your application with facebook and get an app Id for your application.
Then for permissions ::
private const string ExtendedPermissions = "user_about_me,read_stream,publish_stream";
This is a string of permissions. Pass it on further for getting correct permissions to post messages on page. Post using your standard code for posting no FB pages.
Cheers. Hope it helps.
Are you trying to post to [facebook app id]?
I would recomend to post to "me/feed" and test if that works.
Also, to post to Facebook you have to have the publish_stream permission
private async Task Authenticate()
{
string message = String.Empty;
try
{
session = await App.FacebookSessionClient.LoginAsync("user_about_me,read_stream,publish_actions");
App.AccessToken = session.AccessToken;
App.FacebookId = session.FacebookId;
Dispatcher.BeginInvoke(() => NavigationService.Navigate(new Uri("/Pages/LandingPage.xaml", UriKind.Relative)));
}
catch (InvalidOperationException e)
{
message = "Login failed! Exception details: " + e.Message;
MessageBox.Show(message);
}
}
Should work :)
The following should work.
var fb = new FacebookClient("access_token");
fb.PostCompleted += (o, e) => {
if(e.Error == null) {
var result = (IDictionary<string, object>)e.GetResultData();
var newPostId = (string)result.id;
}
};
var parameters = new Dictionary<string, object>();
parameters["message"] = "My first wall post using Facebook SDK for .NET";
fb.PostAsync("me/feed", parameters);
This was taken directly from the documentation.
By creating a extended page token and use it to make the post everything works just fine. See this : How to get Page Access Token by code?
Im surprised that this simple task was so hard to get running and that there was vary little help to get.

Categories