Repository: Cog-Creators/Red-DiscordBot

Issue:
[Trivia] `[p]triviaset payout/stopafter/timelimit` can be set to NaN and Inf
Commands shouldn't allow neither `nan` nor `inf`.

## More details on the problems for each affected command:

#### For `[p]triviaset payout`:
- Using NaN will just work same as 0.
- Using Inf will cause an error in trivia session task (can't easily access the exception because of issue from #3578; you can use `trivia_sessions` attribute of the cog object to find the failed task if you need it for testing):
```py
OverflowError: cannot convert float infinity to integer
```

#### For `[p]triviaset timelimit`:
- Using NaN will cause an error in trivia session (same as above, can't easily access the error):
```py
ValueError: cannot convert float NaN to integer
```
- Inf "disables" time limit for answering the question but causes no errors.

#### For `[p]triviaset stopafter`:
- Using both NaN and Inf will cause the session to never end due to no response but it causes no errors.



Discussion:

Resolving patch:
diff --git a/redbot/cogs/trivia/trivia.py b/redbot/cogs/trivia/trivia.py
--- a/redbot/cogs/trivia/trivia.py
+++ b/redbot/cogs/trivia/trivia.py
@@ -1,9 +1,12 @@
 """Module for Trivia cog."""
+import math
 import pathlib
 from collections import Counter
 from typing import List
+
 import yaml
 import discord
+
 from redbot.core import commands
 from redbot.core import Config, checks
 from redbot.core.data_manager import cog_data_path
@@ -20,6 +23,16 @@
 _ = Translator("Trivia", __file__)
 
 
+def finite_float(arg: str) -> float:
+    try:
+        ret = float(arg)
+    except ValueError:
+        raise commands.BadArgument(_("`{arg}` is not a number.").format(arg=arg))
+    if not math.isfinite(ret):
+        raise commands.BadArgument(_("`{arg}` is not a finite number.").format(arg=ret))
+    return ret
+
+
 class InvalidListError(Exception):
     """A Trivia list file is in invalid format."""
 
@@ -81,7 +94,7 @@ async def triviaset_max_score(self, ctx: commands.Context, score: int):
         await ctx.send(_("Done. Points required to win set to {num}.").format(num=score))
 
     @triviaset.command(name="timelimit")
-    async def triviaset_timelimit(self, ctx: commands.Context, seconds: float):
+    async def triviaset_timelimit(self, ctx: commands.Context, seconds: finite_float):
         """Set the maximum seconds permitted to answer a question."""
         if seconds < 4.0:
             await ctx.send(_("Must be at least 4 seconds."))
@@ -91,7 +104,7 @@ async def triviaset_timelimit(self, ctx: commands.Context, seconds: float):
         await ctx.send(_("Done. Maximum seconds to answer set to {num}.").format(num=seconds))
 
     @triviaset.command(name="stopafter")
-    async def triviaset_stopafter(self, ctx: commands.Context, seconds: float):
+    async def triviaset_stopafter(self, ctx: commands.Context, seconds: finite_float):
         """Set how long until trivia stops due to no response."""
         settings = self.conf.guild(ctx.guild)
         if seconds < await settings.delay():
@@ -150,7 +163,7 @@ async def trivaset_reveal_answer(self, ctx: commands.Context, enabled: bool):
 
     @triviaset.command(name="payout")
     @check_global_setting_admin()
-    async def triviaset_payout_multiplier(self, ctx: commands.Context, multiplier: float):
+    async def triviaset_payout_multiplier(self, ctx: commands.Context, multiplier: finite_float):
         """Set the payout multiplier.
 
         This can be any positive decimal number. If a user wins trivia when at


Test patch: