Trigger notification once in a loop - c#

I have a loop that is running every 10 seconds that does a few things. One thing it does is it enables a button when there is a message that I am sending to the users of the app. I want to send a notification to the system tray when that button enables, but for obvious reasons I only want that notification triggered once when the user has an unread broadcast.
Here is the code I have:
private void EnableBroadcasts()
{
string query = "SELECT COUNT(*) FROM db.broadcasts WHERE broadcast_active = '1'";
int count = StoredProcedures.CountRecordsT4(query);
StreamReader re = new StreamReader(#"C:\\Users\\" + Environment.UserName + #"\\appdata\\Local\\Cache\\broadcasts.cache");
List<string> ReadBroadcastList = new List<string>();
List<string> BcastID = BroadcastID();
if (BroadcastCount != count)
{
string text = re.ReadToEnd();
re.Close();
string[] lines = text.Split('\r');
foreach (string s in lines)
{
ReadBroadcastList.Add(s);
}
for (int t = 0; t < ReadBroadcastList.Count(); t++)
{
ReadBroadcastList[t] = ReadBroadcastList[t].Trim('\n');
}
ReadBroadcastList.Remove("");
BroadcastCount = ReadBroadcastList.Count();
}
var test = BcastID.Except(ReadBroadcastList).ToList();
int read = test.Count();
if (count != 0)
{
btnWESBroadcast.Visible = true;
}
else
{
btnWESBroadcast.Visible = false;
}
The button enables once count is not zero. I have a list of broadcast ID's that are active from the db,I also have a cache file that records what broadcast ID's that user has read.
I am looking for a solution that will have the notification only run when the button is active and there is a broadcast that the user has not read.

Wrap your string broadcast in a simple type: BroadcastMessage. Add a bool IsRead flag.
Mark IsRead = true and the message will be ignored with the following logic.
// pseudo
if (button.IsEnabled && ReadBroadcastList.Any(msg => !msg.IsRead)) {
NotifyTray();
}
Then you can later add a feature for the user to mark a message Unread.
If you intend to persist this data in the database, then both the message and flag can be stored in the BroadcastMessage object. When a user reads the message and the object is marked as read, update the database with the change.
Update: based on clarification in comment
Add a bool IsNotified flag to the BroadcastMessage notification and check !msg.IsNotified instead of !msg.IsRead.

Related

Ready event handler times out

I'm currently working on a Discord bot written in C#. It is supposed to celebrate the birthday of the users in a server, once they have provided their day and month of birth by sending a message (user-birthday pairs are stored inside a local .txt file). A birthday is celebrated by sending an embed in the main channel of the guild (with which it shares the ID parameter) if the date of execution happens to be someone's birthday.
The content of the Ready event handler of the DiscordSocketClient element within the Program class and a partial call stack are shown below:
public DiscordSocketClient Client;
//...
private async Task Client_Ready()
{
await Commands.CelebrateUsers(Client);
}
also
public static async Task CelebrateUsers(DiscordSocketClient client)
{
DateTime now = DateTime.Now;
List<ulong> users = new List<ulong>();
using (StreamReader reader = new StreamReader(#"docs\birthdays.txt"))
{
string date;
ulong user;
while ((date = reader.ReadLine()) != null)
{
user = Convert.ToUInt64(reader.ReadLine());
int[] dm = date.Split(' ').Select(x=>Convert.ToInt32(x)).ToArray();
if(now.Day == dm[0] && now.Month == dm[1])
{
foreach(SocketGuild g in client.Guilds)
{
if(g.Users.FirstOrDefault(x=>x.Id == user) != null)
await CelebrateUser(user, g);
}
}
}
}
}
private static async Task CelebrateUser(ulong id, SocketGuild guild)
{
var embed = new EmbedBuilder()
{
Title = "Auguri ",
Color = Discord.Color.Green,
ThumbnailUrl = "https://c7.uihere.com/files/240/249/186/hot-air-balloon-birthday-balloon.jpg",
ImageUrl = guild.GetUser(id).GetAvatarUrl(size:900),
Footer =
{
IconUrl = "https://cdn.discordapp.com/avatars/406195350903193600/9629b569007720204f7fe775a9be0aeb.png",
Text = "Generato da DrKikkoCeccato++."
},
Timestamp = DateTimeOffset.Now
};
await guild.DefaultChannel.SendMessageAsync("",embed:embed.Build());
}
However, I have an issue with the last function. The last piece of code (the "SendMessageAsync" part) is not even called.
Upon the initialization of the embed, the Log event of the DiscordSocketClient is fired with the following records:
"A Ready handler is blocking the gateway task."
"A Ready handler has thrown an unhandled exception."
The state changes to "Ready" immediately afterwards. Needless to say, my embed is not being sent.
I don't get what the problem might be, since every action is marked as "asynchronous" (in order to avoid problems with the 3-second timeout of the "Ready" event). Could anyone help me?
Thanks in advance.

Redirect to a different aspx page and run the next code in background (.NET 4.5.2)

I am working on an ASP.NET Webform project (legacy code).On my button_click event i am sending sms message to all the datas populated in this.
var customerSMS = BusinessLayer.SMS.SmsSetup.GetAllCustomerSMS(OfficeId);
This takes around 15seconds to do all the computing and get the data(1000rows)
from the Db.And for each data it runs through the loop and does validation and
sends the sms and it does take time.I want to do this task in background and
redirect the user to the index page and the background process continues till it
gets out of the loop.I am new to this and still learning this beautiful
language C#.I did go through this amazing Asynchronous Programming async/await
and Multithreading approach and got hold of it only in simple WindowsForm
applications.Any reference/code snippet/best approach with a simple explanation for my case would be helpful.
My button click event code :
protected void ReturntoDashboard_Click(object sender, EventArgs e)
{
sms = Everest.Net.BusinessLayer.SMS.SmsSetup.GetSmsSetUp(OfficeId);
if (sms.EnableSmsData && sms.SmsCount > 0)
{
#region Loan Section
var smsLoan = Everest.Net.BusinessLayer.SMS.SmsSetup.GetLoanId(s.Sms_AccountNumber);
var loanId =
BusinessLayer.SMS.SmsSetup.GetLoanIdValue(s.Sms_AccountNumber);
var dateexceeded =
BusinessLayer.SMS.SmsSetup.IsDateExceeded(loanId);
if (smsLoan != null && dateexceeded == true)
{
foreach (Common.SMS.SMSSetup sm in smsLoan)
{
var smsClosingBalanceLoan = BusinessLayer.SMS.SmsSetup.GetAmountForLoanAlert( sm.LoanId,
BusinessLayer.Core.DateConversion
.GetCurrentServerDate()
.AddDays(sms.DaysbeforeLoanalerts).ToString());
if (smsClosingBalanceLoan != null)
{
if (smsClosingBalanceLoan.LoanAmountToPay > 0)
{
int smsSentAlertCount = sms.LoanAlertCount;
var logCount = BusinessLayer.SMS.SmsSetup.GetLoanSmsAlertSentCount(DateTime.Now.AddDays(-smsSentAlertCount).ToString("yyyy-MM-dd"), DateTime.Now.ToString("yyyy-MM-dd"), sm.LoanAccountNumber);
if (logCount < smsSentAlertCount)
{
smsLog = new Everest.Net.Common.SMS.SMSSetup();
finalMessage = "Dear Member, Your Loan accnt " + sm.LoanAccountNumber + " with Principal"+ "+" + "Int Amnt: Rs." + smsClosingBalanceLoan.LoanAmountToPay + " need to be payed.Thank You," + officeName.OfficeName;
smsLog.LogServiceType = "Loan";
smsLog.LogSmsType = s.Sms_SmsType;
smsLog.LogSmsMessage = finalMessage;
smsLog.LogCustomerId = s.CustomerId.ToString();
smsLog.LogAccountNumber = s.Sms_AccountNumber;
smsLog.LogAccountType = s.Sms_AccountType;
smsLog.LogSmsSentDate = BusinessLayer.Core.DateConversion.GetCurrentServerDate();
smsLog.LogSmsFailedDate = "";
smsLog.LogSentStatus = true;
smsLog.LogUserId = UserId;
smsLog.LogSmsFailedMessage = "";
try
{
var result = Everest.Net.BusinessLayer.SMS.smsParameters.SendSMS(sms.FromNum, sms.Token, sms.Url, cellNum, finalMessage);
}
catch (Exception ex)
{
smsLog.LogSmsFailedDate = System.DateTime.Now.ToString("MM/dd/yyyy HHmmss");
smsLog.LogSentStatus = false;
smsLog.LogSmsFailedMessage = ex.Message;
Everest.Net.BusinessLayer.SMS.SmsSetup.InsertSMSLog(smsLog);
}
sms = Everest.Net.BusinessLayer.SMS.SmsSetup.GetSmsSetUp(OfficeId);
sms.SmsCount = sms.SmsCount - 1;
Everest.Net.BusinessLayer.SMS.SmsSetup.UpdateSmsSetup(sms);
Everest.Net.BusinessLayer.SMS.SmsSetup.InsertSMSLog(smsLog);
}
}
}
}
}
}
}
}
catch (Exception ex)
The ideal solution would remove the responsibility of sending the SMS from the web application itself. Instead, the web application should create a database record containing the message and recipient addresses, and a separate background job (e.g. a Windows Service) should poll the database and send SMS messages when neeeded. This is the best solution in terms of fault tolerance and auditability, because there is a permanent record of the messaging job which can be resumed if the system fails.
That being said, maybe you don't want to go to all that trouble. If you feel strongly that you wish to send the SMS directly from the ASP.NET application, you will need to create a Task and queue it to run using QueueBackgroundWorkitem. You will need to refactor your code a bit.
Move all the logic for sending the SMS into a separate function that accepts all the information needed as parameters. For example,
static void SendSMS(string[] addresses, string messagetext)
{
//Put your SMS code here
}
When you need to call the function, queue it as a background item
HostingEnvironment.QueueBackgroundWorkItem(a => SendSMS(addresses, messageText));
If your worker task needs to access its own cancellation token (e.g. if it is supposed to loop until cancelled), it is passed as an argument to the lambda expression. So you could modify the prototype
static void SendSMS(string[] addresses, string messagetext, CancellationToken token)
{
while (!token.IsCancellationRequested)
{
//Put your code here
}
}
and pass it thus:
HostingEnvironment.QueueBackgroundWorkItem(token => SendSMS(addresses, messageText, token));
Placing the task in the background queue ensures that ASP.NET keeps track of the thread, doesn't try to garbage collect it, and shuts it down properly when the application pool needs to shut down.
After queuing the background operation, your page can render is content per usual and conclude the HTTP response while the task continues to execute.

How to get update object with webhook in C#?

I use C# for programming Telegram Bot, but when I set webhook I can't fill Update object? I use ashx handler.
public void ProcessRequest(HttpContext context)
{
Fwk_Log.Insert("before","before");
var update = context.Request.QueryString["Update"];
Fwk_Log.Insert(update, "update = ");
long offset = 0;
int whilecount = 0;
int updateId = 0;
whilecount += 1;
string updates = Fwk_HttpRequest.ExecuteUrlRequestJSONString("https://api.telegram.org/bot" + Token + "/getUpdates");
Shp_Telegram_GetUpdate list = new JavaScriptSerializer().Deserialize<Shp_Telegram_GetUpdate>(updates);
if (list != null)
{
foreach (var r in list.result)
{
//offset = list.result.First().update_id;
if (r.message.text == "/start")
{
Fwk_HttpRequest.ExecuteUrlRequestJSONString("https://api.telegram.org/bot" + Token +
"/sendMessage?chat_id=" + r.message.chat.id + "&text=" + "Hello World");
Fwk_Log.Insert("sendMessage", "");
}
}
}
}
You are getting things mixed up. See here
There are two mutually exclusive ways of receiving updates for your
bot — the getUpdates method on one hand and Webhooks on the other.
Incoming updates are stored on the server until the bot receives them
either way, but they will not be kept longer than 24 hours.
Regardless of which option you choose, you will receive
JSON-serialized Update objects as a result.
If you are using webhooks, you do not need to call getUpdate method.

Transferring multiple commands/decisions through tcp socket

Good afternoon all!
The goal of the project:
Build a notification program with client, console, and server executable. Only selected users should get the notification.
The problem:
Sometimes the code works great, and everything works (20% of runs). The rest of the time, it will mess up the order the data is being sent in.
The code:
Server (console = TCPclient):
private void Connect()
{
string username = ReadFromConsole();
if (IsUserAllowed(username)) // Receive username
SendToConsole(bool.TrueString); // Send confirmation
else
{
SendToConsole(bool.FalseString); // Send denial
console.Close();
return;
}
string messageID = ReadFromConsole(); // Receive MessageID
string recipientCount = ReadFromConsole();
int numOfRecipients = int.Parse(recipientCount); // Receive and parse number of recipients
List<string> recipients = new List<string>();
for (int i = 0; i < numOfRecipients; i++)
{
string recipient = ReadFromConsole();
recipients.Add(recipient); // Receive recipient, add to list (required for Message)
}
string department = ReadFromConsole(); // Receive department string
string visibleTime = ReadFromConsole(); // Receive visibility timespan
string expiration = ReadFromConsole(); // Receive expiration datetime
StoreRTF(messageID); // Receive and store RTF file
console.Close(); // Connection is done, close
Message message = new Message(messageID, department, recipients, visibleTime, expiration);
}
Console (server = TCPclient):
private void SendMessage()
{
SendToServer(Environment.UserName);
if (bool.Parse(ReadFromServer()))
{
// User is allowed, continue
string messageID = DateTime.Now.ToUniversalTime().Ticks.ToString();
SendToServer(messageID); // MessageID
string recipientCount = lvRecipients.Items.Count.ToString();
SendToServer(lvRecipients.Items.Count.ToString()); // Amount of recipients
foreach (string item in lvRecipients.Items) // Loop to send each recipient
{
SendToServer(item);
}
string department = TB_Department.Text;
SendToServer(department); // Send department string
string visibleTime = TimeSpan.FromSeconds(SLIDER_VisibleTime.Value).Ticks.ToString();
SendToServer(visibleTime); // Send message visibility time
string expiration = DateTime.Now.ToUniversalTime().AddMinutes(2).ToString();
SendToServer(expiration); //TODO add UI control for this
SendRTFToServer(); // Send RTF file
MessageBox.Show(
"Your designated MessageID is: " + messageID + Environment.NewLine +
"Message upload is succesful.",
"Complete",
MessageBoxButton.OK);
}
else
{
// User is not allowed. Report to user. Disconnect (will be managed by the finally block)
MessageBox.Show("You are not allowed to upload messages to the server.", "Access denied", MessageBoxButton.OK, MessageBoxImage.Stop);
return;
}
}
Send and receive parts (same between console/server/client):
private void SendToServer(string toSend)
{
while (server.GetStream().DataAvailable)
{
// Should wait
}
StreamWriter writer = new StreamWriter(server.GetStream());
writer.WriteLine(toSend);
writer.Flush();
}
private void SendRTFToServer()
{
while (server.GetStream().DataAvailable)
{
// Should wait
}
File.Open(RTFLocation, FileMode.Open, FileAccess.Read).CopyTo(server.GetStream());
server.GetStream().Flush();
server.GetStream().Close();
}
private string ReadFromServer()
{
server.GetStream().Flush();
StreamReader reader = new StreamReader(server.GetStream());
return reader.ReadLine();
}
I have also tried different loops, implementations, switching to byte[]...
After lots of debugging I am getting nowhere. I have checked which information is leaving the console, and that all checks out and is in the right order. However, at the server end it seems to be receiving it in an entirely different order.
Anyone have an idea what is causing this?
I've found how to properly handle this. Code for the relevant parts can be found here:
C# Sockets send/receive problems and questions
Hope this may help someone in the future!

Misbehaving Service behviors

Basically I'm making a program to simulate a petrol station system.
My problem is that I'm trying to send a request through a WCF service such as this:
User Requests Pump to be activated ----> WCF SERVICE ----> Point of Sale
User starts pumping petrol<---- WCF SERVICE <---- Point of Sale Accepts
At the moment it works, but only sometimes.
This is how I try to get a response:
while(PumpserviceClient.getRequestedAcceptedStatusFromPos().Accepted == false)
{
PumpserviceClient.RequestPump(int.Parse(PumpID));
// needs to wait for pump to be activated
if (PumpserviceClient.getRequestedAcceptedStatusFromPos().Accepted == true /*&& PumpserviceClient.getRequestedAcceptedStatusFromPos().PumpNo == int.Parse(PumpID)*/)
{
MessageBox.Show(" The Pos has accepted your pump request");
// if its accepted you call
Customer.ActivatePump();
}
And these are the methods in the service:
bool Accepted= false;
bool Requested=false;
public void AcceptPump(int PumpNumber)
{
Accepted = true;
Requested = false;
int pumpnumber = PumpNumber;
PumpRequest.Accepted = Accepted;
PumpRequest.Requested = Requested;
}
public void RequestPump(int PumpNumber)
{
int pumpnumber = PumpNumber;
Requested = true;
Accepted = false;
PumpRequest.Accepted = Accepted;
PumpRequest.PumpNo = PumpNumber;
PumpRequest.Requested = Requested;
}
public void ResetRequest(int PumpNumber)
{
int pumpnumber = PumpNumber;
Requested = false;
Accepted = false;
PumpRequest.Accepted = Accepted;
PumpRequest.PumpNo = 0;
PumpRequest.Requested = Requested;
}
public Message getRequestedStatusFromPump()
{
return PumpRequest;
}
public Message getRequestedAcceptedStatusFromPos()
{
return PumpRequest;
}
}
and the point of sale system accepts the requests by:
if (Client.getRequestedStatusFromPump().Requested == true)
{
MessageBox.Show("Pump Number: "+Client.getRequestedStatusFromPump().PumpNo + " Is waiting to be accepted");
// need to press a button or something
Client.AcceptPump(Client.getRequestedStatusFromPump().PumpNo);
}
Code here http://www.pastebucket.com/8642
I read the code posted. You use the following attribute:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
This means your code will not multi-thread. But there is no guarantee multiple sessions won't make requests and "interrupt" each other's workflow.
For example:
Client A calls request pump
Client B calls reset pump
Client A reads... client A wonders why pump was reset.
Your code is written expecting the object to be by session. I'd suggest using this context mode and seeing if you have better luck.
The other option is to add session information to your model. I can't imagine why this would be useful. It certainly won't be easy.
The only way i found around this problem, without changing service behaviors was to create a new list
public void CreatePumpList()
{
WaitingPumps = new List<WaitingPumps>();
for (int i = 0; i < PumpLimit+1 ; i++)
{
WaitingPumps.Add(new WaitingPumps());
}
}
Then just use the pump Number as the index in this list so they don't get confused with each other.

Categories