Repository: sopel-irc/sopel

Issue:
Rate limit remaining-time placeholders contain microseconds
Tin, pretty much:

```
[09:06] <notdgw> .test
[09:06] -SopelTest- Relax, notdgw. You can .test again in 0:04:58.030534.
```

`{time_left}` and `{time_left_sec}` don't need this much precision. Effectively doing `math.ceil()` to the next second would be good enough. (Truncating the microseconds would probably be good enough, too, but I'd prefer just always rounding up because users _will_ try to snipe the remaining time if it's from a competitive game or something.)

Would be nice to include in 8.0.2 but not critical; can bump to a new 8.0.3 milestone if it's holding up the line.

Hints from issue comments:

Solution patch:
diff --git a/sopel/bot.py b/sopel/bot.py
index d52e1707..07bf7802 100644
--- a/sopel/bot.py
+++ b/sopel/bot.py
@@ -8,9 +8,11 @@
 from __future__ import annotations
 
 from ast import literal_eval
+from datetime import timedelta
 import inspect
 import itertools
 import logging
+import math
 import re
 import threading
 import time
@@ -631,7 +633,11 @@ class Sopel(irc.AbstractBot):
             return False, None
 
         next_time = metrics.last_time + rate_limit
-        time_left = next_time - at_time
+        time_left = timedelta(
+            seconds=math.ceil(
+                (next_time - at_time).total_seconds()
+            )
+        )
 
         message: Optional[str] = None
 


Test patch:
diff --git a/test/test_bot.py b/test/test_bot.py
index 73904e81..63f68c3b 100644
--- a/test/test_bot.py
+++ b/test/test_bot.py
@@ -1174,8 +1174,8 @@ def test_rate_limit_time_left_field(mockbot, match_hello_rule, limit_type):
     # assert there is now a NOTICE which contains templated time left information
     assert mockbot.backend.message_sent
     patt = (br"NOTICE Test :"
-            br"time_left=\d+:\d+:\d+(\.\d+)? "
-            br"time_left_sec=\d+(\.\d+)?")
+            br"time_left=\d+:\d+:\d+ "
+            br"time_left_sec=\d+")
     assert re.match(patt, mockbot.backend.message_sent[0])