diff options
42 files changed, 451 insertions, 209 deletions
diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..159e4f4c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +# Exclude everything +* + +# Make exceptions for what's needed +!bot +!Pipfile +!Pipfile.lock +!LICENSE @@ -111,3 +111,4 @@ venv.bak/ # jetbrains .idea/ +.DS_Store
\ No newline at end of file @@ -4,7 +4,7 @@ verify_ssl = true name = "pypi" [packages] -discord-py = {ref = "3f06f24",git = "https://github.com/Rapptz/discord.py",editable = true} +discord-py = {ref = "42a7c4f",git = "https://github.com/Rapptz/discord.py",editable = true} arrow = "*" beautifulsoup4 = "*" aiodns = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 582d2a10..27318993 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "a024c71a482341a28d5107025d16ab28b3c0f049f998c5bc33a2da21ceab4d4e" + "sha256": "d8e3a7f796632bf80bc0c0b8ed2162d3f39aa91e8afa424d3ae85f2210a71923" }, "pipfile-spec": 6, "requires": { @@ -125,7 +125,7 @@ "discord-py": { "editable": true, "git": "https://github.com/Rapptz/discord.py", - "ref": "3f06f247c039a23948e7bb0014ea31db533b4ba2" + "ref": "42a7c4f7e5caa7baf555e86b52249419ce33acfc" }, "fuzzywuzzy": { "hashes": [ @@ -267,29 +267,29 @@ }, "websockets": { "hashes": [ - "sha256:0e2f7d6567838369af074f0ef4d0b802d19fa1fee135d864acc656ceefa33136", - "sha256:2a16dac282b2fdae75178d0ed3d5b9bc3258dabfae50196cbb30578d84b6f6a6", - "sha256:5a1fa6072405648cb5b3688e9ed3b94be683ce4a4e5723e6f5d34859dee495c1", - "sha256:5c1f55a1274df9d6a37553fef8cff2958515438c58920897675c9bc70f5a0538", - "sha256:669d1e46f165e0ad152ed8197f7edead22854a6c90419f544e0f234cc9dac6c4", - "sha256:695e34c4dbea18d09ab2c258994a8bf6a09564e762655408241f6a14592d2908", - "sha256:6b2e03d69afa8d20253455e67b64de1a82ff8612db105113cccec35d3f8429f0", - "sha256:79ca7cdda7ad4e3663ea3c43bfa8637fc5d5604c7737f19a8964781abbd1148d", - "sha256:7fd2dd9a856f72e6ed06f82facfce01d119b88457cd4b47b7ae501e8e11eba9c", - "sha256:82c0354ac39379d836719a77ee360ef865377aa6fdead87909d50248d0f05f4d", - "sha256:8f3b956d11c5b301206382726210dc1d3bee1a9ccf7aadf895aaf31f71c3716c", - "sha256:91ec98640220ae05b34b79ee88abf27f97ef7c61cf525eec57ea8fcea9f7dddb", - "sha256:952be9540d83dba815569d5cb5f31708801e0bbfc3a8c5aef1890b57ed7e58bf", - "sha256:99ac266af38ba1b1fe13975aea01ac0e14bb5f3a3200d2c69f05385768b8568e", - "sha256:9fa122e7adb24232247f8a89f2d9070bf64b7869daf93ac5e19546b409e47e96", - "sha256:a0873eadc4b8ca93e2e848d490809e0123eea154aa44ecd0109c4d0171869584", - "sha256:cb998bd4d93af46b8b49ecf5a72c0a98e5cc6d57fdca6527ba78ad89d6606484", - "sha256:e02e57346f6a68523e3c43bbdf35dde5c440318d1f827208ae455f6a2ace446d", - "sha256:e79a5a896bcee7fff24a788d72e5c69f13e61369d055f28113e71945a7eb1559", - "sha256:ee55eb6bcf23ecc975e6b47c127c201b913598f38b6a300075f84eeef2d3baff", - "sha256:f1414e6cbcea8d22843e7eafdfdfae3dd1aba41d1945f6ca66e4806c07c4f454" - ], - "version": "==6.0" + "sha256:04b42a1b57096ffa5627d6a78ea1ff7fad3bc2c0331ffc17bc32a4024da7fea0", + "sha256:08e3c3e0535befa4f0c4443824496c03ecc25062debbcf895874f8a0b4c97c9f", + "sha256:10d89d4326045bf5e15e83e9867c85d686b612822e4d8f149cf4840aab5f46e0", + "sha256:232fac8a1978fc1dead4b1c2fa27c7756750fb393eb4ac52f6bc87ba7242b2fa", + "sha256:4bf4c8097440eff22bc78ec76fe2a865a6e658b6977a504679aaf08f02c121da", + "sha256:51642ea3a00772d1e48fb0c492f0d3ae3b6474f34d20eca005a83f8c9c06c561", + "sha256:55d86102282a636e195dad68aaaf85b81d0bef449d7e2ef2ff79ac450bb25d53", + "sha256:564d2675682bd497b59907d2205031acbf7d3fadf8c763b689b9ede20300b215", + "sha256:5d13bf5197a92149dc0badcc2b699267ff65a867029f465accfca8abab95f412", + "sha256:5eda665f6789edb9b57b57a159b9c55482cbe5b046d7db458948370554b16439", + "sha256:5edb2524d4032be4564c65dc4f9d01e79fe8fad5f966e5b552f4e5164fef0885", + "sha256:79691794288bc51e2a3b8de2bc0272ca8355d0b8503077ea57c0716e840ebaef", + "sha256:7fcc8681e9981b9b511cdee7c580d5b005f3bb86b65bde2188e04a29f1d63317", + "sha256:8e447e05ec88b1b408a4c9cde85aa6f4b04f06aa874b9f0b8e8319faf51b1fee", + "sha256:90ea6b3e7787620bb295a4ae050d2811c807d65b1486749414f78cfd6fb61489", + "sha256:9e13239952694b8b831088431d15f771beace10edfcf9ef230cefea14f18508f", + "sha256:d40f081187f7b54d7a99d8a5c782eaa4edc335a057aa54c85059272ed826dc09", + "sha256:e1df1a58ed2468c7b7ce9a2f9752a32ad08eac2bcd56318625c3647c2cd2da6f", + "sha256:e98d0cec437097f09c7834a11c69d79fe6241729b23f656cfc227e93294fc242", + "sha256:f8d59627702d2ff27cb495ca1abdea8bd8d581de425c56e93bff6517134e0a9b", + "sha256:fc30cdf2e949a2225b012a7911d1d031df3d23e99b7eda7dfc982dc4a860dae9" + ], + "version": "==7.0" }, "yarl": { "hashes": [ diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f81f1139..c98bc4fc 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,12 +1,5 @@ # https://aka.ms/yaml -variables: - LIBRARY_PATH: /lib:/usr/lib - PIPENV_HIDE_EMOJIS: 1 - PIPENV_IGNORE_VIRTUALENVS: 1 - PIPENV_NOSPIN: 1 - PIPENV_VENV_IN_PROJECT: 1 - jobs: - job: test displayName: 'Lint & Test' @@ -15,37 +8,34 @@ jobs: vmImage: 'Ubuntu 16.04' variables: - PIPENV_CACHE_DIR: ".cache/pipenv" PIP_CACHE_DIR: ".cache/pip" PIP_SRC: ".cache/src" + PIPENV_CACHE_DIR: ".cache/pipenv" + PIPENV_DONT_USE_PYENV: 1 + PIPENV_HIDE_EMOJIS: 1 + PIPENV_IGNORE_VIRTUALENVS: 1 + PIPENV_NOSPIN: 1 steps: - - script: sudo apt-get install build-essential curl docker libffi-dev libfreetype6-dev libxml2 libxml2-dev libxslt1-dev zlib1g zlib1g-dev - displayName: 'Install base dependencies' - - task: UsePythonVersion@0 displayName: 'Set Python version' inputs: versionSpec: '3.7.x' addToPath: true - - script: sudo pip install pipenv + - script: pip3 install pipenv displayName: 'Install pipenv' - script: pipenv install --dev --deploy --system displayName: 'Install project using pipenv' - - script: python -m flake8 + - script: python3 -m flake8 displayName: 'Run linter' - job: build displayName: 'Build Containers' dependsOn: 'test' - variables: - PIPENV_CACHE_DIR: ".cache/pipenv" - PIP_CACHE_DIR: ".cache/pip" - steps: - task: Docker@1 displayName: 'Login: Docker Hub' @@ -55,12 +45,6 @@ jobs: dockerRegistryEndpoint: 'DockerHub' command: 'login' - - script: sudo apt-get install python3-setuptools - displayName: 'Install setuptools' - - - script: sudo pip3 install salt-pepper - displayName: 'Install pepper' - - task: ShellScript@2 displayName: 'Build and deploy containers' diff --git a/bot/__init__.py b/bot/__init__.py index dc97df3d..54b242ee 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -1,3 +1,4 @@ +import logging import logging.handlers import os from pathlib import Path @@ -6,25 +7,44 @@ import arrow from bot.constants import Client -# start datetime + +# Configure the "TRACE" logging level (e.g. "log.trace(message)") +logging.TRACE = 5 +logging.addLevelName(logging.TRACE, "TRACE") + + +def monkeypatch_trace(self, msg, *args, **kwargs): + """ + Log 'msg % args' with severity 'TRACE'. + To pass exception information, use the keyword argument exc_info with + a true value, e.g. + logger.trace("Houston, we have an %s", "interesting problem", exc_info=1) + """ + if self.isEnabledFor(logging.TRACE): + self._log(logging.TRACE, msg, args, **kwargs) + + +logging.Logger.trace = monkeypatch_trace + +# Set timestamp of when execution started (approximately) start_time = arrow.utcnow() -# set up logging +# Set up file logging log_dir = Path("bot", "log") log_file = log_dir / "hackbot.log" os.makedirs(log_dir, exist_ok=True) -# file handler sets up rotating logs every 5 MB +# File handler rotates logs every 5 MB file_handler = logging.handlers.RotatingFileHandler( log_file, maxBytes=5*(2**20), backupCount=10) -file_handler.setLevel(logging.DEBUG) +file_handler.setLevel(logging.TRACE if Client.debug else logging.DEBUG) -# console handler prints to terminal +# Console handler prints to terminal console_handler = logging.StreamHandler() -level = logging.DEBUG if Client.debug else logging.INFO +level = logging.TRACE if Client.debug else logging.INFO console_handler.setLevel(level) -# remove old loggers if any +# Remove old loggers, if any root = logging.getLogger() if root.handlers: for handler in root.handlers: @@ -34,11 +54,11 @@ if root.handlers: logging.getLogger("discord").setLevel(logging.ERROR) logging.getLogger("websockets").setLevel(logging.ERROR) -# setup new logging configuration +# Setup new logging configuration logging.basicConfig( format='%(asctime)s - %(name)s %(levelname)s: %(message)s', datefmt="%D %H:%M:%S", - level=logging.DEBUG, + level=logging.TRACE if Client.debug else logging.DEBUG, handlers=[console_handler, file_handler] ) logging.getLogger().info('Logging initialization complete') @@ -5,7 +5,6 @@ from typing import List from aiohttp import AsyncResolver, ClientSession, TCPConnector from discord import Embed -from discord.ext import commands from discord.ext.commands import Bot from bot import constants @@ -19,10 +18,7 @@ class SeasonalBot(Bot): def __init__(self, **kwargs): super().__init__(**kwargs) self.http_session = ClientSession( - connector=TCPConnector( - resolver=AsyncResolver(), - family=socket.AF_INET, - ) + connector=TCPConnector(resolver=AsyncResolver(), family=socket.AF_INET) ) def load_extensions(self, exts: List[str]): @@ -49,6 +45,7 @@ class SeasonalBot(Bot): """ Send an embed message to the devlog channel """ + devlog = self.get_channel(constants.Channels.devlog) if not devlog: @@ -62,10 +59,3 @@ class SeasonalBot(Bot): embed.set_author(name=title, icon_url=icon) await devlog.send(embed=embed) - - async def on_command_error(self, context, exception): - # Don't punish the user for getting the arguments wrong - if isinstance(exception, commands.UserInputError): - context.command.reset_cooldown(context) - else: - await super().on_command_error(context, exception) diff --git a/bot/resources/evergreen/magic8ball.json b/bot/resources/evergreen/magic8ball.json new file mode 100644 index 00000000..6fe86950 --- /dev/null +++ b/bot/resources/evergreen/magic8ball.json @@ -0,0 +1,22 @@ +[ + "It is certain", + "It is decidedly so", + "Without a doubt", + "Yes definitely", + "You may rely on it", + "As I see it, yes", + "Most likely", + "Outlook good", + "Yes", + "Signs point to yes", + "Reply hazy try again", + "Ask again later", + "Better not tell you now", + "Cannot predict now", + "Concentrate and ask again", + "Don't count on it", + "My reply is no", + "My sources say no", + "Outlook not so good", + "Very doubtful" +]
\ No newline at end of file diff --git a/bot/resources/snakes/snake_facts.json b/bot/resources/snakes/snake_facts.json index 49b5a80e..ca9ba769 100644 --- a/bot/resources/snakes/snake_facts.json +++ b/bot/resources/snakes/snake_facts.json @@ -3,10 +3,10 @@ "fact": "The decapitated head of a dead snake can still bite, even hours after death. These types of bites usually contain huge amounts of venom." }, { - "fact": "What is considered the most “dangerous” snake depends on both a specific country’s health care and the availability of antivenom following a bite. Based on these criteria, the most dangerous snake in the world is the saw-scaled viper, which bites and kills more people each year than any other snake." + "fact": "What is considered the most 'dangerous' snake depends on both a specific country’s health care and the availability of antivenom following a bite. Based on these criteria, the most dangerous snake in the world is the saw-scaled viper, which bites and kills more people each year than any other snake." }, { - "fact": "Snakes live on everywhere on Earth except Ireland, Iceland, New Zealand, and the North and South Poles.a" + "fact": "Snakes live everywhere on Earth except Ireland, Iceland, New Zealand, and the North and South Poles." }, { "fact": "Of the approximately 725 species of venomous snakes worldwide, 250 can kill a human with one bite." @@ -54,7 +54,7 @@ "fact": "Some snakes have been known to explode after eating a large meal. For example, a 13-foot python blew up after it tried to eat a 6-foot alligator. The python was found with the alligator’s tail protruding from its midsection. Its head was missing." }, { - "fact": "The word “snake” is from the Proto-Indo-European root *sneg -, meaning “to crawl, creeping thing.” The word “serpent” is from the Proto-Indo-European root *serp -, meaning “to crawl, creep.”" + "fact": "The word 'snake' is from the Proto-Indo-European root *sneg -, meaning 'to crawl, creeping thing.' The word 'serpent' is from the Proto-Indo-European root *serp -, meaning 'to crawl, creep.'" }, { "fact": "Rattlesnake rattles are made of rings of keratin, which is the same material as human hair and fingernails. A rattler will add a new ring each time it sheds its skin." @@ -84,7 +84,7 @@ "fact": "A snake’s fangs usually last about 6–10 weeks. When a fang wears out, a new one grows in its place." }, { - "fact": "Because the end of a snake’s tongue is forked, the two tips taste different amounts of chemicals. Essentially, a snake “smells in stereo” and can even tell which direction a smell is coming from. It identifies scents on its tongue using pits in the roof of its mouth called the Jacobson’s organ." + "fact": "Because the end of a snake’s tongue is forked, the two tips taste different amounts of chemicals. Essentially, a snake 'smells in stereo' and can even tell which direction a smell is coming from. It identifies scents on its tongue using pits in the roof of its mouth called the Jacobson’s organ." }, { "fact": "The amount of food a snake eats determines how many offspring it will have. The Arafura file snake eats the least and lays just one egg every decade." @@ -141,7 +141,7 @@ "fact": "Elephant trunk snakes are almost completely aquatic. They cannot slither because they lack the broad scales in the belly that help other snakes move on land. Rather, elephant trunk snakes have large knobby scales to hold onto slippery fish and constrict them underwater." }, { - "fact": "The shortest known snake is the thread snake. It is about 4 inches long and lives on the island of Barbados in the Caribbean. It is said to be as “thin as spaghetti” and it feeds primarily on termites and larvae." + "fact": "The shortest known snake is the thread snake. It is about 4 inches long and lives on the island of Barbados in the Caribbean. It is said to be as 'thin as spaghetti' and it feeds primarily on termites and larvae." }, { "fact": "In 2009, a farm worker in East Africa survived an epic 3-hour battle with a 12-foot python after accidentally stepping on the large snake. It coiled around the man and carried him into a tree. The man wrapped his shirt over the snake’s mouth to prevent it from swallowing him, and he was finally rescued by police after calling for help on his cell phone." @@ -150,7 +150,7 @@ "fact": "The venom from a Brazilian pit viper is used in a drug to treat high blood pressure." }, { - "fact": "The word “cobra” means “hooded.” Some cobras have large spots on the back of their hood that look like eyes to make them appear intimating even from behind." + "fact": "The word 'cobra' means 'hooded.' Some cobras have large spots on the back of their hood that look like eyes to make them appear intimating even from behind." }, { "fact": "Some desert snakes, such as the African rock python, sleep during the hottest parts of the desert summer. This summer sleep is similar to hibernation and is called “aestivation.”" @@ -201,7 +201,7 @@ "fact": "In the United States, fewer than 1 in 37,500 people are bitten by venomous snakes each year (7,000–8,000 bites per year), and only 1 in 50 million people will die from snake bite (5–6 fatalities per year). In the U.S., a person is 9 times more likely to die from being struck by lightening than to die from a venomous snakebite." }, { - "fact": "Some members of the U.S. Army Special Forces are taught to kill and eat snakes during their survival training, which has earned them the nickname “Snake Eaters.”" + "fact": "Some members of the U.S. Army Special Forces are taught to kill and eat snakes during their survival training, which has earned them the nickname 'Snake Eaters.'" }, { "fact": "One of the great feats of the legendary Greek hero Perseus was to kill Medusa, a female monster whose hair consisted of writhing, venomous snakes." @@ -216,7 +216,7 @@ "fact": "The snake has held various meanings throughout history. For example, The Egyptians viewed the snake as representing royalty and deity. In the Jewish rabbinical tradition and in Hinduism, it represents sexual passion and desire. And the Romans interpreted the snake as a symbol of eternal love." }, { - "fact": "Anacondas mate in a huge “breeding ball.” The ball consists of 1 female and nearly 12 males. They stay in a “mating ball” for up to a month." + "fact": "Anacondas mate in a huge 'breeding ball.' The ball consists of 1 female and nearly 12 males. They stay in a 'mating ball' for up to a month." }, { "fact": "Depending on the species, snakes can live from 4 to over 25 years." @@ -228,6 +228,6 @@ "fact": "Endangered snakes include the San Francisco garter snake, eastern indigo snake, the king cobra, and Dumeril’s boa." }, { - "fact": "A mysterious, new “mad snake disease” causes captive pythons and boas to tie themselves in knots. Other symptoms include “stargazing,” which is when snakes stare upwards for long periods of time. Snake experts believe a rodent virus causes the fatal disease." + "fact": "A mysterious, new 'mad snake disease' causes captive pythons and boas to tie themselves in knots. Other symptoms include 'stargazing,' which is when snakes stare upwards for long periods of time. Snake experts believe a rodent virus causes the fatal disease." } -]
\ No newline at end of file +] diff --git a/bot/resources/valentines/pickup_lines.json b/bot/resources/valentines/pickup_lines.json new file mode 100644 index 00000000..a18d0840 --- /dev/null +++ b/bot/resources/valentines/pickup_lines.json @@ -0,0 +1,97 @@ +{ + "placeholder": "https://i.imgur.com/BB52sxj.jpg", + "lines": [ + { + "line": "Hey baby are you allergic to dairy cause I **laktose** clothes you're wearing", + "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8f/Cheese_%281105942243%29.jpg/800px-Cheese_%281105942243%29.jpg" + }, + { + "line": "I’m not a photographer, but I can **picture** me and you together.", + "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/57/2016_Minolta_Dynax_404si.JPG/220px-2016_Minolta_Dynax_404si.JPG" + }, + { + "line": "I seem to have lost my phone number. Can I have yours?" + }, + { + "line": "Are you French? Because **Eiffel** for you.", + "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/85/Tour_Eiffel_Wikimedia_Commons_%28cropped%29.jpg/240px-Tour_Eiffel_Wikimedia_Commons_%28cropped%29.jpg" + }, + { + "line": "Hey babe are you a cat? Because I'm **feline** a connection between us.", + "image": "https://upload.wikimedia.org/wikipedia/commons/4/4d/Cat_November_2010-1a.jpg" + }, + { + "line": "Baby, life without you is like a broken pencil... **pointless**.", + "image": "https://upload.wikimedia.org/wikipedia/commons/0/08/Pencils_hb.jpg" + }, + { + "line": "Babe did it hurt when you fell from heaven?" + }, + { + "line": "If I could rearrange the alphabet, I would put **U** and **I** together.", + "image": "https://images-na.ssl-images-amazon.com/images/I/51wJaFX%2BnGL._SX425_.jpg" + }, + { + "line": "Is your name Google? Because you're everything I'm searching for.", + "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/1024px-Google_%22G%22_Logo.svg.png" + }, + { + "line": "Are you from Starbucks? Because I like you a **latte**.", + "image": "https://upload.wikimedia.org/wikipedia/en/thumb/d/d3/Starbucks_Corporation_Logo_2011.svg/1200px-Starbucks_Corporation_Logo_2011.svg.png" + }, + { + "line": "Are you a banana? Because I find you **a peeling**.", + "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/44/Bananas_white_background_DS.jpg/220px-Bananas_white_background_DS.jpg" + }, + { + "line": "Do you like vegetables because I love you from my head **tomatoes**.", + "image": "https://vignette.wikia.nocookie.net/veggietales-the-ultimate-veggiepedia/images/e/ec/Bobprofile.jpg/revision/latest?cb=20161227190344" + }, + { + "line": "Do you like science because I've got my **ion** you.", + "image": "https://www.chromacademy.com/lms/sco101/assets/c1_010_equations.jpg" + }, + { + "line": "Are you an angle? Because you are **acute**.", + "image": "https://juicebubble.co.za/wp-content/uploads/2018/03/acute-angle-white-400x400.png" + }, + { + "line": "If you were a fruit, you'd be a **fineapple**." + }, + { + "line": "Did you swallow magnets? Cause you're **attractive**.", + "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Magnetic_quadrupole_moment.svg/1200px-Magnetic_quadrupole_moment.svg.png" + }, + { + "line": "Hey pretty thang, do you have a name or can I call you mine?" + }, + { + "line": "Is your name Wi-Fi? Because I'm feeling a connection.", + "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/ae/WiFi_Logo.svg/1200px-WiFi_Logo.svg.png" + }, + { + "line": "Are you Australian? Because you meet all of my **koala**fications.", + "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/49/Koala_climbing_tree.jpg/240px-Koala_climbing_tree.jpg" + }, + { + "line": "If I were a cat I'd spend all 9 lives with you.", + "image": "https://upload.wikimedia.org/wikipedia/commons/4/4d/Cat_November_2010-1a.jpg" + }, + { + "line": "My love for you is like dividing by 0. It's undefinable.", + "image": "https://i.pinimg.com/originals/05/f5/9a/05f59a9fa44689e3435b5e46937544bb.png" + }, + { + "line": "Take away gravity, I'll still fall for you.", + "image": "https://i.pinimg.com/originals/05/f5/9a/05f59a9fa44689e3435b5e46937544bb.png" + }, + { + "line": "Are you a criminal? Because you just stole my heart.", + "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/98/Hinged_Handcuffs_Rear_Back_To_Back.JPG/174px-Hinged_Handcuffs_Rear_Back_To_Back.JPG" + }, + { + "line": "Hey babe I'm here. What were your other two wishes?", + "image": "https://upload.wikimedia.org/wikipedia/en/thumb/0/0c/The_Genie_Aladdin.png/220px-The_Genie_Aladdin.png" + } + ] +}
\ No newline at end of file diff --git a/bot/seasons/__init__.py b/bot/seasons/__init__.py index c43334a4..1512fae2 100644 --- a/bot/seasons/__init__.py +++ b/bot/seasons/__init__.py @@ -9,4 +9,4 @@ log = logging.getLogger(__name__) def setup(bot): bot.add_cog(SeasonManager(bot)) - log.debug("SeasonManager cog loaded") + log.info("SeasonManager cog loaded") diff --git a/bot/seasons/christmas/adventofcode.py b/bot/seasons/christmas/adventofcode.py index a926a6cb..2995c3fd 100644 --- a/bot/seasons/christmas/adventofcode.py +++ b/bot/seasons/christmas/adventofcode.py @@ -108,7 +108,7 @@ async def day_countdown(bot: commands.Bot): await asyncio.sleep(120) -class AdventOfCode: +class AdventOfCode(commands.Cog): """ Advent of Code festivities! Ho Ho Ho! """ @@ -730,4 +730,4 @@ def _error_embed_helper(title: str, description: str) -> discord.Embed: def setup(bot: commands.Bot) -> None: bot.add_cog(AdventOfCode(bot)) - log.info("Cog loaded: adventofcode") + log.info("AdventOfCode cog loaded") diff --git a/bot/seasons/easter/__init__.py b/bot/seasons/easter/__init__.py new file mode 100644 index 00000000..bfad772d --- /dev/null +++ b/bot/seasons/easter/__init__.py @@ -0,0 +1,17 @@ +from bot.seasons import SeasonBase + + +class Easter(SeasonBase): + """ + Easter is a beautiful time of the year often celebrated after the first Full Moon of the new spring season. + This time is quite beautiful due to the colorful flowers coming out to greet us. So. let's greet Spring + in an Easter celebration of contributions. + """ + + name = "easter" + bot_name = "BunnyBot" + greeting = "Happy Easter to us all!" + + # Duration of season + start_date = "01/04" + end_date = "30/04" diff --git a/bot/seasons/evergreen/error_handler.py b/bot/seasons/evergreen/error_handler.py index 47e18a31..7774f06e 100644 --- a/bot/seasons/evergreen/error_handler.py +++ b/bot/seasons/evergreen/error_handler.py @@ -8,105 +8,97 @@ from discord.ext import commands log = logging.getLogger(__name__)
-class CommandErrorHandler:
+class CommandErrorHandler(commands.Cog):
"""A error handler for the PythonDiscord server!"""
def __init__(self, bot):
self.bot = bot
+ @staticmethod
+ def revert_cooldown_counter(command, message):
+ """Undoes the last cooldown counter for user-error cases."""
+ if command._buckets.valid:
+ bucket = command._buckets.get_bucket(message)
+ bucket._tokens = min(bucket.rate, bucket._tokens + 1)
+ logging.debug(
+ "Cooldown counter reverted as the command was not used correctly."
+ )
+
+ @commands.Cog.listener()
async def on_command_error(self, ctx, error):
"""Activates when a command opens an error"""
if hasattr(ctx.command, 'on_error'):
return logging.debug(
- "A command error occured but "
- "the command had it's own error handler"
+ "A command error occured but the command had it's own error handler."
)
+
error = getattr(error, 'original', error)
+
if isinstance(error, commands.CommandNotFound):
return logging.debug(
- f"{ctx.author} called '{ctx.message.content}' "
- "but no command was found"
+ f"{ctx.author} called '{ctx.message.content}' but no command was found."
)
+
if isinstance(error, commands.UserInputError):
logging.debug(
- f"{ctx.author} called the command '{ctx.command}' "
- "but entered invalid input!"
+ f"{ctx.author} called the command '{ctx.command}' but entered invalid input!"
)
+
+ self.revert_cooldown_counter(ctx.command, ctx.message)
+
return await ctx.send(
- ":no_entry: The command you specified failed to run."
+ ":no_entry: The command you specified failed to run. "
"This is because the arguments you provided were invalid."
)
+
if isinstance(error, commands.CommandOnCooldown):
logging.debug(
- f"{ctx.author} called the command '{ctx.command}' "
- "but they were on cooldown!"
+ f"{ctx.author} called the command '{ctx.command}' but they were on cooldown!"
)
- seconds = error.retry_after
- remaining_minutes, remaining_seconds = divmod(seconds, 60)
- time_remaining = f'{int(remaining_minutes)} minutes {math.ceil(remaining_seconds)} seconds'
+ remaining_minutes, remaining_seconds = divmod(error.retry_after, 60)
+
return await ctx.send(
- "This command is on cooldown,"
- f" please retry in {time_remaining}."
+ "This command is on cooldown, please retry in "
+ f"{int(remaining_minutes)} minutes {math.ceil(remaining_seconds)} seconds."
)
+
if isinstance(error, commands.DisabledCommand):
logging.debug(
- f"{ctx.author} called the command '{ctx.command}' "
- "but the command was disabled!"
- )
- return await ctx.send(
- ":no_entry: This command has been disabled."
+ f"{ctx.author} called the command '{ctx.command}' but the command was disabled!"
)
+ return await ctx.send(":no_entry: This command has been disabled.")
+
if isinstance(error, commands.NoPrivateMessage):
logging.debug(
f"{ctx.author} called the command '{ctx.command}' "
"in a private message however the command was guild only!"
)
- return await ctx.author.send(
- ":no_entry: This command can only be used inside a server."
- )
+ return await ctx.author.send(":no_entry: This command can only be used in the server.")
+
if isinstance(error, commands.BadArgument):
- if ctx.command.qualified_name == 'tag list':
- logging.debug(
- f"{ctx.author} called the command '{ctx.command}' "
- "but entered an invalid user!"
- )
- return await ctx.send(
- "I could not find that member. Please try again."
- )
- else:
- logging.debug(
- f"{ctx.author} called the command '{ctx.command}' "
- "but entered a bad argument!"
- )
- return await ctx.send(
- "The argument you provided was invalid."
- )
- if isinstance(error, commands.CheckFailure):
+ self.revert_cooldown_counter(ctx.command, ctx.message)
+
logging.debug(
- f"{ctx.author} called the command '{ctx.command}' "
- "but the checks failed!"
- )
- return await ctx.send(
- ":no_entry: You are not authorized to use this command."
+ f"{ctx.author} called the command '{ctx.command}' but entered a bad argument!"
)
- print(
- f"Ignoring exception in command {ctx.command}:",
- file=sys.stderr
- )
+ return await ctx.send("The argument you provided was invalid.")
+
+ if isinstance(error, commands.CheckFailure):
+ logging.debug(f"{ctx.author} called the command '{ctx.command}' but the checks failed!")
+ return await ctx.send(":no_entry: You are not authorized to use this command.")
+
+ print(f"Ignoring exception in command {ctx.command}:", file=sys.stderr)
+
logging.warning(
f"{ctx.author} called the command '{ctx.command}' "
"however the command failed to run with the error:"
f"-------------\n{error}"
)
- traceback.print_exception(
- type(error),
- error,
- error.__traceback__,
- file=sys.stderr
- )
+
+ traceback.print_exception(type(error), error, error.__traceback__, file=sys.stderr)
def setup(bot):
bot.add_cog(CommandErrorHandler(bot))
- log.debug("CommandErrorHandler cog loaded")
+ log.info("CommandErrorHandler cog loaded")
diff --git a/bot/seasons/evergreen/fun.py b/bot/seasons/evergreen/fun.py index 4da01dd1..9ef47331 100644 --- a/bot/seasons/evergreen/fun.py +++ b/bot/seasons/evergreen/fun.py @@ -8,7 +8,7 @@ from bot.constants import Emojis log = logging.getLogger(__name__) -class Fun: +class Fun(commands.Cog): """ A collection of general commands for fun. """ @@ -35,4 +35,4 @@ class Fun: # Required in order to load the cog, use the class name in the add_cog function. def setup(bot): bot.add_cog(Fun(bot)) - log.debug("Fun cog loaded") + log.info("Fun cog loaded") diff --git a/bot/seasons/evergreen/magic_8ball.py b/bot/seasons/evergreen/magic_8ball.py new file mode 100644 index 00000000..88c9fd26 --- /dev/null +++ b/bot/seasons/evergreen/magic_8ball.py @@ -0,0 +1,36 @@ +import json +import logging +import random +from pathlib import Path + +from discord.ext import commands + +log = logging.getLogger(__name__) + + +class Magic8ball: + """ + A Magic 8ball command to respond to a users question. + """ + + def __init__(self, bot): + self.bot = bot + with open(Path("bot", "resources", "evergreen", "magic8ball.json"), "r") as file: + self.answers = json.load(file) + + @commands.command(name="8ball") + async def output_answer(self, ctx, *, question): + """ + Return a magic 8 ball answer from answers list. + """ + if len(question.split()) >= 3: + answer = random.choice(self.answers) + await ctx.send(answer) + else: + await ctx.send("Usage: .8ball <question> (minimum length of 3 eg: `will I win?`)") + + +# Required in order to load the cog, use the class name in the add_cog function. +def setup(bot): + bot.add_cog(Magic8ball(bot)) + log.info("Magic 8ball cog loaded") diff --git a/bot/seasons/evergreen/snakes/__init__.py b/bot/seasons/evergreen/snakes/__init__.py index 367aea4d..6fb1f673 100644 --- a/bot/seasons/evergreen/snakes/__init__.py +++ b/bot/seasons/evergreen/snakes/__init__.py @@ -7,4 +7,4 @@ log = logging.getLogger(__name__) def setup(bot): bot.add_cog(Snakes(bot)) - log.info("Cog loaded: Snakes") + log.info("Snakes cog loaded") diff --git a/bot/seasons/evergreen/snakes/snakes_cog.py b/bot/seasons/evergreen/snakes/snakes_cog.py index 57eb7a52..74d2ab4f 100644 --- a/bot/seasons/evergreen/snakes/snakes_cog.py +++ b/bot/seasons/evergreen/snakes/snakes_cog.py @@ -15,7 +15,7 @@ import aiohttp import async_timeout from PIL import Image, ImageDraw, ImageFont from discord import Colour, Embed, File, Member, Message, Reaction -from discord.ext.commands import BadArgument, Bot, Context, bot_has_permissions, group +from discord.ext.commands import BadArgument, Bot, Cog, Context, bot_has_permissions, group from bot.constants import ERROR_REPLIES, Tokens from bot.decorators import locked @@ -132,7 +132,7 @@ CARD = { # endregion -class Snakes: +class Snakes(Cog): """ Commands related to snakes. These were created by our community during the first code jam. @@ -1187,8 +1187,3 @@ class Snakes: await ctx.send(embed=embed) # endregion - - -def setup(bot): - bot.add_cog(Snakes(bot)) - log.info("Cog loaded: Snakes") diff --git a/bot/seasons/evergreen/snakes/utils.py b/bot/seasons/evergreen/snakes/utils.py index 605c7ef3..ec280223 100644 --- a/bot/seasons/evergreen/snakes/utils.py +++ b/bot/seasons/evergreen/snakes/utils.py @@ -117,7 +117,7 @@ ANGLE_RANGE = math.pi * 2 def get_resource(file: str) -> List[dict]: - with (SNAKE_RESOURCES / f"{file}.json").open() as snakefile: + with (SNAKE_RESOURCES / f"{file}.json").open(encoding="utf-8") as snakefile: return json.load(snakefile) diff --git a/bot/seasons/evergreen/uptime.py b/bot/seasons/evergreen/uptime.py index 1321da19..3d2c7d03 100644 --- a/bot/seasons/evergreen/uptime.py +++ b/bot/seasons/evergreen/uptime.py @@ -9,7 +9,7 @@ from bot import start_time log = logging.getLogger(__name__) -class Uptime: +class Uptime(commands.Cog): """ A cog for posting the bots uptime. """ @@ -35,4 +35,4 @@ class Uptime: # Required in order to load the cog, use the class name in the add_cog function. def setup(bot): bot.add_cog(Uptime(bot)) - log.debug("Uptime cog loaded") + log.info("Uptime cog loaded") diff --git a/bot/seasons/halloween/candy_collection.py b/bot/seasons/halloween/candy_collection.py index 80f30a1b..6932097c 100644 --- a/bot/seasons/halloween/candy_collection.py +++ b/bot/seasons/halloween/candy_collection.py @@ -20,7 +20,7 @@ ADD_SKULL_REACTION_CHANCE = 50 # 2% ADD_SKULL_EXISTING_REACTION_CHANCE = 20 # 5% -class CandyCollection: +class CandyCollection(commands.Cog): def __init__(self, bot): self.bot = bot with open(json_location) as candy: @@ -31,6 +31,7 @@ class CandyCollection: userid = userinfo['userid'] self.get_candyinfo[userid] = userinfo + @commands.Cog.listener() async def on_message(self, message): """ Randomly adds candy or skull to certain messages @@ -54,6 +55,7 @@ class CandyCollection: self.msg_reacted.append(d) return await message.add_reaction('\N{CANDY}') + @commands.Cog.listener() async def on_reaction_add(self, reaction, user): """ Add/remove candies from a person if the reaction satisfies criteria @@ -231,4 +233,4 @@ class CandyCollection: def setup(bot): bot.add_cog(CandyCollection(bot)) - log.debug("CandyCollection cog loaded") + log.info("CandyCollection cog loaded") diff --git a/bot/seasons/halloween/hacktoberstats.py b/bot/seasons/halloween/hacktoberstats.py index 41cf10ee..81f11455 100644 --- a/bot/seasons/halloween/hacktoberstats.py +++ b/bot/seasons/halloween/hacktoberstats.py @@ -13,7 +13,7 @@ from discord.ext import commands log = logging.getLogger(__name__) -class HacktoberStats: +class HacktoberStats(commands.Cog): def __init__(self, bot): self.bot = bot self.link_json = Path("bot", "resources", "github_links.json") @@ -332,4 +332,4 @@ class HacktoberStats: def setup(bot): bot.add_cog(HacktoberStats(bot)) - log.debug("HacktoberStats cog loaded") + log.info("HacktoberStats cog loaded") diff --git a/bot/seasons/halloween/halloween_facts.py b/bot/seasons/halloween/halloween_facts.py index 098ee432..9224cc57 100644 --- a/bot/seasons/halloween/halloween_facts.py +++ b/bot/seasons/halloween/halloween_facts.py @@ -25,7 +25,7 @@ PUMPKIN_ORANGE = discord.Color(0xFF7518) INTERVAL = timedelta(hours=6).total_seconds() -class HalloweenFacts: +class HalloweenFacts(commands.Cog): def __init__(self, bot): self.bot = bot @@ -35,6 +35,7 @@ class HalloweenFacts: self.facts = list(enumerate(self.halloween_facts)) random.shuffle(self.facts) + @commands.Cog.listener() async def on_ready(self): self.channel = self.bot.get_channel(Hacktoberfest.channel_id) self.bot.loop.create_task(self._fact_publisher_task()) @@ -63,4 +64,4 @@ class HalloweenFacts: def setup(bot): bot.add_cog(HalloweenFacts(bot)) - log.debug("HalloweenFacts cog loaded") + log.info("HalloweenFacts cog loaded") diff --git a/bot/seasons/halloween/halloweenify.py b/bot/seasons/halloween/halloweenify.py index cda07472..0d6964a5 100644 --- a/bot/seasons/halloween/halloweenify.py +++ b/bot/seasons/halloween/halloweenify.py @@ -10,7 +10,7 @@ from discord.ext.commands.cooldowns import BucketType log = logging.getLogger(__name__) -class Halloweenify: +class Halloweenify(commands.Cog): """ A cog to change a invokers nickname to a spooky one! """ @@ -52,4 +52,4 @@ class Halloweenify: def setup(bot): bot.add_cog(Halloweenify(bot)) - log.debug("Halloweenify cog loaded") + log.info("Halloweenify cog loaded") diff --git a/bot/seasons/halloween/monstersurvey.py b/bot/seasons/halloween/monstersurvey.py index 08873f24..2b251b90 100644 --- a/bot/seasons/halloween/monstersurvey.py +++ b/bot/seasons/halloween/monstersurvey.py @@ -4,7 +4,7 @@ import os from discord import Embed from discord.ext import commands -from discord.ext.commands import Bot, Context +from discord.ext.commands import Bot, Cog, Context log = logging.getLogger(__name__) @@ -14,7 +14,7 @@ EMOJIS = { } -class MonsterSurvey: +class MonsterSurvey(Cog): """ Vote for your favorite monster! This command allows users to vote for their favorite listed monster. @@ -215,4 +215,4 @@ class MonsterSurvey: def setup(bot): bot.add_cog(MonsterSurvey(bot)) - log.debug("MonsterSurvey cog loaded") + log.info("MonsterSurvey cog loaded") diff --git a/bot/seasons/halloween/scarymovie.py b/bot/seasons/halloween/scarymovie.py index b280781e..dcff4f58 100644 --- a/bot/seasons/halloween/scarymovie.py +++ b/bot/seasons/halloween/scarymovie.py @@ -13,7 +13,7 @@ TMDB_API_KEY = environ.get('TMDB_API_KEY') TMDB_TOKEN = environ.get('TMDB_TOKEN') -class ScaryMovie: +class ScaryMovie(commands.Cog): """ Selects a random scary movie and embeds info into discord chat """ @@ -138,4 +138,4 @@ class ScaryMovie: def setup(bot): bot.add_cog(ScaryMovie(bot)) - log.debug("ScaryMovie cog loaded") + log.info("ScaryMovie cog loaded") diff --git a/bot/seasons/halloween/spookyavatar.py b/bot/seasons/halloween/spookyavatar.py index a1173740..032ad352 100644 --- a/bot/seasons/halloween/spookyavatar.py +++ b/bot/seasons/halloween/spookyavatar.py @@ -12,7 +12,7 @@ from bot.utils.halloween import spookifications log = logging.getLogger(__name__) -class SpookyAvatar: +class SpookyAvatar(commands.Cog): """ A cog that spookifies an avatar. @@ -55,4 +55,4 @@ class SpookyAvatar: def setup(bot): bot.add_cog(SpookyAvatar(bot)) - log.debug("SpookyAvatar cog loaded") + log.info("SpookyAvatar cog loaded") diff --git a/bot/seasons/halloween/spookygif.py b/bot/seasons/halloween/spookygif.py index 1233773b..c11d5ecb 100644 --- a/bot/seasons/halloween/spookygif.py +++ b/bot/seasons/halloween/spookygif.py @@ -9,7 +9,7 @@ from bot.constants import Tokens log = logging.getLogger(__name__) -class SpookyGif: +class SpookyGif(commands.Cog): """ A cog to fetch a random spooky gif from the web! """ @@ -40,4 +40,4 @@ class SpookyGif: def setup(bot): bot.add_cog(SpookyGif(bot)) - log.debug("SpookyGif cog loaded") + log.info("SpookyGif cog loaded") diff --git a/bot/seasons/halloween/spookyreact.py b/bot/seasons/halloween/spookyreact.py index f63cd7e5..3b4e3fdf 100644 --- a/bot/seasons/halloween/spookyreact.py +++ b/bot/seasons/halloween/spookyreact.py @@ -2,6 +2,7 @@ import logging import re import discord +from discord.ext.commands import Cog log = logging.getLogger(__name__) @@ -16,7 +17,7 @@ SPOOKY_TRIGGERS = { } -class SpookyReact: +class SpookyReact(Cog): """ A cog that makes the bot react to message triggers. @@ -25,6 +26,7 @@ class SpookyReact: def __init__(self, bot): self.bot = bot + @Cog.listener() async def on_message(self, ctx: discord.Message): """ A command to send the seasonalbot github project @@ -69,4 +71,4 @@ class SpookyReact: def setup(bot): bot.add_cog(SpookyReact(bot)) - log.debug("SpookyReact cog loaded") + log.info("SpookyReact cog loaded") diff --git a/bot/seasons/halloween/spookysound.py b/bot/seasons/halloween/spookysound.py index 4cab1239..1e430dab 100644 --- a/bot/seasons/halloween/spookysound.py +++ b/bot/seasons/halloween/spookysound.py @@ -10,7 +10,7 @@ from bot.constants import Hacktoberfest log = logging.getLogger(__name__) -class SpookySound: +class SpookySound(commands.Cog): """ A cog that plays a spooky sound in a voice channel on command. """ @@ -47,4 +47,4 @@ class SpookySound: def setup(bot): bot.add_cog(SpookySound(bot)) - log.debug("SpookySound cog loaded") + log.info("SpookySound cog loaded") diff --git a/bot/seasons/pride/__init__.py b/bot/seasons/pride/__init__.py new file mode 100644 index 00000000..d8a7e34b --- /dev/null +++ b/bot/seasons/pride/__init__.py @@ -0,0 +1,17 @@ +from bot.seasons import SeasonBase + + +class Pride(SeasonBase): + """ + No matter your origin, identity or sexuality, we come together to celebrate each and everyone's individuality. + Feature contributions to ProudBot is encouraged to commemorate the history and challenges of the LGBTQ+ community. + Happy Pride Month + """ + + name = "pride" + bot_name = "ProudBot" + greeting = "Happy Pride Month!" + + # Duration of season + start_date = "01/06" + end_date = "30/06" diff --git a/bot/seasons/season.py b/bot/seasons/season.py index ae12057f..b7892606 100644 --- a/bot/seasons/season.py +++ b/bot/seasons/season.py @@ -352,7 +352,7 @@ class SeasonBase: await bot.send_log("SeasonalBot Loaded!", f"Active Season: **{self.name_clean}**") -class SeasonManager: +class SeasonManager(commands.Cog): """ A cog for managing seasons. """ diff --git a/bot/seasons/valentines/be_my_valentine.py b/bot/seasons/valentines/be_my_valentine.py index 0046ceb4..4e2182c3 100644 --- a/bot/seasons/valentines/be_my_valentine.py +++ b/bot/seasons/valentines/be_my_valentine.py @@ -15,7 +15,7 @@ log = logging.getLogger(__name__) HEART_EMOJIS = [":heart:", ":gift_heart:", ":revolving_hearts:", ":sparkling_heart:", ":two_hearts:"] -class BeMyValentine: +class BeMyValentine(commands.Cog): """ A cog that sends valentines to other users ! """ @@ -238,4 +238,4 @@ class BeMyValentine: def setup(bot): bot.add_cog(BeMyValentine(bot)) - log.debug("Be My Valentine cog loaded") + log.info("BeMyValentine cog loaded") diff --git a/bot/seasons/valentines/lovecalculator.py b/bot/seasons/valentines/lovecalculator.py index 4df33b93..0662cf5b 100644 --- a/bot/seasons/valentines/lovecalculator.py +++ b/bot/seasons/valentines/lovecalculator.py @@ -9,7 +9,7 @@ from typing import Union import discord from discord import Member from discord.ext import commands -from discord.ext.commands import BadArgument, clean_content +from discord.ext.commands import BadArgument, Cog, clean_content from bot.constants import Roles @@ -20,7 +20,7 @@ with Path('bot', 'resources', 'valentines', 'love_matches.json').open() as file: LOVE_DATA = sorted((int(key), value) for key, value in LOVE_DATA.items()) -class LoveCalculator: +class LoveCalculator(Cog): """ A cog for calculating the love between two people """ @@ -104,3 +104,4 @@ class LoveCalculator: def setup(bot): bot.add_cog(LoveCalculator(bot)) + log.info("LoveCalculator cog loaded") diff --git a/bot/seasons/valentines/movie_generator.py b/bot/seasons/valentines/movie_generator.py index b52eba7f..8fce011b 100644 --- a/bot/seasons/valentines/movie_generator.py +++ b/bot/seasons/valentines/movie_generator.py @@ -11,7 +11,7 @@ TMDB_API_KEY = environ.get("TMDB_API_KEY") log = logging.getLogger(__name__) -class RomanceMovieFinder: +class RomanceMovieFinder(commands.Cog): """ A cog that returns a random romance movie suggestion to a user """ @@ -63,4 +63,4 @@ class RomanceMovieFinder: def setup(bot): bot.add_cog(RomanceMovieFinder(bot)) - log.debug("Random romance movie cog loaded!") + log.info("RomanceMovieFinder cog loaded") diff --git a/bot/seasons/valentines/myvalenstate.py b/bot/seasons/valentines/myvalenstate.py index 9f06553d..7d9f3a59 100644 --- a/bot/seasons/valentines/myvalenstate.py +++ b/bot/seasons/valentines/myvalenstate.py @@ -15,7 +15,7 @@ with open(Path('bot', 'resources', 'valentines', 'valenstates.json'), 'r') as fi STATES = json.load(file) -class MyValenstate: +class MyValenstate(commands.Cog): def __init__(self, bot): self.bot = bot @@ -82,4 +82,4 @@ class MyValenstate: def setup(bot): bot.add_cog(MyValenstate(bot)) - log.debug("MyValenstate cog loaded") + log.info("MyValenstate cog loaded") diff --git a/bot/seasons/valentines/pickuplines.py b/bot/seasons/valentines/pickuplines.py new file mode 100644 index 00000000..e1abb4e5 --- /dev/null +++ b/bot/seasons/valentines/pickuplines.py @@ -0,0 +1,44 @@ +import logging +import random +from json import load +from pathlib import Path + +import discord +from discord.ext import commands + +from bot.constants import Colours + +log = logging.getLogger(__name__) + +with open(Path('bot', 'resources', 'valentines', 'pickup_lines.json'), 'r', encoding="utf8") as f: + pickup_lines = load(f) + + +class PickupLine(commands.Cog): + """ + A cog that gives random cheesy pickup lines. + """ + + def __init__(self, bot): + self.bot = bot + + @commands.command() + async def pickupline(self, ctx): + """ + Gives you a random pickup line. Note that most of them are very cheesy! + """ + random_line = random.choice(pickup_lines['lines']) + embed = discord.Embed( + title=':cheese: Your pickup line :cheese:', + description=random_line['line'], + color=Colours.pink + ) + embed.set_thumbnail( + url=random_line.get('image', pickup_lines['placeholder']) + ) + await ctx.send(embed=embed) + + +def setup(bot): + bot.add_cog(PickupLine(bot)) + log.info('PickupLine cog loaded') diff --git a/bot/seasons/valentines/savethedate.py b/bot/seasons/valentines/savethedate.py index b9484be9..fbc9eb82 100644 --- a/bot/seasons/valentines/savethedate.py +++ b/bot/seasons/valentines/savethedate.py @@ -12,8 +12,11 @@ log = logging.getLogger(__name__) HEART_EMOJIS = [":heart:", ":gift_heart:", ":revolving_hearts:", ":sparkling_heart:", ":two_hearts:"] +with open(Path('bot', 'resources', 'valentines', 'date_ideas.json'), 'r', encoding="utf8") as f: + VALENTINES_DATES = load(f) -class SaveTheDate: + +class SaveTheDate(commands.Cog): """ A cog that gives random suggestion, for a valentines date ! """ @@ -26,19 +29,17 @@ class SaveTheDate: """ Gives you ideas for what to do on a date with your valentine. """ - with open(Path('bot', 'resources', 'valentines', 'date_ideas.json'), 'r', encoding="utf8") as f: - valentine_dates = load(f) - random_date = random.choice(valentine_dates['ideas']) - emoji_1 = random.choice(HEART_EMOJIS) - emoji_2 = random.choice(HEART_EMOJIS) - embed = discord.Embed( - title=f"{emoji_1}{random_date['name']}{emoji_2}", - description=f"{random_date['description']}", - colour=Colours.pink - ) - await ctx.send(embed=embed) + random_date = random.choice(VALENTINES_DATES['ideas']) + emoji_1 = random.choice(HEART_EMOJIS) + emoji_2 = random.choice(HEART_EMOJIS) + embed = discord.Embed( + title=f"{emoji_1}{random_date['name']}{emoji_2}", + description=f"{random_date['description']}", + colour=Colours.pink + ) + await ctx.send(embed=embed) def setup(bot): bot.add_cog(SaveTheDate(bot)) - log.debug("Save the date cog loaded") + log.info("SaveTheDate cog loaded") diff --git a/bot/seasons/valentines/valentine_zodiac.py b/bot/seasons/valentines/valentine_zodiac.py index 06c0237d..33fc739a 100644 --- a/bot/seasons/valentines/valentine_zodiac.py +++ b/bot/seasons/valentines/valentine_zodiac.py @@ -14,7 +14,7 @@ LETTER_EMOJI = ':love_letter:' HEART_EMOJIS = [":heart:", ":gift_heart:", ":revolving_hearts:", ":sparkling_heart:", ":two_hearts:"] -class ValentineZodiac: +class ValentineZodiac(commands.Cog): """ A cog that returns a counter compatible zodiac sign to the given user's zodiac sign. """ @@ -56,4 +56,4 @@ class ValentineZodiac: def setup(bot): bot.add_cog(ValentineZodiac(bot)) - log.debug("Valentine Zodiac cog loaded") + log.info("ValentineZodiac cog loaded") diff --git a/bot/seasons/valentines/whoisvalentine.py b/bot/seasons/valentines/whoisvalentine.py index 2fe07aba..59a13ca3 100644 --- a/bot/seasons/valentines/whoisvalentine.py +++ b/bot/seasons/valentines/whoisvalentine.py @@ -14,7 +14,7 @@ with open(Path("bot", "resources", "valentines", "valentine_facts.json"), "r") a FACTS = json.load(file) -class ValentineFacts: +class ValentineFacts(commands.Cog): def __init__(self, bot): self.bot = bot @@ -51,3 +51,4 @@ class ValentineFacts: def setup(bot): bot.add_cog(ValentineFacts(bot)) + log.info("ValentineFacts cog loaded") diff --git a/docker/Dockerfile b/docker/Dockerfile index edeb5b50..1445441c 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,15 +1,25 @@ -FROM python:3.7-alpine3.7 -RUN apk add --update libffi-dev tini build-base git jpeg-dev zlib zlib-dev +FROM python:3.7.2-alpine3.9 -RUN mkdir /bot -COPY . /bot -WORKDIR /bot +ENTRYPOINT ["python"] +CMD ["-m", "bot"] -ENV LIBRARY_PATH=/lib:/usr/lib \ - PIPENV_VENV_IN_PROJECT=1 +ENV PIP_NO_CACHE_DIR="false" \ + PIPENV_DONT_USE_PYENV="1" \ + PIPENV_HIDE_EMOJIS="1" \ + PIPENV_IGNORE_VIRTUALENVS="1" \ + PIPENV_NOSPIN="1" +RUN apk add --no-cache --update \ + build-base \ + git \ + libffi-dev \ + # Pillow dependencies + freetype-dev \ + libjpeg-turbo-dev \ + zlib-dev RUN pip install pipenv -RUN pipenv install --deploy --system -ENTRYPOINT ["/sbin/tini", "--"] -CMD ["python", "-m", "bot"] +COPY . /bot +WORKDIR /bot + +RUN pipenv install --deploy --system diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index de1f4cf2..6e274451 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,8 +1,9 @@ -version: "3" +version: "3.7" services: dumbo: image: pythondiscord/seasonalbot:latest container_name: seasonalbot + init: true restart: always |