countdown-bot

A Discord bot that runs countdown games and generates analytics
git clone https://git.ashermorgan.net/countdown-bot/
Log | Files | Refs | README

commit b494099d65dc6543a0a7095f88279b043bd917ab
parent 09db08c9e679a3eab3da08904003182a83674a32
Author: ashermorgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Sat,  3 Jul 2021 16:02:37 -0700

Clean up code and improve help information

Diffstat:
MREADME.md | 20++++++++++----------
Msrc/analyticsCog.py | 10+++++-----
Msrc/botUtilities.py | 36++++++++++++++++++++++--------------
Msrc/models.py | 30+++++++++++++++---------------
Msrc/utilitiesCog.py | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
5 files changed, 108 insertions(+), 60 deletions(-)

diff --git a/README.md b/README.md @@ -1,5 +1,5 @@ # countdown-bot -A Discord bot to facilitate countdowns +A Discord bot that facilitates countdowns and generates detailed countdown analytics @@ -11,24 +11,24 @@ A Discord bot to facilitate countdowns 2. Go to the [Discord Developer Portal](https://discord.com/developers/) and create an application and a bot -3. Add the bot to your server - ``` - https://discordapp.com/oauth2/authorize?client_id=BOT_ID_HERE&scope=bot&permissions=232512 - ``` - -4. Run `setup.py` +3. Run `setup.py` ``` python setup.py ``` -5. Open `settings.json` (which was generated by `setup.py`) and add your bot's token +4. Open `settings.json` (which was generated by `setup.py`) and add your bot's token ```json {"token": "YOUR_TOKEN_HERE", "prefixes": ["c."], "database": "sqlite:///data.sqlite3"} ``` -6. Run the bot +5. Run the bot ``` python run.py ``` -7. Run `c.help` to get a list of commands and a description of the bot's behavior +6. Add the bot to your server + ``` + https://discordapp.com/oauth2/authorize?client_id=BOT_ID_HERE&scope=bot&permissions=232512 + ``` + +7. Send `c.help` to the bot get a list of commands and a description of the bot's behavior diff --git a/src/analyticsCog.py b/src/analyticsCog.py @@ -136,7 +136,7 @@ class Analytics(commands.Cog): try: os.remove(tmp.name) except: - print(f"Unable to delete temp file: {tmp.name}.") + print(f"Unable to delete temp file: {tmp.name}") @@ -216,7 +216,7 @@ class Analytics(commands.Cog): try: os.remove(tmp.name) except: - print(f"Unable to delete temp file: {tmp.name}.") + print(f"Unable to delete temp file: {tmp.name}") @@ -281,7 +281,7 @@ class Analytics(commands.Cog): try: os.remove(tmp.name) except: - print(f"Unable to delete temp file: {tmp.name}.") + print(f"Unable to delete temp file: {tmp.name}") @@ -423,7 +423,7 @@ class Analytics(commands.Cog): try: os.remove(tmp.name) except: - print(f"Unable to delete temp file: {tmp.name}.") + print(f"Unable to delete temp file: {tmp.name}") @@ -496,4 +496,4 @@ class Analytics(commands.Cog): try: os.remove(tmp.name) except: - print(f"Unable to delete temp file: {tmp.name}.") + print(f"Unable to delete temp file: {tmp.name}") diff --git a/src/botUtilities.py b/src/botUtilities.py @@ -29,19 +29,19 @@ class CountdownNotFound(Exception): async def getUsername(bot, id): """ - Get a username from a user ID. + Get a username from a user ID Parameters ---------- bot : commands.Bot The bot id : int - The user ID. + The user ID Returns ------- str - The username (ex: "user#0000"). + The username (ex: "user#0000") """ user = await bot.fetch_user(id) @@ -100,20 +100,28 @@ async def getContributor(bot, countdown, text): contributors = [x["author"] for x in countdown.contributors()] # Get user from mention - if (re.match("^<@!\d+>$", text) and int(text[3:-1]) in contributors): return int(text[3:-1]) - elif (re.match("^<@!\d+>$", text)): raise ContributorNotFound(text) + if (re.match("^<@!\d+>$", text) and int(text[3:-1]) in contributors): + return int(text[3:-1]) + elif (re.match("^<@!\d+>$", text)): + raise ContributorNotFound(text) # Get user from username for contributor in contributors: - try: username = await getUsername(bot, contributor) - except: continue - if (username.lower().startswith(text.lower())): return contributor + try: + username = await getUsername(bot, contributor) + except: + continue + if (username.lower().startswith(text.lower())): + return contributor # Get user from nickname for contributor in contributors: - try: nickname = await getNickname(bot, countdown.server_id, contributor) - except: continue - if (nickname.lower().startswith(text.lower())): return contributor + try: + nickname = await getNickname(bot, countdown.server_id, contributor) + except: + continue + if (nickname.lower().startswith(text.lower())): + return contributor raise ContributorNotFound(text) @@ -142,7 +150,7 @@ def getCountdown(session, id): def getContextCountdown(session, ctx): """ - Get the most relevant countdown to a certain context. + Get the most relevant countdown to a certain context Parameters ---------- @@ -155,7 +163,7 @@ def getContextCountdown(session, ctx): ------- Countdown The countdown - + Raises ------ CountdownNotFound @@ -182,7 +190,7 @@ def getContextCountdown(session, ctx): def getPrefix(databaseSessionMaker, ctx, default): """ - Get the bot prefix for a certain context. + Get the bot prefix for a certain context Parameters ---------- diff --git a/src/models.py b/src/models.py @@ -45,15 +45,15 @@ POINT_RULES = { # Error classes class EmptyCountdownError(Exception): - """Raised when an action cannot be completed because the countdown is empty.""" + """Raised when an action cannot be completed because the countdown is empty""" pass class MessageNotAllowedError(Exception): - """Raised when someone posts twice in a row.""" + """Raised when someone posts twice in a row""" pass class MessageIncorrectError(Exception): - """Raised when someone posts an incorrect number.""" + """Raised when someone posts an incorrect number""" pass @@ -133,12 +133,12 @@ class Countdown(Base): def contributors(self): """ - Get countdown contributor statistics. + Get countdown contributor statistics Returns ------- list - A list of contributor statistics. + A list of contributor statistics """ # Make sure countdown has started @@ -164,17 +164,17 @@ class Countdown(Base): def eta(self, period=timedelta(days=1)): """ - Get countdown eta statistics. + Get countdown eta statistics Parameters ---------- period : timedelta - The period size. The default is 1 day. + The period size (the default is 1 day) Returns ------- list - The countdown eta statistics. + The countdown eta statistics """ # Make sure countdown has at least two messages @@ -259,12 +259,12 @@ class Countdown(Base): def leaderboard(self): """ - Get countdown leaderboard. + Get countdown leaderboard Returns ------- list - The leaderboard. + The leaderboard """ # Make sure countdown has started @@ -328,12 +328,12 @@ class Countdown(Base): def progress(self): """ - Get countdown progress statistics. + Get countdown progress statistics Returns ------- dict - A dictionary containing countdown progress statistics. + A dictionary containing countdown progress statistics """ # Make sure countdown has started @@ -375,17 +375,17 @@ class Countdown(Base): def speed(self, period=timedelta(days=1)): """ - Get countdown speed statistics. + Get countdown speed statistics Parameters ---------- periodLength : timedelta - The period size. The default is 1 day. + The period size (the default is 1 day) Returns ------- list - The countdown speed statistics. + The countdown speed statistics """ # Make sure countdown has started diff --git a/src/utilitiesCog.py b/src/utilitiesCog.py @@ -47,7 +47,7 @@ class Utilities(commands.Cog): # Send initial response print(f"Activated {self.bot.get_channel(ctx.channel.id)} as a countdown") - embed = discord.Embed(title=":clock3: Loading Countdown", description="@here This channel is now a countdown\nPlease wait to start counting", color=COLORS["embed"]) + embed = discord.Embed(title=":clock3: Loading Countdown", description="This channel is now a countdown\nPlease wait to start counting", color=COLORS["embed"]) msg = await ctx.send(embed=embed) # Load countdown @@ -56,7 +56,7 @@ class Utilities(commands.Cog): session.commit() # Send final response - embed = discord.Embed(title=":white_check_mark: Countdown Activated", description="@here This channel is now a countdown\nYou may start counting!", color=COLORS["embed"]) + embed = discord.Embed(title=":white_check_mark: Countdown Activated", description="This channel is now a countdown\nYou may start counting!", color=COLORS["embed"]) await msg.edit(embed=embed) @@ -89,6 +89,8 @@ class Utilities(commands.Cog): embed.description += f"**Reactions:**\n" for number in list(dict.fromkeys([x.number for x in countdown.reactions])): embed.description += f"**-** #{number}: {', '.join([x.value for x in countdown.reactions if x.number == number])}\n" + embed.description += f"\nUse `{ctx.prefix}help config` to view more information about settings\n" + embed.description += f"Use `{ctx.prefix}config <key> <value>` to modify settings\n" elif (not ctx.message.author.guild_permissions.administrator): raise CommandError("You must be an administrator to modify settings") elif (len(args) == 0): @@ -150,7 +152,7 @@ class Utilities(commands.Cog): # Send response print(f"Deactivated {self.bot.get_channel(ctx.channel.id)} as a countdown") - embed = discord.Embed(title=":octagonal_sign: Countdown Deactivated", description="@here This channel is no longer a countdown", color=COLORS["embed"]) + embed = discord.Embed(title=":octagonal_sign: Countdown Deactivated", description="This channel is no longer a countdown", color=COLORS["embed"]) await ctx.send(embed=embed) @@ -201,13 +203,17 @@ class Utilities(commands.Cog): f"**Usage:** `{prefixes[0]}activate`\n" \ "**Aliases:** none\n" \ "**Arguments:** none\n" \ + "**Examples:**\n" \ + f"**-** `{prefixes[0]}activate`\n" \ "**Notes:** Users must have admin permissions to turn a channel into a countdown\n", "analytics": "**Name:** analytics\n" \ "**Description:** Shows all countdown analytics\n" \ f"**Usage:** `{prefixes[0]}analytics|a`\n" \ "**Aliases:** `a`\n" \ - "**Arguments:**\n" \ + "**Arguments: none**\n" \ + "**Examples:**\n" \ + f"**-** `{prefixes[0]}analytics`\n" \ "**Notes:** none\n", "config": "**Name:** config\n" \ @@ -215,12 +221,17 @@ class Utilities(commands.Cog): f"**Usage:** `{prefixes[0]}config [<key> <value>...]`\n" \ "**Aliases:** none\n" \ "**Arguments:**\n" \ - "**-** `<key>`: The name of the setting to modify (see below).\n" \ - "**-** `<value>`: The new value(s) for the setting. If no key-value pair is supplied, all settings will be shown.\n" \ + "**-** `<key>`: The name of the setting to modify. If no key is supplied, all settings will be shown\n" \ + "**-** `<value>`: The new value(s) for the setting\n" \ "**Available Settings:**\n" \ - "**-** `prefix`, `prefixes`: The prefix(es) for the self.bot.\n" \ - "**-** `tz`, `timezone`: The UTC offset, in hours.\n" \ - "**-** `react`: The reactions for a certain number. Ex: `react 0 :partying_face: :smile:`\n" \ + "**-** `prefix`, `prefixes`: The prefix(es) for the bot\n" \ + "**-** `tz`, `timezone`: The UTC offset in hours\n" \ + "**-** `react`: The reactions for a certain number\n" \ + "**Examples:**\n" \ + f"**-** `{prefixes[0]}config`\n" \ + f"**-** `{prefixes[0]}config prefixes prefix1 prefix2 prefix3`\n" \ + f"**-** `{prefixes[0]}config timezone -1.5`\n" \ + f"**-** `{prefixes[0]}config react 0 :partying_face: :smile:`\n" \ "**Notes:** Users must have admin permissions to modify settings\n", "contributors": "**Name:** contributors\n" \ @@ -229,6 +240,9 @@ class Utilities(commands.Cog): "**Aliases:** `c`\n" \ "**Arguments:**\n" \ "**-** `history`, `h`: Shows historical data about countdown contributors\n" \ + "**Examples:**\n" \ + f"**-** `{prefixes[0]}contributors`\n" \ + f"**-** `{prefixes[0]}contributors history`\n" \ "**Notes:** The contributors embed will only show the top 20 contributors\n", "deactivate": "**Name:** deactivate\n" \ @@ -236,6 +250,8 @@ class Utilities(commands.Cog): f"**Usage:** `{prefixes[0]}deactivate`\n" \ "**Aliases:** none\n" \ "**Arguments:** none\n" \ + "**Examples:**\n" \ + f"**-** `{prefixes[0]}deactivate`\n" \ "**Notes:** Users must have admin permissions to deactivate a countdown channel\n", "eta": "**Name:** eta\n" \ @@ -243,7 +259,10 @@ class Utilities(commands.Cog): f"**Usage:** `{prefixes[0]}eta|e [<period>]`\n" \ "**Aliases:** `e`\n" \ "**Arguments:**\n" \ - "**-** `<period>`: The size of the period in hours. The default is 24 hours.\n" \ + "**-** `<period>`: The size of the period in hours (the default is 24 hours)\n" \ + "**Examples:**\n" \ + f"**-** `{prefixes[0]}eta`\n" \ + f"**-** `{prefixes[0]}eta 48`\n" \ "**Notes:** none\n", "heatmap": "**Name:** heatmap\n" \ @@ -251,7 +270,11 @@ class Utilities(commands.Cog): f"**Usage:** `{prefixes[0]}heatmap [<user>]`\n" \ "**Aliases:** none\n" \ "**Arguments:**\n" \ - "**-** `<user>`: The username or nickname of the user to view heatmap information about. If no value is supplied, the general heatmap will be shown.\n" \ + "**-** `<user>`: The username or nickname of the user to view heatmap information about. If no value is supplied, the general heatmap will be shown\n" \ + "**Examples:**\n" \ + f"**-** `{prefixes[0]}heatmap`\n" \ + f"**-** `{prefixes[0]}heatmap @Alice`\n" \ + f"**-** `{prefixes[0]}heatmap Bob`\n" \ "**Notes:** none\n", "help": "**Name:** help\n" \ @@ -259,7 +282,10 @@ class Utilities(commands.Cog): f"**Usage:** `{prefixes[0]}help|h [<command>]`\n" \ "**Aliases:** `h`\n" \ "**Arguments:**\n" \ - "**-** `<command>`: The command to view help information about. If no value is supplied, general help information will be shown.\n" \ + "**-** `<command>`: The command to view help information about. If no value is supplied, general help information will be shown\n" \ + "**Examples:**\n" \ + f"**-** `{prefixes[0]}help`\n" \ + f"**-** `{prefixes[0]}help config`\n" \ "**Notes:** none\n", "leaderboard": "**Name:** leaderboard\n" \ @@ -267,7 +293,12 @@ class Utilities(commands.Cog): f"**Usage:** `{prefixes[0]}leaderboard|l [<user>]`\n" \ "**Aliases:** `l`\n" \ "**Arguments:**\n" \ - "**-** `<user>`: The rank, username, or nickname of the user to view leaderboard information about. If no value is supplied, the whole leaderboard will be shown.\n" \ + "**-** `<user>`: The rank, username, or nickname of the user to view leaderboard information about. If no value is supplied, the whole leaderboard will be shown\n" \ + "**Examples:**\n" \ + f"**-** `{prefixes[0]}leaderboard`\n" \ + f"**-** `{prefixes[0]}leaderboard 1`\n" \ + f"**-** `{prefixes[0]}leaderboard @Alice`\n" \ + f"**-** `{prefixes[0]}leaderboard Bob`\n" \ "**Notes:** The leaderboard embed will only show the top 20 contributors\n", "ping": "**Name:** ping\n" \ @@ -275,6 +306,8 @@ class Utilities(commands.Cog): f"**Usage:** `{prefixes[0]}ping`\n" \ "**Aliases:** none\n" \ "**Arguments:** none\n" \ + "**Examples:**\n" \ + f"**-** `{prefixes[0]}ping`\n" \ "**Notes:** none\n", "progress": "**Name:** progress\n" \ @@ -282,6 +315,8 @@ class Utilities(commands.Cog): f"**Usage:** `{prefixes[0]}progress|p`\n" \ "**Aliases:** `p`\n" \ "**Arguments:** none\n" \ + "**Examples:**\n" \ + f"**-** `{prefixes[0]}progress`\n" \ "**Notes:** none\n", "reload": "**Name:** reload\n" \ @@ -289,14 +324,19 @@ class Utilities(commands.Cog): f"**Usage:** `{prefixes[0]}reload`\n" \ "**Aliases:** none\n" \ "**Arguments:** none\n" \ - "**Notes:** none\n", + "**Examples:**\n" \ + f"**-** `{prefixes[0]}reload`\n" \ + "**Notes:** This command must be used in a countdown channel\n", "speed": "**Name:** speed\n" \ "**Description:** Shows information about countdown speed\n" \ f"**Usage:** `{prefixes[0]}speed|s [<period>]`\n" \ "**Aliases:** `s`\n" \ "**Arguments:**\n" \ - "**-** `<period>`: The size of the period in hours. The default is 24 hours.\n" \ + "**-** `<period>`: The size of the period in hours (the default is 24 hours)\n" \ + "**Examples:**\n" \ + f"**-** `{prefixes[0]}speed`\n" \ + f"**-** `{prefixes[0]}speed 48`\n" \ "**Notes:** none\n", } @@ -366,7 +406,7 @@ class Utilities(commands.Cog): countdown = getCountdown(session, ctx.channel.id) if (countdown): # Send initial response - embed = discord.Embed(title=":clock3: Reloading Countdown Cache", description="Please wait to continue counting.", color=COLORS["embed"]) + embed = discord.Embed(title=":clock3: Reloading Countdown Cache", description="Please wait to continue counting", color=COLORS["embed"]) msg = await ctx.channel.send(embed=embed) # Reload messages