Don't start a new timer every time someone calls !latency, and make it
work even if automatic pinging is disabled. Also, moved the command to
handler.py per @Vgr255's suggestion.
In some cases, disconnection from the server can be unclean and won't be
noticed until the bot tries to send a message, which may never happen if
it did not disconnect during the game. To solve this problem, it will
now ping the server every 2 minutes by default, which will result in
a broken pipe error if the connection is dead. This won't be able to
detect netsplits where the server the bot is on stays online, because
those are impossible to correctly detect in every case.
This commit also adds a !latency command so that users can verify if the
bot is lagging.
In 5-player charming games, it is possible to get into a situation where
it's impossible for the village to win - if all players except for the
wolf are charmed, lynching the wolf results in the piper winning, and
lynching the piper results in the wolf winning.
This was originally done so that we wouldn't need to iterate through the (presumably large) set of users. However, when this raises, we have no idea which users actually match the criterias. By waiting until after, the traceback will actually print off the list of all potential users.
This takes a different code path because of slightly different conditions; in the first case, the ident and host are both known, and so the instance is hashable. Being hashable, it can be checked for set containment, and exactly one instance in that set will be equal (since the hash is based off of the ident and host, and the comparisons check for all non-None attributes, two instances cannot possibly be equal while having a different hash).
In the second case (the new one), however, at least the ident or the host is missing, and so the hash cannot be calculated. This means that two instances may compare equal and hash to different values (since only non-None attributes are compared), so we need to run through the entire set no matter what to make sure that one - and only one - instance in that set compares equal with the new one. We can't know in advance whether or not there is an instance that compares equal to the new one in that set, nor can we know if there are multiple instances that are going to compare equal.
The two code paths are separated so that the difference in functionality is obvious (and this commit description, as well as the comment in the code, should help with that), and that the distinction remains clear.