I'm trying to make a Spam Filter for a discord server of mine, building it into the bot I already have. This filter will, when finished, delete any 2 consecutive messages that are the same in order to prevent copy and paste spam. So far, I have most of the functionality implemented, but I cannot figure out how to convert Discord.Messages[] to strings, in order to properly compare current and past messages. What I have so far looks like this;
// Spam Filter
discord.MessageReceived += async (s, e) =>
{
var channel = e.Server.FindChannels("general", ChannelType.Text).FirstOrDefault();
var user = discord.CurrentUser;
spamold = spamnew;
Message[] messagesToObtain;
messagesToObtain = await e.Channel.DownloadMessages(1);
spamnew = string.Format(messagesToObtain);
if(spamnew == spamold)
{
Message[] messagesToDelete;
messagesToDelete = await e.Channel.DownloadMessages(2);
await e.Channel.DeleteMessages(messagesToDelete);
};
};
I'm capable of making enough modifications for the program to run, but when it does so it doesn't properly compare strings and so it continually deletes 2 messages for every 1 sent.
[insert unnecessary rant below]
I'm not the best with C#, nor am I in any C language but I understand the very basics such as variable types and the grammatical structure. It's a wonder I've managed to get this far making my bot without the need for actually asking for help on any forums (Well I did once but nobody answered). I would be overjoyed to get this thing working.
Related
I'm currently programming a bot that will be able to purge a channel from all of his messages. While doing so, I encountered a few problems.
I started by using
IEnumerable<IMessage> messages = await channel.GetMessagesAsync(1000).FlattenAsync();
await ((ITextChannel)channel).DeleteMessagesAsync(messages);
It worked, but you can't deleted messages older than 2 weeks for some unknown reasons.
People told me than this doesn't happen if you delete each messages individualy using DeleteAsync(), so I did
IEnumerable<IMessage> messages;
do
{
messages = await channel.GetMessagesAsync(100).FlattenAsync();
foreach (IMessage item in messages)
{
item.DeleteAsync();
}
} while (messages.Count() != 0);
Now when I use it, I get the "Rate limit triggered" error, which makes sense.
But now, I'm looking for a way to delete all of my messages, while staying under the rate limit.
How can I know that the next request (to deleted a message) will trigger the rate limit (so my bot can wait for the limit to leave)?
Is there a way to get the current "Bucket" using the wrapper/API?
Or is there an altogether better way to purge a channel?
Like someone in the comments mentioned; If you really wanted to delete all messages in a channel, 'copying' the channel and deleting the old one is a solution.
Like so:
var oldChannel = ((ITextChannel)channel);
// Assuming you have a variable 'guild' that is a IGuild
// (Which is your targeted guild)
await guild.CreateTextChannelAsync(oldChannel.Name, newChannel =>
{
// Copies over all the properties of the channel to the new channel
newChannel.CategoryId = oldChannel.CategoryId;
newChannel.Topic = oldChannel.Topic;
newChannel.Position = oldChannel.Position;
newChannel.SlowModeInterval = oldChannel.SlowModeInterval;
newChannel.IsNsfw = oldChannel.IsNsfw;
});
await oldChannel.DeleteAsync();
The downside is that the bot now needs permission to manage channel, rather than manage messages.
Though if you really wanted to only delete messages without using the former method, you can add a delay before you delete each message. Like so:
//...
foreach (IMessage item in messages)
{
await item.DeleteAsync();
// Waits half a second before deleting the next.
await Task.Delay(500)
}
//...
Downside to this is that it would take some time to delete all the messages.
With some modifications, you can combine this with ((ITextChannel)channel).DeleteMessagesAsync(messages) first to purge the newer messages, before using this loop. This will cut down some time to delete all the messages.
EDIT: I've edited a few lines of code, when running in the IDE it fails without an error or anything.
I'm new to Reactive Extensions and have a problem that I am trying to sort out. I'm using RX to queue events on a machine, then every so often send that data to a server. My problem appears to be that when the application is shutting down, anything that is an async call of any sort seems to just cancel and not run, thus the last batch of events never gets sent.
I have a Subject, where Event is my data class. I know now that a Subject might not be the best class to use, but here we are.
My code looks mostly like the following, added a few comments for clarity:
IObservable<IList<Event>> eventsObserver = Instance.EventBuffer.ToList<Event>();
var eventsEnumerable = eventsObserver.ToEnumerable();
List<Event> events = new List<Event>();
try
{
events = (List<Event>)eventsEnumerable.First(); // THIS LINE FAILS SILENTLY, EVEN IN DEBUGGER...
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine("Error: " + ex.Message);
}
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(someURI);
HttpResponseMessage response = client.PostAsync(somePage, new StringContent(SerializeToJSON(events))).Result;
response.EnsureSuccessStatusCode();
}
If I don't make the call to the web server synchronous (with the '.Result'), it fails there. I've tried a lot of ways to get the data from the IObservable into something I can send, but either the code fails (usually with a bad cast of some sort), or the events are not yet put into the data structure that I want to send. I know that RX is by nature async, and I'm asking to deal with it in a synchronous way, I would figure that there would be a solution. Thanks in advance!
Supposing that you control the Observable source, you could call Observable.OnComplete() like Enigmativity has pointed out. Otherwise, you could try to keep a copy of every value received before buffering it:
Observable.Do(x => localCopy = x).Buffer(..)
This local copy would be accessible to you at shutdown.
In any case, please note that .First() is marked obsolete in the latest Rx versions, possibly to avoid the problem you are experiencing.
I have to make a c# application which uses REST api to fetch JIRA issues. After I run the tool I am getting the correct output but it is taking a lot of time to display the output. Below is the part of code which is taking the maximum time
var client =new WebClient();
foreach(dynamic i in jira_keys)
{
issue_id=i.key;
string rest_api_url="some valid url"+issue_id;
var jira_response=client.DownloadString(rest_api_url);
//rest of the processing
}
jira_keys is a JArray. After this there is processing part of the JSON in the for each loop. This is taking a lot of time as the number of jira_keys increase. I cannot apply multi-threading to this since there are shared variable issues. So please someone suggest some way to optimise this.
If the issues are tied to a specific project or some other grouping, you can instead search for issues with a JQL string. This way you get them in bulk and paginated.
https://docs.atlassian.com/jira/REST/cloud/#api/2/search-search
Also, like cubrr said in his comment, async calls should work fine if you want to make api calls with multiple threads. Awaiting the call will block until the shared resources are ready.
(Would have posted as a comment if I had enough rep)
Here is an example of how you can fetch the responses from JIRA asynchronously.
var taskList = new List<Task<string>>();
foreach (dynamic i in jira_keys)
{
issue_id = i.key;
string rest_api_url = "some valid url" + issue_id;
var jiraDownloadTask = Task.Factory.StartNew(() => client.DownloadString(rest_api_url));
taskList.Add(jiraDownloadTask);
}
Task.WaitAll(taskList.ToArray());
//access the results
foreach(var task in taskList)
{
Console.WriteLine(task.Result);
}
I'm working on small C# application, that application has one DataGrid on main screen and items in it, application has one purpose: when user press F9 it will loop throught DataGrid items and write that items from datagrid in notepad and send that notepad to a FISCAL PRINTER (that means put that notepad file to path where printer is listening). (* Explanation what is fiscal printer: that is small printer which is waiting for notepad files on some location i.e (C:\MyFiles), and if you give him correct file he will proceed it ->it means paper with items will come out of printer, price, item title etc, and printer will put his own file where he's telling is everything ok, and also printer will write there one special number that is called FISCAL NUMBER ID*), now lets back to C# code!
So I did everything, everything is working fine, but there is one issue:
When I leave correct file in folder where printer is listening, printer will take it, and my application will keep listening for printer's file because application must read that FISCAL NUMBER ID from that printers file.
And here comes my problem, because application must listen for printer to answer, and to read his file to put that FISCAL NUMBER ID to database etc that takes a lot time, like 3-4 seconds, so while that is happening it is impossible to click anywhere on my application or smth like that, it simply freezes!
So my question is how could I put this method in a thread or something so it can do her job while my application is still responsive to user clicks/requests.
Here is my code when user PRESS F9:
if (e.Key == Key.F9)
{
try
{
if (dtgStavke.Items.Count > 0)
{
Printer printer = new Printer();
printer.AdressAnswer = "C:\\MyPrinterFiles\\";
printer.AdressError = "C:\\MyPrinterFiles\\Errors\\";
printer.AdressExit = "C:\\MyPrinterFiles\\Good\\";
Bill newBill = new Bill();
newBill.OrdinalNumber = Convert.ToInt32(BillController.Instance.GetNextBillOrdinalNumber())+1;
newBill.Reversed = false;
newBill.Fiscalized = false;
newBill.BFF = 0;
newBill.BRR = 0;
newBill.TotalAmount = stavkeTemp.Sum(x => (x.Price*x.Quantity));
newBill.TotalWithoutTax = totalWithoutTAXGlobal;
newBill.TotalTax = totalTaxGlobal;
if (_globalCustomer != null)
{
if (_globalCustomer.Status == 1)
{
newBill.CompanyId = _globalCustomer.Id;
}
else
newBill.PersonId = _globalCustomer.Id;
}
else
{
newBill.CompanyId = 1;
newBill.PersonId = 1;
newBill.UserId=1;
}
Bill lastInsertedBill = BillController.Instance.Save(newBill);
}
bool succeed = Print(lastInsertedBill, p, printer, _globalCustomer, false);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
How could I isolate this to a new thread or something like that, I tried few things but it is definately not working?
There is Print method and everything is happening there:
file making
waiting for answer to read printer's file
again updating database with that fiscal printer id from that printers file
So must of the work is happenig there, so It would be nice to isolate it somehow, to keep my application responsive to user requestes while Print method is doing her job
bool succeed = Print(lastInsertedBill, p, printer, _globalCustomer, false);
Thanks guys
Cheers!
The easy answer would be if you could make it with async await like Sparrow already mentioned.
The call to Print would afterwards look like this:
bool succeed = await Task.Run(()=>Print(lastInsertedBill, p, printer, _globalCustomer, false)).ConfigureAwait(false);
The method signature of the code where your whole code construct is in has to change as well. So imagine your current signature looks like this:
public void KeyPressed(Object o, KeyPressEventArgs e)
It should change to look afterwards like this:
public async Task KeyPressed(Object o, KeyPressEventArgs e)
These changes would make your Print method run in a different Task (which is simply said a more efficient way of utilizing other Threads). So in this way your UI-Thread is free to do stuff while another Background-Thread does your Print job.
If you want to read up on async await I'd recommend this blog by Stpehen Cleary as a starting point. And a book that helped me a lot regarding async await was "Async in C# 5.0" by Alex Davies. It has only 108 pages, is really good written and you gain all the knowledge you need.
We are using MailKit to successfully send emails by creating a client, making a connection and sending a mail. Very standard stuff which works great as we receive a message in our application, we do something and send on an email.
However we want to be able to process 10's of thousands of emails as quickly as possible.
Our destination email server may be unavailable and therefore we could quickly backup messages therefore producing a backlog.
With MailKit, what is the best and quickwest way to process the mails so that they get sent as quickly as possible. For example at the moment each mail may be processed one after the other and if they take a second each to process it could take a long time to send 40000 mails.
We have been using a parallel foreach to spin up a number of threads but this has limitations. Any suggestions or recommendations would be appreciated.
Code sample added: CORRECTION, NEW CODE SAMPLE ADDED. This is much faster but I cannot get it to work creating a new connection each time. Exchange throws errors 'sender already specified'. This is currently sending around 6 mails per second on average.
var rangePartitioner = Partitioner.Create(0, inpList.Count, 15);
var po = new ParallelOptions { MaxDegreeOfParallelism = 30 };
Parallel.ForEach(rangePartitioner, (range, loopState) =>
{
using (var client = new SmtpClient(new SlabProtocolLogger()))
{
client.Connect(_appSettings.RelayAddress, _appSettings.RelayPort);
client.AuthenticationMechanisms.Remove("XOAUTH2");
for (int i = range.Item1; i < range.Item2; i++)
{
var message = _outboundQueueRepository.Read(inpList[i]).Load();
client.Send(message.Body, message.Metadata.Sender, message.Metadata.Recipients.Select(r => (MailboxAddress)r));
_outboundQueueRepository.Remove(inpList[i]);
};
}
});
Correct me if I'm wrong, but it looks to me like the way this works is that the Parallel.Foreach is creating some number of threads. Each thread is then creating an SMTP connection and then looping to send a batch of messages.
This seems pretty reasonable to me.
The only advice I can give you that might optimize this more is if many of these messages have the exact same content and the same From address and that the only difference is who the recipients are, you could vastly reduce the number of messages you need to send.
For example, if you are currently doing something like sending out the following 3 messages:
Message #1:
From: no-reply#company.com
To: Joe The Plumber <joe#plumbing-masters.com>
Subject: We've got a new sale! 50% off everything in stock!
some message text goes here.
Message #2
From: no-reply#company.com
To: Sara the Chef <sara#amazing-chefs.com>
Subject: We've got a new sale! 50% off everything in stock!
some message text goes here.
Message #3:
From: no-reply#company.com
To: Ben the Cobbler <ben#cobblers-r-us.com>
Subject: We've got a new sale! 50% off everything in stock!
some message text goes here.
Your code might create 3 threads, sending 1 of the messages in each of those threads.
But what if, instead, you created the following single message:
From: no-reply#company.com
To: undisclosed-recipients:;
Subject: We've got a new sale! 50% off everything in stock!
some message text goes here.
and then used the following code to send to all 3 customers at the same MimeMessage?
var sender = new MailboxAddress (null, "no-reply#company.com");
var recipients = new List<MailboxAddress> ();
recipients.Add (new MailboxAddress ("Joe the Plumber", "joe#plumbing-masters.com"));
recipients.Add (new MailboxAddress ("Sara the Chef", "sara#amazing-chefs.com"));
recipients.Add (new MailboxAddress ("Ben the Cobbler", "ben#cobblers-r-us.com"));
client.Send (message, sender, recipients);
All 3 of your customers will receive the same email and you didn't have to send 3 messages, you only needed to send 1 message.
You may already understand this concept so this might not help you at all - I merely mention it because I've noticed that this is not immediately obvious to everyone. Some people think they need to send 1 message per recipient and so end up in a situation where they try to optimize sending 1000's of messages when really they only ever needed to send 1.
So we found a wider ranging fix which improved performance massively in addition to the improvements we found with the Parrallel ForEach loop. Not related to MailKit but I thought I would share anyway. The way that our calling code was creating our inputList was to use DirectoryInfo.GetDirectories to enumerate over all the first in the directory. In some cases our code took 2 seconds to execute over the directory with 40k files in it. We changed this to EnumerateDirectories and it effecitvely freed up the mail sending code to send many many emails.
So to confirm, the Parallel loop worked great, underlying performance issue elsewhere was the real bottleneck.