Bot responses with lag after certain time of non-use - azure-web-app-service

I have a permanent problem which I couldn't solve.
Seems my dll was unloaded from the memory after a while.
For example, when I do not use bot during 30 minutes, and then reuse, it takes some time before responding, like it was re-deployed or dll was no longer in the RAM and it was reloaded before response.
Is there a way to fix the lag when bot is not used for a while?
update :3/1/17
To activate the option ' Always on ', it is necessary to pass on a paying offer.
As I am still there dev, I cannot pass on this offer.
I found a solution to keep my Bot in alive mode by creating a script that will send messages every minute.
My script is in c# and when I execute this, I obtain an error of authorisation.
var authValue = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{MyMicrosoftAppId}:{MyMicrosoftAppPassword}")));
var client = new HttpClient()
{
DefaultRequestHeaders = { Authorization = authValue }
};
var jsonObject = new Rootobject()
{
type = "message",
id = "0a24ca1428074419a1679b37e0e3dd39",
timestamp = DateTime.Now,
serviceUrl = "http://localhost:9000/",
channelId = "emulator",
from = new From()
{
id = "2c1c7fa3",
name = "User1"
},
conversation = new Conversation()
{
isGroup = false,
id = "8a684db8",
name = "Conv1"
},
recipient = new Recipient()
{
id = "56800324",
name = "Bot1"
},
text = "ping",
attachments = new object[0],
entities = new object[0]
};
var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json");
var result = client.PostAsync("http://emiko.azurewebsites.net/api/messages", content).Result;
result.ToString();
But I get an authorization error:
StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.StreamContent...
How can I login correctly on the Bot and send a message on its Url
(http://emiko.azurewebsites.net/api/messages)

As mentioned in the official document about Always On:
By default, web apps are unloaded if they are idle for some period of time. This lets the system conserve resources. In Basic or Standard mode, you can enable Always On to keep the app loaded all the time. If your app runs continuous web jobs, you should enable Always On, or the web jobs may not run reliably.
To activate the option ' Always on ', it is necessary to pass on a paying offer. As I am still there dev, I cannot pass on this offer.
Based on your scenario, I assumed that you could periodically send requests to make sure your site is alive, here are some tutorials you could refer to them (tutorial1 and tutorial2).

Related

Can I schedule a Google Cloud Task on the client side to call a cloud function with a payload?

I want to make sure I'm thinking about Cloud Tasks right conceptually, and not sure that I am.
The examples I've been looking at seem to trigger a cloud function first that then schedules a task, that then calls a cloud function again.
(Or at least this is what I'm understanding, I could be wrong).
I'd like to set up something so that when a user clicks a button, it schedules a cloud task for some time in the future (anywhere from 1 minute to an hour and half). The cloud task then triggers the cloud function to upload the payload to the db.
I tried to set this up client side but I've been getting the error "You need to pass auth instance to use gRPC-fallback client in browser or other non-Node.js environments."
I don't want the user to have to authenticate if that's what this is saying (not sure why I'd have to do that for my use case).
This is the code that gives that error.
const {CloudTasksClient} = require('#google-cloud/tasks');
const client = new CloudTasksClient();
// import { Plugins } from '#capacitor/core';
// const { RemotePlugin } = Plugins;
const scheduleTask = async(seconds) => {
async function createHttpTask() {
const project = 'spiral-productivity';
const queue = 'spiral';
const location = 'us-west2';
const url = 'https://example.com/taskhandler';
const payload = 'Hello, World!';
const inSeconds = 5;
// Construct the fully qualified queue name.
const parent = client.queuePath(project, location, queue);
const task = {
httpRequest: {
httpMethod: 'POST',
url,
},
};
if (payload) {
task.httpRequest.body = Buffer.from(payload).toString('base64');
}
if (inSeconds) {
// The time when the task is scheduled to be attempted.
task.scheduleTime = {
seconds: inSeconds + Date.now() / 1000,
};
}
// Send create task request.
console.log('Sending task:');
console.log(task);
const request = {parent: parent, task: task};
const [response] = await client.createTask(request);
console.log(`Created task ${response.name}`);
}
createHttpTask();
// [END cloud_tasks_create_http_task]
}
More recently I set up a service account and download a .json file and all of that. But doesn't this mean my users will have to authenticate?
That's why I stopped. Maybe I'm on the wrong track, but if anyone wants to answer what I need to do to schedule a cloud task from the client side without making the user authenticate, it would be a big help.
As always, I'm happy to improve the question if anything isn't clear. Just let me know, thanks!
Yes.
Your understanding is mostly accurate. Cloud Tasks is a way to queue "tasks". The examples are likely using Cloud Functions as an analog for "some app" (a web app) that would be analogous to your Node.js (web) app, i.e. your Node.js app can submit tasks to Cloud Tasks. To access Google Cloud Platform services (e.g. Cloud Tasks), you need to authenticate and authorize.
Since your app is the "user" of the GCP services, you're correct in using a Service Account.
See Application Default Credentials to understand authenticating (code) as a service account.
Additionally, see Controlling access to webapps.

The specified Checkout Session could not be found

Goal: Create a successful (test) Checkout Session using Stripe's API for checkout.
[the link for their tutorial on Checkout here: https://github.com/stripe-samples/checkout-one-time-payments]
I'm creating a checkout session using my UI & building the checkout session with the data supplied to the backend web service using the following code:
var options = new Stripe.Checkout.SessionCreateOptions
{
PaymentMethodTypes = new List<string>
{
"card",
},
LineItems = stripeCartLineItems,
Mode = "payment",
SuccessUrl = "https://" + HostName + "/Stripe/OrderPlaced",
CancelUrl = "https://example.com/cancel",
};
var requestOptions = new RequestOptions
{
StripeAccount = stripeConnectedAccountId,
ApiKey = StripeConfiguration.ApiKey
};
var service = new Stripe.Checkout.SessionService();
Stripe.Checkout.Session session = service.Create(options, requestOptions);
return Json(new { sessionId = session.Id });
As you can see, I receive acknowledgment back from Stripe's API with a valid checkout session id:
Logs on Stripe's Dashboard confirm a successful checkout session:
However, I keep getting this error message:
The API keys have already been refreshed and placed appropriately. That's not the issue... Loading up the test Checkout page is failing. My logs in Stripe's dashboard say this:
The Javascript call which initiates the redirect to Stripe's checkout experience is copied straight from their tutorial (pasted above). That code looks like this:
checkoutButton.addEventListener('click', function () {
$.ajax({
url: "/Stripe/CreateCheckoutSession",
method: "POST",
data: { stripeConnectedAccountId: stripeConnectedAccountId, cartLineItems: scope.cartLineItems },
}).done(function (resp) {
stripe.redirectToCheckout({
sessionId: resp.sessionId
}).then(function (result) {
// If `redirectToCheckout` fails due to a browser or network
// error, display the localized error message to your customer
// using `result.error.message`.
alert(result.error.message);
});
})
After going to: https://stripe.com/docs/error-codes/resource-missing. The docs says this for that specific error code: "The ID provided is not valid. Either the resource does not exist, or an ID for a different resource has been provided."
Ok Stripe. Sure sure. You made this API - I'll listen. However, according to your docs, Intellisense, & your sample code... my code is correct and I used the session.Id provided by the response object YOU sent me after initiating a Checkout Session:
I have no clue how to proceed.
Any ideas are appreciated.
If you have already verified the session and keys from server and stripe,
Please check the stripe key used in your client side. The public key used to initialise the stripe in both client & server should be same.
Check the logs in client side to make sure that the key is same.

Is there a way to schedule (via application user) calling an URL within the Azure ecosystem?

Here is my scenario:
I have an azure website, https://[my app on azure].azurewebsites.us
A user of this application wishes to schedule a future action,
reached via a uri.
The application keeps a record of this desire in an Azure SQL database table
having a "When to act" datetime column and an "Action GUID".
Example: User "bob" will go to the scheduling page in my website
and enter a date and time he wishes the action to execute.
Bob enters 2020-07-11 # 11:11 am (PDT).
Upon successful submission a record gets added to the database
with an application generated GUID, "AC5ECA4B-FB4F-44AE-90F9-56931329DB2E"
with a "When to act" value of 2020-07-11 11:11:00.00 -07:00
The action url will an url in my website https://[my app on azure].azurewebsites.us/PerformAction/AC5ECA4B-FB4F-44AE-90F9-56931329DB2E
The SQL database is NOT a Managed Instance.
Would this be possible using SQL CLR?
I'm thinking not.
Is there a way to do this using an Azure Function and/or Logic App?
Any other ideas?
Much appreciated!
The pattern you are putting together is a simple polling pattern. You need a function that will poll your data store every x minutes(or seconds) (function cron expression => * */x * * * *). It would then check that the current time is on or about to be exceeded by the stored time. If this is the case, it could then make your call. Don't forget to write back to the data store the success or failure of the call. If there are multiple steps, you should record where in the full life-cycle that command is. This way you can take the appropriate action.
To make your function app better, I would introduce a message queue (Azure Storage Queue) that receives the parts required to make the call. Then have a function (triggered by the messages on the queue) to send your web call. This way each function is operating separately from each other.
To reduce the need to poll all of the records, you could filter your records with SQL to be "between" certain timestamps guided by the current time of the the polling function.
If you need more clarity, leave comments and I'll try to provide more context.
Store or convert times to UTC to avoid timezone mismatches.
Logic apps have a Built-in step Action named "Delay until"
Using this step action in a logic app workflow solved the issue.
Here is what it looks like in the designer:
Here are further details.
The applications calls the logic app via the HTTP POST URL in the first step
passing in these properties:
{
"properties": {
"GUID": { // an identifier of the apps schedule
"type": "string"
},
"ID": { // an simpler identifier of the apps schedule
"type": "integer"
},
"MessageID": { // yet another identifier
"type": "integer"
},
"WhenToSend": { // the datetime in UTC format for when to Delay Until
"type": "string"
}
},
"type": "object"
}
Example of payload sent:
Here is a snippet of the app calling the LA:
newMessageSchedule.MessageScheduleStatuses.Add(new MessageScheduleStatus()
{
MessageScheduleStatusID = (int)ApplicationEnums.MessageScheduleStatuses.New
});
messageGettingSent.MessageSchedules.Add(newMessageSchedule);
await _dbContext.SaveChangesAsync();
dynamic schedulePayloadJSON = JsonConvert.SerializeObject(new
{
ID = newMessageSchedule.ID,
GUID = newMessageSchedule.MSGuid,
MessageID = newMessageSchedule.MessageID,
WhenToSend = newMessageSchedule.WhenToSend.UtcDateTime
});
HttpClient httpClient = new HttpClient();
HttpContent c = new StringContent(schedulePayloadJSON, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync($"{dto.SchedulerUri}", c);
if (response.StatusCode == HttpStatusCode.OK)
{
var content = await response.Content.ReadAsStringAsync();
//dynamic httpResponse = JsonConvert.DeserializeObject<dynamic>(content);
newMessageSchedule.MessageScheduleStatuses.Add(new MessageScheduleStatus()
{
MessageScheduleStatusID = (int)ApplicationEnums.MessageScheduleStatuses.Scheduled
});
messageGettingSent.MessageSchedules.Add(newMessageSchedule);
await _dbContext.SaveChangesAsync();
}
The dto.SchedulerUri is set from an appsettings setting,
TextMessaging:Scheduler and has a value which looks something like this:
"https://prod-123.usgovarizona.logic.azure.us:443/workflows/1d8.../triggers/request/paths/invoke?api-version=2016-10-01&sp=%2Ftriggers%2Frequest%2Frun&sv=1.0&sig=CJt..."
Once the "Delay Until" time occurs, the LA calls back into the app w/ the IDs:
The controller action performs all the things.
Voila
/vwäˈlä/

ChatBot retrieve data from SharePoint On Premise Issue

I've developed a chatbot that communicates with SharePoint on Premise,
When I run the chatbot in Emulator its work.
But When I run at Web that hosted outside of SharePoint, it does not work.
Herewith my screenshot of Error On Azure, From the result of Error is starting from XMLReader and SyndicationFeed
Success in Local Emulator
Herewith my Souce Code.
private async Task ProcessRSSAsync(ITurnContext<IMessageActivity> turnContext, LuisResult luisResult, string intent, CancellationToken cancellationToken)
{
var questionluis = turnContext.Activity.Text;
await turnContext.SendActivityAsync("intent recognize" + intent);
var intentresut = intent;
await turnContext.SendActivityAsync("Get LUIS Entity");
await turnContext.SendActivityAsync(string.Join("\t", luisResult.Entities.Select((entityObj) => entityObj.Entity)));
var entityfound = string.Join("\t", luisResult.Entities.Select((entityObj) => entityObj.Entity));
string spxurl = #"https://intra.aspac.com/sites/sg/daw/_layouts/15/srchrss.aspx?k=*%20ListId:7BC0F2C3-6366-48B8-B88A-8738BE1F9C31";
await turnContext.SendActivityAsync("Intent: " + intent.ToString() + " Entity: " + entityfound.ToString());
////---------------------------------------------------------------------------------------------
//22112019
try
{
//#ES09122019
var credentials = new NetworkCredential("email#example.com", "Pa$$w0rd", "sg.kworld.com");
var handler = new HttpClientHandler { Credentials = credentials, UseDefaultCredentials = false };
var client = new HttpClient(handler);
client.BaseAddress = new Uri("https://intra.aspac.com/sites/sg/daw/");
HttpResponseMessage resp = client.GetAsync("_layouts/15/srchrss.aspx?k=" + entityfound + "*%20ListId:7BC0F2C3-6366-48B8-B88A-8738BE1F9C31").Result;
string respString = resp.Content.ReadAsStringAsync().Result;
if (resp.StatusCode == HttpStatusCode.OK)
{
await turnContext.SendActivityAsync("Connected");
//Success 06122019 .
try
{
string spurl = #"https://intra.aspac.com/sites/sg/daw/_layouts/15/srchrss.aspx?k=*%20ListId:7BC0F2C3-6366-48B8-B88A-8738BE1F9C31";
XmlSecureResolver resolver = new XmlSecureResolver(new XmlUrlResolver(), spurl);
resolver.Credentials = new NetworkCredential("email#example.com.sg", "Pa$$w0rd", "sg.kworld.com");
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Parse;
settings.ValidationType = ValidationType.DTD;
settings.XmlResolver = resolver;
XmlReader reader = XmlReader.Create(spurl, settings);
SyndicationFeed feed = SyndicationFeed.Load(reader);
reader.Close();
var attachments = new List<Attachment>();
foreach (SyndicationItem item in feed.Items)
{
//Get Title,Description,URL
String title = item.Title.Text;
String description = item.Summary.Text;
String link = item.Links.FirstOrDefault().Uri.ToString();
//Hero Card
var heroCard = new HeroCard(
title: item.Title.Text,
// subtitle: description,
buttons: new CardAction[]
{
new CardAction(ActionTypes.OpenUrl,"Learn More",value:link)
}
).ToAttachment();
attachments.Add(heroCard);
}
var reply = MessageFactory.Carousel(attachments);
await turnContext.SendActivityAsync(reply);
await ProcessCosmoDBStorageLUISAsync(turnContext, questionluis, intent, entityfound, respString, cancellationToken);
}
catch (Exception ex)
{
await turnContext.SendActivityAsync(ex.ToString());
}
}
}
catch (Exception ex)
{
await turnContext.SendActivityAsync("Sorry,Currently Server Under Maintenace");
await turnContext.SendActivityAsync(ex.ToString());
}
}
any solution for this and suggestion?
ok, I think I finally understand this better, so hopefully can put a useful reply together. Would be much easier if we had a shared whiteboard :-)
Basically, in terms of hosting a bot on the Microsoft Bot Framework Services, you need to have a registration in Azure. However, there are two different options, and both are VERY different in terms of hosting. When you "create" the resource in Azure, and search for "Bot", you'll see two options - "Web App Bot" and "Bot Channels Registration":
"Bot Channels Registration" means JUST registering your bot in Azure, but HOSTING it elsewhere.
"Web App Bot" - INCLUDES the "Bot Channels Registration" but ALSO adds hosting using an Azure Web Application (so it's a Bot registration PLUS hosting)
From the screenshot you posted, I can see you've selected (2) above, and so your bot is running inside Azure, and therefore can't connect to your on premises resource (SharePoint).
As a result, I'd suggest one of two options:
Create an Azure Application Proxy - this is basically a small gateway so that your bot HOSTED in Azure can securely talk to your on-premises SharePoint. There is in fact a specific use case for SharePoint in particular.
Delete and re-create your Azure Bot entry to instead be just a "Bot Channels Registration", and then in the "Settings" screen you can call a bot hosted at any "https" endpoint. You can then have your bot run on the local network, but it will need a live "https" address (not -that- hard to do, but you have to involve your IT team to get a live web address, like "whatever.aspac.com", and you'll need an SSL/TLS certificate so that it can run httpS instead of just http.
Which option you choose might depend on the skills and resources on your team, as well as in the organisation. For instance, the company might have Azure Application Proxy configured already, in which case that saves a lot of work. It might have a wildcard certificate, which would make option (2) easier, etc.
Either way, I hope that helped, but feel free to ask more if anything is still unclear.
i had a similar problem using a on premise database. as you are deploying your bot externally, the bot needs resources that are available on the internet, and not contained internally. It will work fine using the bot emulator because it has access to what your machine has.
Saying that, azure has developed some actions which you can use to help this problem. If you look at application proxys, that may be able to help you out.
i think thats what you mean... anyway!

Webclient 401 Unauthorized Issue on above 5 minutes of processing time

I have HttpHandler (abc.ashx), which I make a call to process (Bulk Upload) an excel file. This processing takes a certain amount of time, which is proportionate to the number of rows in the excel.
On top of this I have an Event Handler in Sharepoint which uses a WebClient to make a post service call to this handler using the code below;
NOTE: I generate an identity token using elevated privilege of Sharepoint, and use that token to impersonate to make the service call. net net I make the post service call using the identity of the System Account.
WindowsIdentity identity = null;
// Get an identity token using delegate activity of Elevated Privelleges of Sharepoint
SPHelper.ElevatedActivity(properties.Web.Site.Url, properties.Web.ServerRelativeUrl, web =>
{
identity = WindowsIdentity.GetCurrent();
});
//Impersonates the identity of System Account user, as received in token in the line above
using (identity.Impersonate())
{
// create a web client object which only increases the timeout of the web client call
var webClient = (new CustomWebClient());
webClient.Credentials = CredentialCache.DefaultNetworkCredentials;
webClient.UseDefaultCredentials = true;
//url of the httphandler as deployed in sharepoint, and the parameters that needs to be passed
string handlerUrl =
string.Format(
properties.Web.Url.Trim('/') + "/_layouts/Handlers/abc.ashx?Table=BulkUpload&Region={0}&UserId={1}&BatchId={2}&FileUrl={3}",
region, userValue.User.LoginName, batchId, fileUrl);
}
// execute the web service call using the webclient object
webClient.DownloadString(handlerUrl));
Essentially this code is written in the Event Handler for ItemAdded and ItemUpdated event.
The issue is in the webClient which seems to give an error "system.net.webexception: the remote server returned an error: (401) unauthorized", after precisely 5 mins of processing. Hence if the excel sheet to be processed has rows less than certain number (1700), the processing happens within 5 minutes, and everything runs fine without any error. However if it has more than that, then the processing takes more than 5 minutes, and fails with the error specificed above.
The strange behaviour is it seems like a timeout issue but the error message indicates authorization issue, which does not make sense, as if there was an authorization issue, it shouldnt have worked even when the processing time was less that 5 minutes.
We have tried to find any configuration that times out in 5 minutes, however we could not find any.
Any help or suggestion on this matter is appreciated.
UPDATE: I have now tried to make this work using HttpWebRequest, and have tried a bunch of setting which might cause this timeout/issue. However still getting the same issue, where after 5 minutes of processing I am getting "system.net.webexception: the remote server returned an error: (401) unauthorized". Below is the code I have tried
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(handlerUrl);
httpWebRequest.Credentials = CredentialCache.DefaultCredentials;
httpWebRequest.UseDefaultCredentials = true;
httpWebRequest.Timeout = 600000;
httpWebRequest.ReadWriteTimeout = 600000;
httpWebRequest.ServicePoint.ConnectionLeaseTimeout = 600000;
httpWebRequest.ServicePoint.MaxIdleTime = 600000;
httpWebRequest.ContentType = "application/json; charset=utf-8";
httpWebRequest.ContentLength = 0;
httpWebRequest.Method = WebRequestMethods.Http.Get;
BulkUploadProcessorResponse response = null;
var httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (Stream stream = httpWebResponse.GetResponseStream())
{
using (StreamReader sr = new StreamReader(stream))
{
response =
JsonHelper.JsonDeserialize<BulkUploadProcessorResponse>(sr.ReadToEnd());
}
}

Resources