Skip to content

Commit 053de7f

Browse files
authored
Merge pull request #3 from midirhee12/main
2 parents 8cfa34c + f6ebe17 commit 053de7f

File tree

11 files changed

+237
-41
lines changed

11 files changed

+237
-41
lines changed

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ PROD_COG_IGNORE_LIST="rolecount,mail,git" # Default ignores ATL-specific cogs
4242
# Sentry (Error Tracking)
4343
# SENTRY_URL=""
4444

45+
# Wolfram Alpha (Math/Science Queries)
46+
# MAKE SURE THIS IS FOR THE SIMPLE API OR IT WILL NOT WORK
47+
# WOLFRAM_APP_ID=""
48+
4549
# InfluxDB (Metrics/Logging)
4650
# ------------------
4751

DEVELOPER.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ Explore the following pages for more detailed information on specific developmen
2626
* **[Database Controller Patterns](./docs/content/dev/database_patterns.md)**
2727
* Using controllers for CRUD, transactions, relations.
2828
* Best practices for database interactions in code.
29-
* **[Docker Environment](./docs/content/dev/docker_environment.md)** (Optional)
29+
* **[Docker Environment](./docs/content/dev/docker_development.md)** (Optional)
3030
* Setting up and using the Docker-based development environment.
3131
* Running commands within Docker containers.

tux/cog_loader.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def __init__(self, bot: commands.Bot) -> None:
4444
"utility": 30,
4545
"info": 20,
4646
"fun": 10,
47+
"tools": 5,
4748
}
4849

4950
async def is_cog_eligible(self, filepath: Path) -> bool:

tux/cogs/fun/fact.py

Lines changed: 115 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,145 @@
11
import random
2+
from enum import Enum
23

4+
from discord import app_commands
35
from discord.ext import commands
46

57
from tux.bot import Tux
68
from tux.ui.embeds import EmbedCreator
9+
from tux.utils.config import CONFIG
10+
from tux.utils.functions import docstring_parameter
11+
12+
linux_facts = [
13+
"The Linux kernel has over 36 million lines of code in its Git repository.",
14+
"The vast majority of the world's supercomputers run on Linux.",
15+
"Linux 1.0 was launched on September 17, 1991 featuring 176,250 lines of code.",
16+
"There's an easter egg in `apt` if you enter `apt moo`.",
17+
"Linus Torvalds was around 22 years old when he started work on the Linux Kernel in 1991. In the same year, he also released prototypes of the kernel publicly.",
18+
"Linux's 1.0 release was in March 1994.",
19+
"Less than 1% of the latest kernel release includes code written by Linus Torvalds.",
20+
"Approximately 13.3% of the latest Linux kernel is made up of blank lines.",
21+
"Vim has various easter eggs. A notable one is found by typing :help 42 into the command bar.",
22+
"Slackware is the oldest active linux distribution being released on the 17th July 1993.",
23+
"Freax was the original planned name for Linux.",
24+
"The first GUI that ran on Linux was X Window System. It ran on Linux Kernel version 0.95.",
25+
"The Linux Kernel was the first OS kernel to support x86-64 in 2001.",
26+
"Over 14,600 individual developers from over 1,300 different companies have contributed to the kernel.",
27+
"95% of the Linux kernel is written in the C programming Language. Assembly language is the second most used language for Linux at 2.8%.",
28+
"The first kernel version - Version 0.01 - contained about 10,000 lines of code.",
29+
"96.3% of the top 1,000,000 web servers were reported to run on Linux.",
30+
"In the early 2000s, Steve Jobs, who at the time was the CEO of Apple, offered a job to Linus Torvalds to work on OSX which Torvalds declined.",
31+
"Linus Torvalds said that he would have never created Linux if FreeBSD had been available at the time.",
32+
"Linux is used by every major space programme in the world including NASA, SpaceX, and the European Space Agency.",
33+
]
34+
35+
cat_facts = [
36+
"Unlike dogs, cats do not have a sweet tooth. Scientists believe this is due to a mutation in a key taste receptor.",
37+
"When a cat chases its prey, it keeps its head level. Dogs and humans bob their heads up and down.",
38+
"The technical term for a cat's hairball is a “bezoar.”",
39+
"A group of cats is called a “clowder.”",
40+
"A cat can't climb head first down a tree because every claw on a cat's paw points the same way. To get down from a tree, a cat must back down.",
41+
"Cats make about 100 different sounds. Dogs make only about 10.",
42+
"Every year, nearly four million cats are eaten in Asia.",
43+
"There are more than 500 million domestic cats in the world, with approximately 40 recognized breeds.",
44+
"Approximately 24 cat skins can make a coat.",
45+
"While it is commonly thought that the ancient Egyptians were the first to domesticate cats, the oldest known pet cat was recently found in a 9,500-year-old grave on the Mediterranean island of Cyprus. This grave predates early Egyptian art depicting cats by 4,000 years or more.",
46+
"During the time of the Spanish Inquisition, Pope Innocent VIII condemned cats as evil and thousands of cats were burned. Unfortunately, the widespread killing of cats led to an explosion of the rat population, which exacerbated the effects of the Black Death.",
47+
"During the Middle Ages, cats were associated with witchcraft, and on St. John's Day, people all over Europe would stuff them into sacks and toss the cats into bonfires. On holy days, people celebrated by tossing cats from church towers.",
48+
"The first cat in space was a French cat named Felicette (a.k.a. “Astrocat”) In 1963, France blasted the cat into outer space. Electrodes implanted in her brains sent neurological signals back to Earth. She survived the trip.",
49+
"The group of words associated with cat (catt, cath, chat, katze) stem from the Latin catus, meaning domestic cat, as opposed to feles, or wild cat.",
50+
"The term “puss” is the root of the principal word for “cat” in the Romanian term pisica and the root of secondary words in Lithuanian (puz) and Low German puus. Some scholars suggest that “puss” could be imitative of the hissing sound used to get a cat's attention. As a slang word for the female pudenda, it could be associated with the connotation of a cat being soft, warm, and fuzzy.",
51+
"Approximately 40,000 people are bitten by cats in the U.S. annually.",
52+
"Cats are the world's most popular pets, outnumbering dogs by as many as three to one",
53+
"Cats are North America's most popular pets: there are 73 million cats compared to 63 million dogs. Over 30% of households in North America own a cat.",
54+
"According to Hebrew legend, Noah prayed to God for help protecting all the food he stored on the ark from being eaten by rats. In reply, God made the lion sneeze, and out popped a cat.",
55+
"A cat's hearing is better than a dog's. And a cat can hear high-frequency sounds up to two octaves higher than a human.",
56+
"A cat can travel at a top speed of approximately 31 mph (49 km) over a short distance.",
57+
"A cat rubs against people not only to be affectionate but also to mark out its territory with scent glands around its face. The tail area and paws also carry the cat's scent.",
58+
"Researchers are unsure exactly how a cat purrs. Most veterinarians believe that a cat purrs by vibrating vocal folds deep in the throat. To do this, a muscle in the larynx opens and closes the air passage about 25 times per second.",
59+
"When a family cat died in ancient Egypt, family members would mourn by shaving off their eyebrows. They also held elaborate funerals during which they drank wine and beat their breasts. The cat was embalmed with a sculpted wooden mask and the tiny mummy was placed in the family tomb or in a pet cemetery with tiny mummies of mice.",
60+
"In 1888, more than 300,000 mummified cats were found an Egyptian cemetery. They were stripped of their wrappings and carted off to be used by farmers in England and the U.S. for fertilizer.",
61+
]
62+
63+
tux_facts = [
64+
f"As of 2025, {CONFIG.BOT_NAME} has over 20 thousand lines of code.",
65+
f"The first stable release of {CONFIG.BOT_NAME} was over 1 year after its creation.",
66+
f"{CONFIG.BOT_NAME} is fully open source and available on GitHub (https://github.com/allthingslinux/tux). Unless you forked it and made it private, in which case, please make it public (GPLv3).",
67+
]
68+
69+
70+
class FactType(Enum):
71+
LINUX = "Linux"
72+
TUX = f"{CONFIG.BOT_NAME}"
73+
CAT = "Cat"
74+
RANDOM = "Random"
75+
76+
77+
# dictionary to map fact types to their respective lists
78+
# TODO: add more facts and make some grabbed from apis
79+
"""
80+
https://uselessfacts.jsph.pl/
81+
https://alexwohlbruck.github.io/cat-facts/
82+
https://dukengn.github.io/Dog-facts-API/
83+
https://kinduff.github.io/dog-api/
84+
https://github.com/wh-iterabb-it/meowfacts
85+
https://chandan-02.github.io/anime-facts-rest-api/
86+
"""
87+
fact_type_map = {
88+
FactType.LINUX: linux_facts,
89+
FactType.TUX: tux_facts,
90+
FactType.CAT: cat_facts,
91+
FactType.RANDOM: linux_facts + tux_facts + cat_facts,
92+
}
793

894

995
class Fact(commands.Cog):
1096
def __init__(self, bot: Tux) -> None:
1197
self.bot = bot
12-
self.facts = [
13-
"The Linux kernel has over 36 million lines of code in its Git repository.",
14-
"The vast majority of the world's supercomputers run on Linux.",
15-
"Linux 1.0 was launched on September 17, 1991 featuring 176,250 lines of code.",
16-
"There's an easter egg in `apt` if you enter `apt moo`.",
17-
"Linus Torvalds was around 22 years old when he started work on the Linux Kernel in 1991. In the same year, he also released prototypes of the kernel publicly.",
18-
"Linux's 1.0 release was in March 1994.",
19-
"Less than 1% of the latest kernel release includes code written by Linus Torvalds.",
20-
"Approximately 13.3% of the latest Linux kernel is made up of blank lines.",
21-
"Vim has various easter eggs. A notable one is found by typing :help 42 into the command bar.",
22-
"Slackware is the oldest active linux distribution being released on the 17th July 1993.",
23-
"Freax was the original planned name for Linux.",
24-
"The first GUI that ran on Linux was X Window System. It ran on Linux Kernel version 0.95.",
25-
"The Linux Kernel was the first OS kernel to support x86-64 in 2001.",
26-
"Over 14,600 individual developers from over 1,300 different companies have contributed to the kernel.",
27-
"95% of the Linux kernel is written in the C programming Language. Assembly language is the second most used language for Linux at 2.8%.",
28-
"The first kernel version - Version 0.01 - contained about 10,000 lines of code.",
29-
"96.3% of the top 1,000,000 web servers were reported to run on Linux.",
30-
"In the early 2000s, Steve Jobs, who at the time was the CEO of Apple, offered a job to Linus Torvalds to work on OSX which Torvalds declined.",
31-
"Linus Torvalds said that he would have never created Linux if FreeBSD had been available at the time.",
32-
"Linux is used by every major space programme in the world including NASA, SpaceX, and the European Space Agency.",
33-
]
34-
35-
@commands.hybrid_command(
36-
name="fact",
37-
aliases=["funfact"],
98+
99+
@commands.hybrid_command(name="fact", aliases=["funfact"])
100+
@app_commands.describe(fact_type="Select the category of fact to retrieve")
101+
@app_commands.choices(fact_type=[app_commands.Choice(name=ft.value, value=ft.value) for ft in FactType])
102+
@docstring_parameter(
103+
f"Available categories: {', '.join([ft.value for ft in FactType])}. Default category is {FactType.RANDOM.value}.",
38104
)
39-
async def fact(self, ctx: commands.Context[Tux]) -> None:
105+
async def fact(self, ctx: commands.Context[Tux], fact_type: str = FactType.RANDOM.value) -> None:
40106
"""
41-
Get a random fun fact.
107+
Get a random fun fact from the specified category.
108+
109+
{0}
42110
43111
Parameters
44112
----------
45113
ctx : commands.Context[Tux]
46-
The context object for the command.
114+
The context in which the command is being invoked.
115+
fact_type : str
116+
The category of fact to retrieve. {0}
47117
"""
118+
119+
# Map selected name back to enum
120+
mapping = {ft.value: ft for ft in FactType}
121+
if fact_type not in mapping:
122+
opts = ", ".join(mapping.keys())
123+
await ctx.send(f"Invalid category '{fact_type}'. Available: {opts}.")
124+
return
125+
sel = mapping[fact_type]
126+
# Pick a random fact
127+
facts = fact_type_map.get(sel, [])
128+
description = random.choice(facts) if facts else "No facts available for this category."
129+
48130
embed = EmbedCreator.create_embed(
49131
bot=self.bot,
50132
embed_type=EmbedCreator.INFO,
51133
user_name=ctx.author.name,
52134
user_display_avatar=ctx.author.display_avatar.url,
53-
title="Fun Fact",
54-
description=random.choice(self.facts),
55-
custom_author_text="Click here to submit more facts here!",
135+
title=f"Fun Fact ({sel.value})",
136+
description=description,
137+
custom_author_text="Click here to submit more facts!",
56138
custom_author_text_url="https://github.com/allthingslinux/tux/blob/main/tux/cogs/fun/fact.py",
57139
custom_author_icon_url="https://github.com/allthingslinux/tux/blob/main/assets/emojis/tux_info.png?raw=true",
58140
)
59141

60-
# # set author
61-
# embed.set_author(
62-
# name="Submit more facts here!",
63-
# url="https://github.com/allthingslinux/tux/blob/main/tux/cogs/fun/fact.py",
64-
# icon_url="https://github.com/allthingslinux/tux/blob/main/assets/emojis/tux_info.png?raw=true",
65-
# )
66-
142+
# Send the fact embed
67143
await ctx.send(embed=embed)
68144

69145

tux/cogs/services/influxdblogger.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ async def logger(self) -> None:
6666
guild_id = int(guild.guild_id)
6767

6868
# Collect data by querying controllers
69-
starboard_stats = await self.db.starboard_message.find_many(where={"guild_id": guild_id})
69+
starboard_stats = await self.db.starboard_message.find_many(where={"message_guild_id": guild_id})
7070

7171
snippet_stats = await self.db.snippet.find_many(where={"guild_id": guild_id})
7272

tux/cogs/tools/__init__.py

Whitespace-only changes.

tux/cogs/tools/wolfram.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import asyncio
2+
import io
3+
from urllib.parse import quote_plus
4+
5+
import aiohttp
6+
import discord
7+
from discord import app_commands
8+
from discord.ext import commands
9+
from loguru import logger
10+
from PIL import Image
11+
12+
from tux.bot import Tux
13+
from tux.ui.embeds import EmbedCreator
14+
from tux.utils.config import CONFIG
15+
16+
17+
class Wolfram(commands.Cog):
18+
def __init__(self, bot: Tux) -> None:
19+
self.bot = bot
20+
21+
# Verify AppID configuration; unload cog if missing
22+
if not CONFIG.WOLFRAM_APP_ID:
23+
logger.warning("Wolfram Alpha API ID is not set. Some Science/Math commands will not work.")
24+
# Store the task reference
25+
self._unload_task = asyncio.create_task(self._unload_self())
26+
else:
27+
logger.info("Wolfram Alpha API ID is set, Science/Math commands that depend on it will work.")
28+
29+
async def _unload_self(self):
30+
"""Unload this cog if configuration is missing."""
31+
try:
32+
await self.bot.unload_extension("tux.cogs.tools.wolfram")
33+
logger.info("Wolfram cog has been unloaded due to missing configuration")
34+
except Exception as e:
35+
logger.error(f"Failed to unload Wolfram cog: {e}")
36+
37+
@commands.hybrid_command(name="wolfram", description="Query Wolfram|Alpha Simple API and return an image result.")
38+
@app_commands.describe(
39+
query="The input query for Wolfram|Alpha, e.g. 'integrate x^2' or 'What is the capital of France?'",
40+
)
41+
async def wolfram(self, ctx: commands.Context[Tux], *, query: str) -> None:
42+
"""
43+
Send a query to Wolfram|Alpha Simple API and return the visual result.
44+
45+
Parameters
46+
----------
47+
ctx : commands.Context[Tux]
48+
Invocation context for the command.
49+
query : str
50+
Input string for the Wolfram|Alpha query, e.g. 'integrate x^2'.
51+
"""
52+
53+
await ctx.defer()
54+
55+
# Build the Simple API endpoint URL with URL-encoded query
56+
encoded = quote_plus(query)
57+
url = f"https://api.wolframalpha.com/v1/simple?appid={CONFIG.WOLFRAM_APP_ID}&i={encoded}"
58+
59+
try:
60+
# Perform async HTTP GET with a 10-second timeout
61+
timeout = aiohttp.ClientTimeout(total=10)
62+
async with aiohttp.ClientSession() as session, session.get(url, timeout=timeout) as resp:
63+
resp.raise_for_status()
64+
img_data = await resp.read()
65+
except Exception:
66+
# On error, notify user via an error embed
67+
embed = EmbedCreator.create_embed(
68+
bot=self.bot,
69+
embed_type=EmbedCreator.ERROR,
70+
user_name=ctx.author.name,
71+
user_display_avatar=ctx.author.display_avatar.url,
72+
description="Failed to retrieve image from Wolfram|Alpha Simple API.",
73+
)
74+
await ctx.send(embed=embed)
75+
return
76+
77+
# Crop the top 80 pixels from the fetched image
78+
image = Image.open(io.BytesIO(img_data))
79+
width, height = image.size
80+
cropped = image.crop((0, 80, width, height))
81+
buffer = io.BytesIO()
82+
cropped.save(buffer, format=image.format or "PNG")
83+
buffer.seek(0)
84+
image_file = discord.File(buffer, filename="wolfram.png")
85+
86+
embed = EmbedCreator.create_embed(
87+
bot=self.bot,
88+
embed_type=EmbedCreator.INFO,
89+
user_name=ctx.author.name,
90+
user_display_avatar=ctx.author.display_avatar.url,
91+
title=f"Wolfram|Alpha: {query}",
92+
)
93+
# Display the image via embed attachment URL
94+
embed.set_image(url="attachment://wolfram.png")
95+
await ctx.send(embed=embed, file=image_file)
96+
97+
98+
async def setup(bot: Tux) -> None:
99+
await bot.add_cog(Wolfram(bot))

tux/cogs/utility/poll.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ async def on_reaction_add(self, reaction: discord.Reaction, user: discord.User)
8181
and embed.author.name.startswith("Poll")
8282
and reaction.emoji not in [f"{num + 1}\u20e3" for num in range(9)]
8383
):
84+
await reaction.remove(user)
8485
await reaction.clear()
8586

8687
@app_commands.command(name="poll", description="Creates a poll.")

tux/help.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ async def _create_category_options(self) -> list[discord.SelectOption]:
413413
"levels": "📈",
414414
"services": "🔌",
415415
"guild": "🏰",
416+
"tools": "🛠",
416417
}
417418

418419
options: list[discord.SelectOption] = []

tux/utils/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ def BOT_TOKEN(self) -> str: # noqa: N802
8484
# before this property is accessed.
8585
return get_bot_token() # Get token based on manager's current env
8686

87+
# Wolfram
88+
WOLFRAM_APP_ID: Final[str] = os.getenv("WOLFRAM_APP_ID", "")
89+
8790
# InfluxDB
8891
INFLUXDB_TOKEN: Final[str] = os.getenv("INFLUXDB_TOKEN", "")
8992
INFLUXDB_URL: Final[str] = os.getenv("INFLUXDB_URL", "")

0 commit comments

Comments
 (0)