I am trying to track down a performance issue that is not allowing me to send more than 3 or so message with the twilio c# api. In short, we have a windows service that responds to bulk 'JOIN' messages. See the following code:
int i = 0;
DateTime d = DateTime.Now;
TimeSpan ts = new TimeSpan(0,0,1);
foreach (CustomerTextMessage t in o)
{
i++;
ThreadInput ti = new ThreadInput { MessageIds = i, Body = t.Body, ThreadCreated = DateTime.Now, FromNumber = t.FromNumber };
ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessResponseList), (object)ti);
}
The ProcessResponseList method does a quick look up on the customer, then responds using this method:
public static void SendResponse(string number, string body)
{
string AccountSid = ConfigurationManager.AppSettings["AccountSid"];
string AuthToken = ConfigurationManager.AppSettings["AccountToken"];
var twilio = new TwilioRestClient(AccountSid, AuthToken);
string fromNumber = "";
if (ConfigurationManager.AppSettings["StageExecution"] == "true")
{
BusinessLayer.SLOSBL.Logger logger = new BusinessLayer.SLOSBL.Logger("DiscoverTextMessagingService", "DiscoverTextMessagingService");
AuthToken = ConfigurationManager.AppSettings["StageApplicationToken"];
var longCodes = twilio.ListIncomingPhoneNumbers(null, "StagePhoneNumber", null, null);
fromNumber = longCodes.IncomingPhoneNumbers[1].PhoneNumber;
logger.Notify("Sending text message to: " + number + " from Twilio number: " + fromNumber + ". The body of the message reads: " + body);
}
else
{
var shortCodes = twilio.ListShortCodes(null, null);
fromNumber = "+" + shortCodes.ShortCodes[0].ShortCode;
}
//var message = twilio.SendMessage(fromNumber, number, body, new string[0], null, AuthToken );
DateTime d = DateTime.Now;
twilio.SendMessage(fromNumber, number, body, new string[0], null, AuthToken, null, m => { MessageAsyncCallBack(m, d); });
}
What I am seeing is that it is able to send these message about 3 every second. I am really looking to increase that closer to the 30 every second that a short code is possible but I am not able to even get them to the twilio queue fast enough. So my question, is there anything obvious that I should be doing? Should I be reusing the TwilioRestClient variable for all message? Is there a limit to the number of connections someplace that I am missing? Thanks in advance.
Related
I have a usecase where I have 1000 emails with the bodies prepared (they are ready to send as is), a single sent from email address, 1000 recipients. I am using SendGrid API v3 in C#. I am trying to figure out how to bulk send them to the SendGrid API. This is my code:
private async Task SendBatchEmails(DataRowCollection emailDataRows)
{
var WriteToDatabaseCollection = new Dictionary<Guid, string>();
var emailObjectCollection = new List<SendGridMessage>();
foreach (DataRow emailDataRow in emailDataRows)
{
var emailObject = new SendGridMessage();
var to = (new EmailAddress(emailDataRow["RecipientEmailAddress"] + "", emailDataRow["RecipientName"] + ""));
var from = new EmailAddress(emailDataRow["SenderEmailAddress"] + "", emailDataRow["SenderName"] + "");
var subject = emailDataRow["Subject"] + "";
var text = emailDataRow["MessageBody"] + "";
var html = $"<strong>{emailDataRow["MessageBody"] + "" }</strong>";
var msg = MailHelper.CreateSingleEmail(from, to, subject, text, html);
emailObjectCollection.Add(msg);
}
await emailClient.SendBatchEmailsEmailAsync(emailObjectCollection);
dataContext.UpdateEmailResult(WriteToDatabaseCollection);
}
public async Task SendBatchEmailsEmailAsync(List<SendGridMessage> messages)
{
return await client.????(messages);
}
client is a SendGridClient, and the only option I have is: SendEmailAsync(msg)
How do I send a batch fo sendgrid messages?
Twilio SendGrid developer evangelist here.
There isn't a batch email send for emails with different bodies. Though you can send the same body to multiple addresses.
To send your 1000 emails you need to loop through your list of messages and call the API once per message.
If any of the emails you want to send has same body as others (you can't bulk send emails with different content as mentioned in the other answer), you could group them by body and then send in chunks. SendGridClient takes max 1000 at a time, so you'd also need to check for that. With something like this:
public class Email
{
string Content { get; }
string Address { get; }
}
Your code to group, chunk and send your emails in batches (where emails is a collection of Email objects) would look something like this:
var client = new SendGridClient("123456APIKEY");
var senderEmail = "someEmail";
var groupedByContent = emails.GroupBy(x => x.Content, x => x.Address);
foreach(var group in groupedByContent)
foreach(var recipientsBatch in group.Chunk(1000))
{
var message = new SendGridMessage();
// This extra index is needed to create separate personalizations, if you don't want recipients to see each others email
for (var i = 0; i < recipientsBatch.Length; i++)
message.AddTo(new EmailAddress { Email = recipientsBatch[i] }, i);
message.PlainTextContent = group.Key;
message.SetFrom(senderEmail);
var response = await client.SendEmailAsync(message);
// response processing code ...
}
I need to get the results of google searches in order to loop through and parse them. With that aim in view, I followed (as best I could) the tutorial on how to do that here
This is my code, based on the sample/example code in the article referenced above:
private void btnRentFlick_Click(object sender, EventArgs e)
{
OpenBestPageForSearchString("rent amazon movie Will Penny");
}
private void OpenBestPageForSearchString(string searchStr)
{
try
{
const string apiKey = "blaBlaBla"; // "blaBlaBla" stands for my API key
const string searchEngineId = "bla"; // "bla" stands for various things I tried: my client_id
(also called UniqueId), private_key_id (also called KeyId), and project_id. Not having
the correct value may be the problem. If so, how do I get it?
const string query = "rent amazon movie Will Penny";
var customSearchService = new CustomsearchService(new BaseClientService.Initializer { ApiKey
= apiKey });
//CseResource.ListRequest listRequest = customSearchService.Cse.List(query); // This is the
code in the article, but it won't compile - "no overload for "List" takes one argument"
// So how is the value in "query" assigned, then?
CseResource.ListRequest listRequest = customSearchService.Cse.List();
listRequest.Cx = searchEngineId;
List<string> linksReturned = new List<string>();
IList<Result> paging = new List<Result>();
var count = 0; // I don't know what the purpose of the counting is, but I'll leave as-is
until I get it working at least
while (paging != null)
{
listRequest.Start = count * 10 + 1;
paging = listRequest.Execute().Items; // this takes several seconds, then it throws an
exception
if (paging != null)
{
foreach (var item in paging)
{
linksReturned.Add("Title : " + item.Title + Environment.NewLine + "Link : " +
item.Link +
Environment.NewLine + Environment.NewLine);
}
}
count++;
}
MessageBox.Show("Done with google amazon query");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
As the comment at the end of that line says, this line of code:
paging = listRequest.Execute().Items;
...works for several seconds, then throws an exception, namely this:
So what is causing this exception? Is it because the searchEngineId value I assigned is bad? Or is it because the search string (assigned to the query variable) has not been provided to the call?
The info about my Ids is contained in a .json file provided by google, and there is no "searchEngineId" value in it. This is what it does contain:
"type": "service_account", "project_id": "flix4famsasinlocator",
"private_key_id": "[my private key id]", "private_key": "-----BEGIN
PRIVATE KEY-----. . . PRIVATE KEY-----\n", "client_email":
"[bla].gserviceaccount.com", "client_id": "[my client Id]",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url":
"https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url":
"https://www.googleapis.com/robot/v1/metadata/x509/[bla]gserviceaccount.com"
So though the article previously mentioned purported to be, and at first appeared to be, just what the doctor ordered, I have ran into a wall of considerable dimensions. Does anybody know how to scale this wall - perhaps primarily by providing the search string to the CseResource.ListRequest object?
UPDATE
Trying DalmTo's code first, I used this (not showing his GetService() method, which I copied verbatim):
var query = "rent amazon movie Will Penny";
var service = GetService("theRainInSpainFallsMainlyOnTheDirt");
var request = service.Cse.List();
// add option values to the request here.
request.ExactTerms = query;
request.Q = query;
var response = request.ExecuteAsync();
// my contribution:
List<string> linksReturned = new List<string>();
foreach (var item in response.Result.Items)
{
//Console.WriteLine(item.Title);
// next two lines also mine
MessageBox.Show(string.Format("Title: {0}; Link: {1}; ETag: {2}", item.Title, item.Link, item.ETag));
linksReturned.Add(item.Link);
}
...but this exception was thrown while in the foreach loop:
UPDATE 2
Yes, this works (adapted from Trekco's answer):
const string apiKey = "gr8GooglyMoogly";
const string searchEngineId = "theRainInSpainFallsMainOnTheDirt";
const string query = "rent amazon movie Will Penny";
var customSearchService = new CustomsearchService(new BaseClientService.Initializer { ApiKey = apiKey });
CseResource.ListRequest listRequest = customSearchService.Cse.List();
listRequest.Cx = searchEngineId;
listRequest.Q = query;
List<string> linksReturned = new List<string>();
IList<Result> paging = new List<Result>();
var count = 0;
while (paging != null)
{
listRequest.Start = count * 10 + 1;
paging = listRequest.Execute().Items;
if (paging != null)
{
foreach (var item in paging)
{
linksReturned.Add(item.Link);
}
}
count++;
}
The query is not being send to google. To fix your code you need to tell the api what query to use. After listRequest.Cx = searchEngineId; add listRequest.Q = query;
var count = 0;
string apiKey = "THE API KEY";
string searchEngineId = "THE SEARCH ENGIN ID";
string query = "rent amazon movie Will Penny";
var customSearchService = new CustomsearchService(new BaseClientService.Initializer
{
ApiKey = apiKey
});
CseResource.ListRequest listRequest = customSearchService.Cse.List();
listRequest.Cx = searchEngineId;
listRequest.Q = query; // <---- Add this line
List<string> linksReturned = new List<string>();
while (count < 10) // Google limit you to 100 records
{
listRequest.Start = count * 10;
var paging = listRequest.Execute().Items;
foreach (var item in paging)
{
linksReturned.Add("Title : " + item.Title + Environment.NewLine + "Link : " +
item.Link +
Environment.NewLine + Environment.NewLine);
}
count++;
}
In your code you have a comment that you don't know what var count = 0; is for. It is to keep track on how many items you have requested.
If you look at google's documentation you will see that they will only return 100 results max. After that they will give you a error. That error will also be the same generic message: "INVALID_ARGUMENT"
You can review the custom search api requirements here: https://developers.google.com/custom-search/v1/reference/rest/v1/cse/list
The searchEngineId variable is the search Engine id that you generate on the site https://www.google.com/cse/all. The documentation you followed is a bit out of date. you will find the id here:
If you check the doucmntation cse.list I think you will find that the list method has no required fields which means that you need to add the option values in the following manner.
I think ExactTerms may be the one you are looking for. But it could also be Q i think you should read though the option values and decide which one is best for your purpose.
var query = "rent amazon movie Will Penny";
var service = GetService("MYKEY");
var request = service.Cse.List();
// add option values to the request here.
request.ExactTerms = query;
request.Q = query;
var response = request.ExecuteAsync();
foreach (var item in response.Result.Items)
{
Console.WriteLine(item.Title);
}
My get service method
public static CustomsearchService GetService(string apiKey)
{
try
{
if (string.IsNullOrEmpty(apiKey))
throw new ArgumentNullException("api Key");
return new CustomsearchService(new BaseClientService.Initializer()
{
ApiKey = apiKey,
ApplicationName = string.Format("{0} API key example", System.Diagnostics.Process.GetCurrentProcess().ProcessName),
});
}
catch (Exception ex)
{
throw new Exception("Failed to create new Customsearch Service", ex);
}
}
I'm trying to create an API that consumes various topics.
For this, I'm trying to multi-thread things, so that the whole thing can be scalable into multiple APIs, later on, but that's very besides the point.
I'm using ASP.net Core 4.0, if that's got anything to do with it. Entity Framework as well.
My problem is based on my connection to my Mosquitto server being broken without throwing an exception or anything of the like, after a minute or so. It doesn't matter how big the messages are, or how many are exchanged. I have no idea of how I can create a callback or anything of the kind to know what's going on with my connection. Can anyone help?
I'll link the code I use to establish a connection and subscribe to a connection below. Using the Subscribe method or doing it manually also changes nothing. I'm at a loss, here.
Thanks in advance!
Main.cs:
Task.Factory.StartNew(() => DataflowController.ResumeQueuesAsync());
BuildWebHost(args).Run();
DataflowController.cs:
public static Boolean Subscribe(String topic)
{
Console.WriteLine("Hello from " + topic);
MqttClient mqttClient = new MqttClient(brokerAddress);
byte code = mqttClient.Connect(Guid.NewGuid().ToString());
// Register to message received
mqttClient.MqttMsgPublishReceived += client_recievedMessageAsync;
string clientId = Guid.NewGuid().ToString();
mqttClient.Connect(clientId);
// Subscribe to topic
mqttClient.Subscribe(new String[] { topic }, new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });
System.Console.ReadLine();
return true;
}
public static async Task ResumeQueuesAsync()
{
var mongoClient = new MongoClient(connectionString);
var db = mongoClient.GetDatabase(databaseName);
var topics = db.GetCollection<BsonDocument>(topicCollection);
var filter = new BsonDocument();
List<BsonDocument> result = topics.Find(filter).ToList();
var resultSize = result.Count;
Task[] subscriptions = new Task[resultSize];
MqttClient mqttClient = new MqttClient(brokerAddress);
byte code = mqttClient.Connect(Guid.NewGuid().ToString());
// Register to message received
mqttClient.MqttMsgPublishReceived += client_recievedMessageAsync;
string clientId = Guid.NewGuid().ToString();
mqttClient.Connect(clientId);
int counter = 0;
foreach(var doc in result)
{
subscriptions[counter] = new Task(() =>
{
Console.WriteLine("Hello from " + doc["topic"].ToString());
// Subscribe to topic
mqttClient.Subscribe(new String[] { doc["topic"].ToString() }, new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });
System.Console.ReadLine();
});
counter++;
}
foreach(Task task in subscriptions)
{
task.Start();
}
}
static async void client_recievedMessageAsync(object sender, MqttMsgPublishEventArgs e)
{
// Handle message received
var message = System.Text.Encoding.Default.GetString(e.Message);
var topic = e.Topic;
var id = topic.Split("/")[2];
BsonDocument doc = new BsonDocument {
{"Plug ID", id },
{"Consumption", message }
};
await Save(doc, "smartPDM_consumption");
System.Console.WriteLine("Message received from " + topic + " : " + message);
}
This line was the issue:
byte code = mqttClient.Connect(Guid.NewGuid().ToString());
Deleted it, and it just worked.
I am using PushSharp 4.0.10.0 library to send the notification on iOS devices but it's not working. I have debugged it and found there is some ApnsConfiguration connection problem.
I am using this code to send the notificaiton:
public IHttpActionResult Notify()
{
HttpResponseMessage response = new HttpResponseMessage();
HttpContent requestContent = Request.Content;
string errorMessage = "Some error occured.please try again later";
HttpStatusCode responseCode = HttpStatusCode.Unauthorized;
string requestParameter = requestContent.ReadAsStringAsync().Result;
string tokan = "";
var r = Request;
var header = r.Headers;
try
{
if (requestParameter != null)
{
PushNotificationModel complaintModel = JsonConvert.DeserializeObject<PushNotificationModel>(requestParameter);
JsonConvert.DeserializeObject<PushNotificationModel>(requestParameter);
var appleCert = File.ReadAllBytes(HttpContext.Current.Server.MapPath("~/Images/User/xyz.pem"));
var config = new ApnsConfiguration(ApnsConfiguration.ApnsServerEnvironment.Production,
appleCert, "xyz");
// Create a new broker
var push = new ApnsServiceBroker(config);
int DeviceType = 1;
string deviceId = Convert.ToString(complaintModel.deviceToken);
string message = "New notification!!";
Guid complaintId = complaintModel.ComplaintId;
string detail = complaintModel.detail;
try
{
//System.Web.Hosting.HostingEnvironment.MapPath("~/Images/User/")
// var appleCert = File.ReadAllBytes(HttpContext.Current.Server.MapPath("~/Images/User/CTPwd.pem"));
push.OnNotificationFailed += (notification, aggregateEx) =>
{
aggregateEx.Handle(ex =>
{
// See what kind of exception it was to further diagnose
if (ex is ApnsNotificationException)
{
message = ex.Message;
}
else
{
message = "Not an APNSException";
}
// Mark it as handled
return true;
});
};
try
{
push.OnNotificationSucceeded += (notification) =>
{
message = "New Notification";
};
push.Start();
string appleJsonFormat = "{\"aps\": {\"alert\":" + '"' + message + '"' + ",\"sound\": \"default\"}}";
//string appleJsonFormat = "{\"aps\": {\"alert\": " + "Hello World" + ",\"sound\": \"default\"}}";
push.QueueNotification(new ApnsNotification
{
DeviceToken = deviceId,
Payload = JObject.Parse(appleJsonFormat)
});
push.Stop();
}
catch(Exception ex)
{
}
I have searched on google, but did not find any relevant answer. Is there any syntax problem ?
Thanks in advance.
Please use .P12 file format for push notification happy coding:)
Update 1:
I also checked MSDN for the window.external.notify() method, on the MSDN, the method starts with “N" in capital(window.external.Notify()), but at many other places on the Internet, I also saw many examples using "n" in lower case(window.external.notify()). This really confuses me.
It's really odd.
The first time when I used the window.external.notify method, it worked perfectly.
Went to a trip overseas about 2 weeks, got back to home today and couldn't find my latest code which had the notidy method in my Javascript code, but the code for Browser_ScriptNotify method in my C# code is still there.
Searched for 1 hour and found nothing(where did it go?)
Now I coded the Javascript part again and here is the code:
Btw, I am useing jQuery + jQuery Mobile to take care the layout and UI.
The setColor method is called in
$(document).on("pagecreate", "[data-role='page'].weekday", function
() {
setColor(this);
});
JavaScript:
var myNotifier = function (msg) {
if (window.external)
{
window.external.notify('Hi Mate');
}
};
function setColor(activePage){
$(activePage).find('.rightCol').each(function (index, element) {
var innerRows = $(element).children("div").length;
if ($(element).hasClass('orange'))
{
$(element).children("div").addClass('orange');
}
var timeCol = $(element).prev();
var time = $(timeCol).text().trim();
var innerRowsDivs = $(element).children("div");
innerRowsDivs.each(function (index, rowElement) {
var innerRowContent = $(rowElement).text().trim();
if (innerRowContent === "") { }
else
{
var room = $(rowElement).find("div").text().trim();
var trainingClass = $(rowElement).find("strong").text().trim();
var duration = innerRowContent.substring(innerRowContent.length - 5, innerRowContent.length - 3).trim();
var message = String('myMsg:open:calender:' + time + '|' + room + '|' + trainingClass + '|' + duration);
$(rowElement).click(myNotifier(message));
}
});
});
}
C# code:
void Browser_ScriptNotify(object sender, NotifyEventArgs e)
{
MessageBox.Show(e.Value);
if (e.Value.Contains("myMsg"))
{
if (e.Value.Contains("share"))
{
ShareLinkTask shareMyWebsite = new ShareLinkTask();
shareMyWebsite.Title = "WinTech Company";
shareMyWebsite.LinkUri = new Uri("http://wintech.azurewebsites.net/", UriKind.Absolute);
shareMyWebsite.Message = "Check this awesome website, I'm sure you will find something interesting!";
shareMyWebsite.Show();
}
if (e.Value.Contains("open"))
{
if (e.Value.Contains("website"))
{
WebBrowserTask myWebsite = new WebBrowserTask();
myWebsite.Uri = new Uri("http://wintech.azurewebsites.net/", UriKind.Absolute);
myWebsite.Show();
}
else
if (e.Value.Contains("facebook"))
{
WebBrowserTask myWebsite = new WebBrowserTask();
myWebsite.Uri = new Uri("https://www.facebook.com/franva008", UriKind.Absolute);
myWebsite.Show();
}
else
// myMsg:open:calender:<Message>
if (e.Value.Contains("calender"))
{
//var message = e.Value.Split(':')[3];
//var time = message.Split('|')[0];
//var trainingClassAndDuration = message.Split('|')[1];
//string[] content = trainingClassAndDuration.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
//string durationString = content[content.Length - 1];
//int takeCount = content.Length - 2;
//string trainingClass = string.Join(" ", content.Skip(1).Take(takeCount));
//string room = content[0];
//int duration = Convert.ToInt32(durationString.Substring(0, 2));
var message = e.Value.Split(':')[3];
var time = message.Split('|')[0];
var room = message.Split('|')[1];
var trainingClass = message.Split('|')[2];
var duration = Convert.ToInt32(message.Split('|')[3]);
DateTime startTime = ParseTime(time);
DateTime endTime = startTime.AddMinutes(duration);
this.CreateAppointment(startTime, endTime, room, trainingClass);
}
}
}
}
IsScriptEnabled has been set in the xaml
and the Browser_ScriptNotify event has been hooked up in my code as well.
I keep getting an error about jquery:
Uncaught TypeError: Object 50 has no method 'apply'
and an error about notify
Uncaught TypeError: Object # has no method 'notify'
Please help, these errors have stopped me more than 4 hours, I have read through many similar questions and none of them soloved my problem.