aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Xithrius <[email protected]>2021-03-10 11:28:57 -0800
committerGravatar GitHub <[email protected]>2021-03-10 11:28:57 -0800
commit6e9a71c23b2cac5e5f6f581c36d284da11702a45 (patch)
treeb9e4cea4aa0a5719c9d8552b74490e8ffbbbde83
parentsuggestion by @Xithrius (diff)
parentMerge pull request #616 from python-discord/aoc-fixes (diff)
Merge branch 'master' into earth_photos
-rw-r--r--bot/constants.py2
-rw-r--r--bot/exts/christmas/advent_of_code/_cog.py7
-rw-r--r--bot/exts/evergreen/emoji.py (renamed from bot/exts/evergreen/emoji_count.py)67
-rw-r--r--bot/exts/evergreen/pythonfacts.py33
-rw-r--r--bot/resources/evergreen/python_facts.txt3
-rw-r--r--bot/resources/evergreen/trivia_quiz.json57
-rw-r--r--bot/utils/time.py84
7 files changed, 205 insertions, 48 deletions
diff --git a/bot/constants.py b/bot/constants.py
index 721defc8..b8e30a7c 100644
--- a/bot/constants.py
+++ b/bot/constants.py
@@ -158,6 +158,8 @@ class Colours:
soft_orange = 0xf9cb54
soft_red = 0xcd6d6d
yellow = 0xf9f586
+ python_blue = 0x4B8BBE
+ python_yellow = 0xFFD43B
grass_green = 0x66ff00
diff --git a/bot/exts/christmas/advent_of_code/_cog.py b/bot/exts/christmas/advent_of_code/_cog.py
index 466edd48..29902306 100644
--- a/bot/exts/christmas/advent_of_code/_cog.py
+++ b/bot/exts/christmas/advent_of_code/_cog.py
@@ -36,9 +36,6 @@ class AdventOfCode(commands.Cog):
self.about_aoc_filepath = Path("./bot/resources/advent_of_code/about.json")
self.cached_about_aoc = self._build_about_embed()
- self.countdown_task = None
- self.status_task = None
-
notification_coro = _helpers.new_puzzle_notification(self.bot)
self.notification_task = self.bot.loop.create_task(notification_coro)
self.notification_task.set_name("Daily AoC Notification")
@@ -173,6 +170,7 @@ class AdventOfCode(commands.Cog):
else:
await ctx.message.add_reaction(Emojis.envelope)
+ @in_month(Month.DECEMBER)
@adventofcode_group.command(
name="leaderboard",
aliases=("board", "lb"),
@@ -198,6 +196,7 @@ class AdventOfCode(commands.Cog):
await ctx.send(content=f"{header}\n\n{table}", embed=info_embed)
+ @in_month(Month.DECEMBER)
@adventofcode_group.command(
name="global",
aliases=("globalboard", "gb"),
@@ -268,7 +267,7 @@ class AdventOfCode(commands.Cog):
def cog_unload(self) -> None:
"""Cancel season-related tasks on cog unload."""
log.debug("Unloading the cog and canceling the background task.")
- self.countdown_task.cancel()
+ self.notification_task.cancel()
self.status_task.cancel()
def _build_about_embed(self) -> discord.Embed:
diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji.py
index cc43e9ab..99f71218 100644
--- a/bot/exts/evergreen/emoji_count.py
+++ b/bot/exts/evergreen/emoji.py
@@ -1,49 +1,51 @@
-import datetime
import logging
import random
+import textwrap
from collections import defaultdict
-from typing import List, Tuple
+from datetime import datetime
+from typing import List, Optional, Tuple
-import discord
+from discord import Color, Embed, Emoji
from discord.ext import commands
from bot.constants import Colours, ERROR_REPLIES
from bot.utils.pagination import LinePaginator
+from bot.utils.time import time_since
log = logging.getLogger(__name__)
-class EmojiCount(commands.Cog):
- """Command that give random emoji based on category."""
+class Emojis(commands.Cog):
+ """A collection of commands related to emojis in the server."""
def __init__(self, bot: commands.Bot):
self.bot = bot
@staticmethod
- def embed_builder(emoji: dict) -> Tuple[discord.Embed, List[str]]:
+ def embed_builder(emoji: dict) -> Tuple[Embed, List[str]]:
"""Generates an embed with the emoji names and count."""
- embed = discord.Embed(
+ embed = Embed(
color=Colours.orange,
title="Emoji Count",
- timestamp=datetime.datetime.utcnow()
+ timestamp=datetime.utcnow()
)
msg = []
if len(emoji) == 1:
for category_name, category_emojis in emoji.items():
if len(category_emojis) == 1:
- msg.append(f"There is **{len(category_emojis)}** emoji in **{category_name}** category")
+ msg.append(f"There is **{len(category_emojis)}** emoji in the **{category_name}** category.")
else:
- msg.append(f"There are **{len(category_emojis)}** emojis in **{category_name}** category")
+ msg.append(f"There are **{len(category_emojis)}** emojis in the **{category_name}** category.")
embed.set_thumbnail(url=random.choice(category_emojis).url)
else:
for category_name, category_emojis in emoji.items():
emoji_choice = random.choice(category_emojis)
if len(category_emojis) > 1:
- emoji_info = f"There are **{len(category_emojis)}** emojis in **{category_name}** category"
+ emoji_info = f"There are **{len(category_emojis)}** emojis in the **{category_name}** category."
else:
- emoji_info = f"There is **{len(category_emojis)}** emoji in **{category_name}** category"
+ emoji_info = f"There is **{len(category_emojis)}** emoji in the **{category_name}** category."
if emoji_choice.animated:
msg.append(f'<a:{emoji_choice.name}:{emoji_choice.id}> {emoji_info}')
else:
@@ -51,9 +53,9 @@ class EmojiCount(commands.Cog):
return embed, msg
@staticmethod
- def generate_invalid_embed(emojis: list) -> Tuple[discord.Embed, List[str]]:
- """Generates error embed."""
- embed = discord.Embed(
+ def generate_invalid_embed(emojis: list) -> Tuple[Embed, List[str]]:
+ """Generates error embed for invalid emoji categories."""
+ embed = Embed(
color=Colours.soft_red,
title=random.choice(ERROR_REPLIES)
)
@@ -64,11 +66,19 @@ class EmojiCount(commands.Cog):
emoji_dict[emoji.name.split("_")[0]].append(emoji)
error_comp = ', '.join(emoji_dict)
- msg.append(f"These are the valid categories\n```{error_comp}```")
+ msg.append(f"These are the valid emoji categories:\n```{error_comp}```")
return embed, msg
- @commands.command(name="emojicount", aliases=["ec", "emojis"])
- async def emoji_count(self, ctx: commands.Context, *, category_query: str = None) -> None:
+ @commands.group(name="emoji", invoke_without_command=True)
+ async def emoji_group(self, ctx: commands.Context, emoji: Optional[Emoji]) -> None:
+ """A group of commands related to emojis."""
+ if emoji is not None:
+ await ctx.invoke(self.info_command, emoji)
+ else:
+ await ctx.send_help(ctx.command)
+
+ @emoji_group.command(name="count", aliases=("c",))
+ async def count_command(self, ctx: commands.Context, *, category_query: str = None) -> None:
"""Returns embed with emoji category and info given by the user."""
emoji_dict = defaultdict(list)
@@ -91,7 +101,24 @@ class EmojiCount(commands.Cog):
embed, msg = self.embed_builder(emoji_dict)
await LinePaginator.paginate(lines=msg, ctx=ctx, embed=embed)
+ @emoji_group.command(name="info", aliases=("i",))
+ async def info_command(self, ctx: commands.Context, emoji: Emoji) -> None:
+ """Returns relevant information about a Discord Emoji."""
+ emoji_information = Embed(
+ title=f"Emoji Information: {emoji.name}",
+ description=textwrap.dedent(f"""
+ **Name:** {emoji.name}
+ **Created:** {time_since(emoji.created_at, precision="hours")}
+ **Date:** {datetime.strftime(emoji.created_at, "%d/%m/%Y")}
+ **ID:** {emoji.id}
+ """),
+ color=Color.blurple(),
+ url=str(emoji.url),
+ ).set_thumbnail(url=emoji.url)
+
+ await ctx.send(embed=emoji_information)
+
def setup(bot: commands.Bot) -> None:
- """Emoji Count Cog load."""
- bot.add_cog(EmojiCount(bot))
+ """Add the Emojis cog into the bot."""
+ bot.add_cog(Emojis(bot))
diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py
new file mode 100644
index 00000000..457c2fd3
--- /dev/null
+++ b/bot/exts/evergreen/pythonfacts.py
@@ -0,0 +1,33 @@
+import itertools
+
+import discord
+from discord.ext import commands
+
+from bot.constants import Colours
+
+with open('bot/resources/evergreen/python_facts.txt') as file:
+ FACTS = itertools.cycle(list(file))
+
+COLORS = itertools.cycle([Colours.python_blue, Colours.python_yellow])
+
+
+class PythonFacts(commands.Cog):
+ """Sends a random fun fact about Python."""
+
+ def __init__(self, bot: commands.Bot) -> None:
+ self.bot = bot
+
+ @commands.command(name='pythonfact', aliases=['pyfact'])
+ async def get_python_fact(self, ctx: commands.Context) -> None:
+ """Sends a Random fun fact about Python."""
+ embed = discord.Embed(title='Python Facts',
+ description=next(FACTS),
+ colour=next(COLORS))
+ embed.add_field(name='Suggestions',
+ value="Suggest more facts [here!](https://github.com/python-discord/meta/discussions/93)")
+ await ctx.send(embed=embed)
+
+
+def setup(bot: commands.Bot) -> None:
+ """Load PythonFacts Cog."""
+ bot.add_cog(PythonFacts(bot))
diff --git a/bot/resources/evergreen/python_facts.txt b/bot/resources/evergreen/python_facts.txt
new file mode 100644
index 00000000..0abd971b
--- /dev/null
+++ b/bot/resources/evergreen/python_facts.txt
@@ -0,0 +1,3 @@
+Python was named after Monty Python, a British Comedy Troupe, which Guido van Rossum likes.
+If you type `import this` in the Python REPL, you'll get a poem about the philosophies about Python. (check it out by doing !zen in <#267659945086812160>)
+If you type `import antigravity` in the Python REPL, you'll be directed to an [xkcd comic](https://xkcd.com/353/) about how easy Python is.
diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json
index faa3bc3b..a4225eb1 100644
--- a/bot/resources/evergreen/trivia_quiz.json
+++ b/bot/resources/evergreen/trivia_quiz.json
@@ -2,36 +2,51 @@
"retro": [
{
"id": 1,
- "hints": ["It is not a mainline Mario Game, although the plumber is present.", "It is not a mainline Zelda Game, although Link is present."],
+ "hints": [
+ "It is not a mainline Mario Game, although the plumber is present.",
+ "It is not a mainline Zelda Game, although Link is present."
+ ],
"question": "What is the best selling game on the Nintendo GameCube?",
"answer": "Super Smash Bros"
},
{
"id": 2,
- "hints": ["It was released before the 90's.", "It was released after 1980."],
+ "hints": [
+ "It was released before the 90's.",
+ "It was released after 1980."
+ ],
"question": "What year was Tetris released?",
"answer": "1984"
},
{
"id": 3,
- "hints": ["The occupation was in construction", "He appeared as this kind of worker in 1981's Donkey Kong"],
+ "hints": [
+ "The occupation was in construction",
+ "He appeared as this kind of worker in 1981's Donkey Kong"
+ ],
"question": "What was Mario's original occupation?",
"answer": "Carpenter"
},
{
"id": 4,
- "hints": ["It was revealed in the Nintendo Character Guide in 1993.", "His last name has to do with eating Mario's enemies."],
+ "hints": [
+ "It was revealed in the Nintendo Character Guide in 1993.",
+ "His last name has to do with eating Mario's enemies."
+ ],
"question": "What is Yoshi's (from Mario Bros.) full name?",
"answer": "Yoshisaur Munchakoopas"
},
{
"id": 5,
- "hints": ["The game was released in 1990.", "It was released on the SNES."],
+ "hints": [
+ "The game was released in 1990.",
+ "It was released on the SNES."
+ ],
"question": "What was the first game Yoshi appeared in?",
"answer": "Super Mario World"
}
],
- "general":[
+ "general": [
{
"id": 100,
"question": "Name \"the land of a thousand lakes\"",
@@ -114,7 +129,7 @@
"id": 113,
"question": "What's the name of the tallest waterfall in the world.",
"answer": "Angel Falls",
- "info": "Angel Falls (Salto Ángel) in Venezuela is the highest waterfall in the world. The falls are 3230 feet in height, with an uninterrupted drop of 2647 feet. Angel Falls is located on a tributary of the Rio Caroni."
+ "info": "Angel Falls (Salto \u00c1ngel) in Venezuela is the highest waterfall in the world. The falls are 3230 feet in height, with an uninterrupted drop of 2647 feet. Angel Falls is located on a tributary of the Rio Caroni."
},
{
"id": 114,
@@ -180,7 +195,7 @@
"id": 124,
"question": "When did the Second World War end?",
"answer": "1945",
- "info": "World War 2 ended with the unconditional surrender of the Axis powers. On 8 May 1945, the Allies accepted Germany's surrender, about a week after Adolf Hitler had committed suicide. VE Day – Victory in Europe celebrates the end of the Second World War on 8 May 1945."
+ "info": "World War 2 ended with the unconditional surrender of the Axis powers. On 8 May 1945, the Allies accepted Germany's surrender, about a week after Adolf Hitler had committed suicide. VE Day \u2013 Victory in Europe celebrates the end of the Second World War on 8 May 1945."
},
{
"id": 125,
@@ -190,72 +205,66 @@
},
{
"id": 126,
- "question": "What's the name of the largest river in the world?",
- "answer": "Nile",
- "info": "The Nile, which is about 6,650 km (4,130 mi) long, is an \"international\" river as its drainage basin covers eleven countries, namely, Tanzania, Uganda, Rwanda, Burundi, the Democratic Republic of the Congo, Kenya, Ethiopia, Eritrea, South Sudan, Republic of the Sudan and Egypt."
- },
- {
- "id": 127,
"question": "Which is the smallest planet in the Solar System?",
"answer": "Mercury",
"info": "Mercury is the smallest planet in our solar system. It's just a little bigger than Earth's moon. It is the closest planet to the sun, but it's actually not the hottest. Venus is hotter."
},
{
- "id": 128,
+ "id": 127,
"question": "What is the smallest country?",
"answer": "Vatican City",
"info": "With an area of 0.17 square miles (0.44 km2) and a population right around 1,000, Vatican City is the smallest country in the world, both in terms of size and population."
},
{
- "id": 129,
+ "id": 128,
"question": "What's the name of the largest bird?",
"answer": "Ostrich",
"info": "The largest living bird, a member of the Struthioniformes, is the ostrich (Struthio camelus), from the plains of Africa and Arabia. A large male ostrich can reach a height of 2.8 metres (9.2 feet) and weigh over 156 kilograms (344 pounds)."
},
{
- "id": 130,
+ "id": 129,
"question": "What does the acronym GPRS stand for?",
"answer": "General Packet Radio Service",
"info": "General Packet Radio Service (GPRS) is a packet-based mobile data service on the global system for mobile communications (GSM) of 3G and 2G cellular communication systems. It is a non-voice, high-speed and useful packet-switching technology intended for GSM networks."
},
{
- "id": 131,
+ "id": 130,
"question": "In what country is the Ebro river located?",
"answer": "Spain",
"info": "The Ebro river is located in Spain. It is 930 kilometers long and it's the second longest river that ends on the Mediterranean Sea."
},
{
- "id": 132,
+ "id": 131,
"question": "What year was the IBM PC model 5150 introduced into the market?",
"answer": "1981",
"info": "The IBM PC was introduced into the market in 1981. It used the Intel 8088, with a clock speed of 4.77 MHz, along with the MDA and CGA as a video card."
},
{
- "id": 133,
+ "id": 132,
"question": "What's the world's largest urban area?",
"answer": "Tokyo",
"info": "Tokyo is the most populated city in the world, with a population of 37 million people. It is located in Japan."
},
{
- "id": 134,
+ "id": 133,
"question": "How many planets are there in the Solar system?",
"answer": "8",
"info": "In the Solar system, there are 8 planets: Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus and Neptune. Pluto isn't considered a planet in the Solar System anymore."
},
{
- "id": 135,
+ "id": 134,
"question": "What is the capital of Iraq?",
"answer": "Baghdad",
"info": "Baghdad is the capital of Iraq. It has a population of 7 million people."
},
{
- "id": 136,
+ "id": 135,
"question": "The United Nations headquarters is located at which city?",
"answer": "New York",
"info": "The United Nations is headquartered in New York City in a complex designed by a board of architects led by Wallace Harrison and built by the architectural firm Harrison & Abramovitz. The complex has served as the official headquarters of the United Nations since its completion in 1951."
},
{
- "id": 137,
+ "id": 136,
"question": "At what year did Christopher Columbus discover America?",
"answer": "1492",
"info": "The explorer Christopher Columbus made four trips across the Atlantic Ocean from Spain: in 1492, 1493, 1498 and 1502. He was determined to find a direct water route west from Europe to Asia, but he never did. Instead, he stumbled upon the Americas"
diff --git a/bot/utils/time.py b/bot/utils/time.py
new file mode 100644
index 00000000..3c57e126
--- /dev/null
+++ b/bot/utils/time.py
@@ -0,0 +1,84 @@
+import datetime
+
+from dateutil.relativedelta import relativedelta
+
+
+# All these functions are from https://github.com/python-discord/bot/blob/master/bot/utils/time.py
+def _stringify_time_unit(value: int, unit: str) -> str:
+ """
+ Returns a string to represent a value and time unit, ensuring that it uses the right plural form of the unit.
+
+ >>> _stringify_time_unit(1, "seconds")
+ "1 second"
+ >>> _stringify_time_unit(24, "hours")
+ "24 hours"
+ >>> _stringify_time_unit(0, "minutes")
+ "less than a minute"
+ """
+ if unit == "seconds" and value == 0:
+ return "0 seconds"
+ elif value == 1:
+ return f"{value} {unit[:-1]}"
+ elif value == 0:
+ return f"less than a {unit[:-1]}"
+ else:
+ return f"{value} {unit}"
+
+
+def humanize_delta(delta: relativedelta, precision: str = "seconds", max_units: int = 6) -> str:
+ """
+ Returns a human-readable version of the relativedelta.
+
+ precision specifies the smallest unit of time to include (e.g. "seconds", "minutes").
+ max_units specifies the maximum number of units of time to include (e.g. 1 may include days but not hours).
+ """
+ if max_units <= 0:
+ raise ValueError("max_units must be positive")
+
+ units = (
+ ("years", delta.years),
+ ("months", delta.months),
+ ("days", delta.days),
+ ("hours", delta.hours),
+ ("minutes", delta.minutes),
+ ("seconds", delta.seconds),
+ )
+
+ # Add the time units that are >0, but stop at accuracy or max_units.
+ time_strings = []
+ unit_count = 0
+ for unit, value in units:
+ if value:
+ time_strings.append(_stringify_time_unit(value, unit))
+ unit_count += 1
+
+ if unit == precision or unit_count >= max_units:
+ break
+
+ # Add the 'and' between the last two units, if necessary
+ if len(time_strings) > 1:
+ time_strings[-1] = f"{time_strings[-2]} and {time_strings[-1]}"
+ del time_strings[-2]
+
+ # If nothing has been found, just make the value 0 precision, e.g. `0 days`.
+ if not time_strings:
+ humanized = _stringify_time_unit(0, precision)
+ else:
+ humanized = ", ".join(time_strings)
+
+ return humanized
+
+
+def time_since(past_datetime: datetime.datetime, precision: str = "seconds", max_units: int = 6) -> str:
+ """
+ Takes a datetime and returns a human-readable string that describes how long ago that datetime was.
+
+ precision specifies the smallest unit of time to include (e.g. "seconds", "minutes").
+ max_units specifies the maximum number of units of time to include (e.g. 1 may include days but not hours).
+ """
+ now = datetime.datetime.utcnow()
+ delta = abs(relativedelta(now, past_datetime))
+
+ humanized = humanize_delta(delta, precision, max_units)
+
+ return f"{humanized} ago"