aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.pre-commit-config.yaml5
-rw-r--r--CONTRIBUTING.md54
-rw-r--r--Pipfile4
-rw-r--r--Pipfile.lock245
-rwxr-xr-xREADME.md361
-rw-r--r--azure-pipelines.yml1
-rw-r--r--bot/bot.py8
-rw-r--r--bot/constants.py14
-rw-r--r--bot/resources/valentines/bemyvalentine_valentines.json45
-rw-r--r--bot/resources/valentines/date_ideas.json127
-rw-r--r--bot/resources/valentines/love_matches.json58
-rw-r--r--bot/resources/valentines/valentine_facts.json24
-rw-r--r--bot/resources/valentines/zodiac_compatibility.json262
-rw-r--r--bot/seasons/christmas/adventofcode.py11
-rw-r--r--bot/seasons/evergreen/error_handler.py5
-rw-r--r--bot/seasons/evergreen/fun.py38
-rw-r--r--bot/seasons/valentines/__init__.py19
-rw-r--r--bot/seasons/valentines/be_my_valentine.py241
-rw-r--r--bot/seasons/valentines/lovecalculator.py106
-rw-r--r--bot/seasons/valentines/movie_generator.py66
-rw-r--r--bot/seasons/valentines/savethedate.py44
-rw-r--r--bot/seasons/valentines/valentine_zodiac.py59
-rw-r--r--bot/seasons/valentines/whoisvalentine.py53
-rwxr-xr-xscripts/deploy-azure.sh8
24 files changed, 1682 insertions, 176 deletions
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..1d75342a
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,5 @@
+repos:
+- repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v2.0.0
+ hooks:
+ - id: flake8 \ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5c16e605..4161715e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,14 +1,50 @@
-# Contributors guide
+# Contributing to Seasonalbot
-This project is a community project for the Python Discord community over at https://discord.gg/python. We will be providing support for those of you who are new to Git, and this project is to be considered educational. Server staff and event handlers from the Discord server will, in the spirit of the Hacktoberfest event, be creating issues on this repo which can be solved by members of the community. Pull requests will be reviewed and eventually accepted as long as they follow the rules outlined below.
+Seasonalbot is a community project for the Python Discord community over at https://discord.gg/python. We will be providing support for those of you who are new to Git, and this project is to be considered educational.
-# Rules
+Our projects are open-source and are automatically deployed whenever commits are pushed to the `master` branch on each repository, so we've created a set of guidelines in order to keep everything clean and in working order.
-* Your pull request must solve an issue created by a staff member or event handler. These will be labeled with the `approved` label.
-* Only staff may create issues for you to solve, although you are free to suggest issues of your own, which staff can choose to approve. If your issue is approved, the `approved` label will be applied to the issue.
-* Pull requests that do not solve an open issue, for issues that have already been solved, or that are low-effort will be marked with the `invalid` label, which will ensure that they do not count towards your 5 Hacktoberfest PRs.
-* You must be a member of our Discord community in order to contribute to this project.
+## Rules
-# Installation & Dependency Management
+1. You must be a member of [our Discord community](https://discord.gg/python) in order to contribute to this project.
+2. Your pull request must solve an issue created by or approved a staff member or event handler. These will be labeled with the `approved` label. Feel free to suggest issues of your own, which staff can choose to approve.
+3. **No force-pushes** or modifying the Git history in any way.
+4. If you have direct access to the repository, **create a branch for your changes** and create a pull request for that branch. If not, create a branch on a fork of the repository and create a pull request from there.
+ * It's common practice for a repository to reject direct pushes to `master`, so make branching a habit!
+5. **Adhere to the prevailing code style**, which we enforce using [flake8](http://flake8.pycqa.org/en/latest/index.html).
+ * Run `flake8` against your code **before** you push it. Your commit will be rejected by the build server if it fails to lint.
+ * [Git Hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) are a powerful tool that can be a daunting to set up. Fortunately, [`pre-commit`](https://github.com/pre-commit/pre-commit) abstracts this process away from you and is provided as a dev dependency for this project. Run `pipenv run precommit` when setting up the project and you'll never have to worry about breaking the build for linting errors.
+6. **Make great commits**. A well structured git log is key to a project's maintainability; it efficiently provides insight into when and *why* things were done for future maintainers of the project.
+ * Commits should be as narrow in scope as possible. Commits that span hundreds of lines across multiple unrelated functions and/or files are very hard for maintainers to follow. After about a week they'll probably be hard for you to follow too.
+ * Try to avoid making minor commits for fixing typos or linting errors. Since you've already set up a pre-commit hook to run `flake8` before a commit, you shouldn't be committing linting issues anyway.
+ * A more in-depth guide to writing great commit messages can be found in Chris Beam's [*How to Write a Git Commit Message*](https://chris.beams.io/posts/git-commit/)
+7. **Avoid frequent pushes to the main repository**. This goes for PRs opened against your fork as well. Our test build pipelines are triggered every time a push to the repository (or PR) is made. Try to batch your commits until you've finished working for that session, or you've reached a point where collaborators need your commits to continue their own work. This also provides you the opportunity to amend commits for minor changes rather than having to commit them on their own because you've already pushed.
+ * This includes merging master into your branch. Try to leave merging from master for after your PR passes review; a maintainer will bring your PR up to date before merging. Exceptions to this include: resolving merge conflicts, needing something that was pushed to master for your branch, or something was pushed to master that could potentionally affect the functionality of what you're writing.
+8. **Don't fight the framework**. Every framework has its flaws, but the frameworks we've picked out have been carefully chosen for their particular merits. If you can avoid it, please resist reimplementing swathes of framework logic - the work has already been done for you!
+9. If someone is working on a pull request, **do not open your own pull request for the same task**. Instead, collaborate with the author(s) of the existing pull request. Communication is key, and there's no point in two separate implementations of the same thing.
+ * One option is to fork the other contributor's repository and submit your changes to their branch with your own pull request. We suggest following these guidelines when interacting with their repository as well.
+10. **Work as a team** and collaborate whereever possible. Keep things friendly and help each other out - these are shared projects and nobody likes to have their feet trodden on.
+11. **Internal projects are internal**. As a contributor, you have access to information that the rest of the server does not. With this trust comes responsibility - do not release any information you have learned as a result of your contributor position. We are very strict about announcing things at specific times, and many staff members will not appreciate a disruption of the announcement schedule.
-Hacktoberbot utilizes [Pipenv](https://pipenv.readthedocs.io/en/latest/) for installation and dependency management. For users unfamiliar with the Pipenv workflow, a [Pipenv Primer](https://github.com/python-discord/seasonalbot/wiki/Hacktoberbot-and-Pipenv) is provided in Seasonalbot's Wiki.
+Above all, the needs of our community should come before the wants of an individual. Work together, build solutions to problems and try to do so in a way that people can learn from easily. Abuse of our trust may result in the loss of your Contributor role, especially in relation to Rule 7.
+
+## Changes to this arrangement
+
+All projects evolve over time, and this contribution guide is no different. This document is open to pull requests or changes by contributors. If you believe you have something valuable to add or change, please don't hesitate to do so in a PR.
+
+## Supplemental Information
+### Developer Environment
+Seasonalbot utilizes [Pipenv](https://pipenv.readthedocs.io/en/latest/) for installation and dependency management. For users unfamiliar with the Pipenv workflow, Pipenv's documentation provides a [Basic Usage](https://pipenv.readthedocs.io/en/latest/basics/) tutorial, along with some of the more advanced workflows.
+
+### Logging levels
+The project currently defines [`logging`](https://docs.python.org/3/library/logging.html) levels as follows:
+* **TRACE:** Use this for tracing every step of a complex process. That way we can see which step of the process failed. Err on the side of verbose. **Note:** This is a PyDis-implemented logging level.
+* **DEBUG:** Someone is interacting with the application, and the application is behaving as expected.
+* **INFO:** Something completely ordinary happened. Like a cog loading during startup.
+* **WARNING:** Someone is interacting with the application in an unexpected way or the application is responding in an unexpected way, but without causing an error.
+* **ERROR:** An error that affects the specific part that is being interacted with
+* **CRITICAL:** An error that affects the whole application.
+
+## Footnotes
+
+This document was inspired by the [Glowstone contribution guidelines](https://github.com/GlowstoneMC/Glowstone/blob/dev/docs/CONTRIBUTING.md).
diff --git a/Pipfile b/Pipfile
index 6ffc2136..90707d25 100644
--- a/Pipfile
+++ b/Pipfile
@@ -4,7 +4,7 @@ verify_ssl = true
name = "pypi"
[packages]
-"discord-py" = {ref = "rewrite", git = "https://github.com/Rapptz/discord.py"}
+discord-py = {ref = "rewrite",git = "https://github.com/Rapptz/discord.py"}
arrow = "*"
beautifulsoup4 = "*"
aiodns = "*"
@@ -18,6 +18,7 @@ pytz = "*"
"flake8-tidy-imports" = "*"
"flake8-todo" = "*"
"flake8-string-format" = "*"
+pre-commit = "*"
[requires]
python_version = "3.7"
@@ -25,3 +26,4 @@ python_version = "3.7"
[scripts]
start = "python -m bot"
lint = "flake8 bot"
+precommit = "pre-commit install"
diff --git a/Pipfile.lock b/Pipfile.lock
index 1f693364..a318af42 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "37690a44eef1762372759be11694f873af63f032de0264280ad1762d917c2b89"
+ "sha256": "56ef4499fef622c131943cc402884901581fa337ac602069e9e6e2d25096ff93"
},
"pipfile-spec": 6,
"requires": {
@@ -18,93 +18,95 @@
"default": {
"aiodns": {
"hashes": [
- "sha256:99d0652f2c02f73bfa646bf44af82705260a523014576647d7959e664830b26b",
- "sha256:d8677adc679ce8d0ef706c14d9c3d2f27a0e0cc11d59730cdbaf218ad52dd9ea"
+ "sha256:970688599fcb7d65334ec490a94a51afd634ae2de8a2138d21e2ffbbddc12718",
+ "sha256:d67e14b32176bcf3ff79b5d47c466011ce4adeadfa264f7949da1377332a0449"
],
"index": "pypi",
- "version": "==1.1.1"
+ "version": "==1.2.0"
},
"arrow": {
"hashes": [
- "sha256:a558d3b7b6ce7ffc74206a86c147052de23d3d4ef0e17c210dd478c53575c4cd"
+ "sha256:9cb4a910256ed536751cd5728673bfb53e6f0026e240466f90c2a92c0b79c895"
],
"index": "pypi",
- "version": "==0.12.1"
+ "version": "==0.13.0"
},
"beautifulsoup4": {
"hashes": [
- "sha256:194ec62a25438adcb3fdb06378b26559eda1ea8a747367d34c33cef9c7f48d57",
- "sha256:90f8e61121d6ae58362ce3bed8cd997efb00c914eae0ff3d363c32f9a9822d10",
- "sha256:f0abd31228055d698bb392a826528ea08ebb9959e6bea17c606fd9c9009db938"
+ "sha256:034740f6cb549b4e932ae1ab975581e6103ac8f942200a0e9759065984391858",
+ "sha256:945065979fb8529dd2f37dbb58f00b661bdbcbebf954f93b32fdf5263ef35348",
+ "sha256:ba6d5c59906a85ac23dadfe5c88deaf3e179ef565f4898671253e50a78680718"
],
"index": "pypi",
- "version": "==4.6.3"
+ "version": "==4.7.1"
},
"discord-py": {
"git": "https://github.com/Rapptz/discord.py",
- "ref": "66e5033785259400d340b8c00eaa8ad60fbbb82a"
+ "ref": "7f4c57dd5ad20b7fa10aea485f674a4bc24b9547"
},
"pillow": {
"hashes": [
- "sha256:00203f406818c3f45d47bb8fe7e67d3feddb8dcbbd45a289a1de7dd789226360",
- "sha256:0616f800f348664e694dddb0b0c88d26761dd5e9f34e1ed7b7a7d2da14b40cb7",
- "sha256:1f7908aab90c92ad85af9d2fec5fc79456a89b3adcc26314d2cde0e238bd789e",
- "sha256:2ea3517cd5779843de8a759c2349a3cd8d3893e03ab47053b66d5ec6f8bc4f93",
- "sha256:48a9f0538c91fc136b3a576bee0e7cd174773dc9920b310c21dcb5519722e82c",
- "sha256:5280ebc42641a1283b7b1f2c20e5b936692198b9dd9995527c18b794850be1a8",
- "sha256:5e34e4b5764af65551647f5cc67cf5198c1d05621781d5173b342e5e55bf023b",
- "sha256:63b120421ab85cad909792583f83b6ca3584610c2fe70751e23f606a3c2e87f0",
- "sha256:696b5e0109fe368d0057f484e2e91717b49a03f1e310f857f133a4acec9f91dd",
- "sha256:870ed021a42b1b02b5fe4a739ea735f671a84128c0a666c705db2cb9abd528eb",
- "sha256:916da1c19e4012d06a372127d7140dae894806fad67ef44330e5600d77833581",
- "sha256:9303a289fa0811e1c6abd9ddebfc770556d7c3311cb2b32eff72164ddc49bc64",
- "sha256:9577888ecc0ad7d06c3746afaba339c94d62b59da16f7a5d1cff9e491f23dace",
- "sha256:987e1c94a33c93d9b209315bfda9faa54b8edfce6438a1e93ae866ba20de5956",
- "sha256:99a3bbdbb844f4fb5d6dd59fac836a40749781c1fa63c563bc216c27aef63f60",
- "sha256:99db8dc3097ceafbcff9cb2bff384b974795edeb11d167d391a02c7bfeeb6e16",
- "sha256:a5a96cf49eb580756a44ecf12949e52f211e20bffbf5a95760ac14b1e499cd37",
- "sha256:aa6ca3eb56704cdc0d876fc6047ffd5ee960caad52452fbee0f99908a141a0ae",
- "sha256:aade5e66795c94e4a2b2624affeea8979648d1b0ae3fcee17e74e2c647fc4a8a",
- "sha256:b78905860336c1d292409e3df6ad39cc1f1c7f0964e66844bbc2ebfca434d073",
- "sha256:b92f521cdc4e4a3041cc343625b699f20b0b5f976793fb45681aac1efda565f8",
- "sha256:bfde84bbd6ae5f782206d454b67b7ee8f7f818c29b99fd02bf022fd33bab14cb",
- "sha256:c2b62d3df80e694c0e4a0ed47754c9480521e25642251b3ab1dff050a4e60409",
- "sha256:c5e2be6c263b64f6f7656e23e18a4a9980cffc671442795682e8c4e4f815dd9f",
- "sha256:c99aa3c63104e0818ec566f8ff3942fb7c7a8f35f9912cb63fd8e12318b214b2",
- "sha256:dae06620d3978da346375ebf88b9e2dd7d151335ba668c995aea9ed07af7add4",
- "sha256:db5499d0710823fa4fb88206050d46544e8f0e0136a9a5f5570b026584c8fd74",
- "sha256:f36baafd82119c4a114b9518202f2a983819101dcc14b26e43fc12cbefdce00e",
- "sha256:f52b79c8796d81391ab295b04e520bda6feed54d54931708872e8f9ae9db0ea1",
- "sha256:ff8cff01582fa1a7e533cb97f628531c4014af4b5f38e33cdcfe5eec29b6d888"
+ "sha256:051de330a06c99d6f84bcf582960487835bcae3fc99365185dc2d4f65a390c0e",
+ "sha256:0ae5289948c5e0a16574750021bd8be921c27d4e3527800dc9c2c1d2abc81bf7",
+ "sha256:0b1efce03619cdbf8bcc61cfae81fcda59249a469f31c6735ea59badd4a6f58a",
+ "sha256:163136e09bd1d6c6c6026b0a662976e86c58b932b964f255ff384ecc8c3cefa3",
+ "sha256:18e912a6ccddf28defa196bd2021fe33600cbe5da1aa2f2e2c6df15f720b73d1",
+ "sha256:24ec3dea52339a610d34401d2d53d0fb3c7fd08e34b20c95d2ad3973193591f1",
+ "sha256:267f8e4c0a1d7e36e97c6a604f5b03ef58e2b81c1becb4fccecddcb37e063cc7",
+ "sha256:3273a28734175feebbe4d0a4cde04d4ed20f620b9b506d26f44379d3c72304e1",
+ "sha256:4c678e23006798fc8b6f4cef2eaad267d53ff4c1779bd1af8725cc11b72a63f3",
+ "sha256:4d4bc2e6bb6861103ea4655d6b6f67af8e5336e7216e20fff3e18ffa95d7a055",
+ "sha256:505738076350a337c1740a31646e1de09a164c62c07db3b996abdc0f9d2e50cf",
+ "sha256:5233664eadfa342c639b9b9977190d64ad7aca4edc51a966394d7e08e7f38a9f",
+ "sha256:5d95cb9f6cced2628f3e4de7e795e98b2659dfcc7176ab4a01a8b48c2c2f488f",
+ "sha256:7eda4c737637af74bac4b23aa82ea6fbb19002552be85f0b89bc27e3a762d239",
+ "sha256:801ddaa69659b36abf4694fed5aa9f61d1ecf2daaa6c92541bbbbb775d97b9fe",
+ "sha256:825aa6d222ce2c2b90d34a0ea31914e141a85edefc07e17342f1d2fdf121c07c",
+ "sha256:9c215442ff8249d41ff58700e91ef61d74f47dfd431a50253e1a1ca9436b0697",
+ "sha256:a3d90022f2202bbb14da991f26ca7a30b7e4c62bf0f8bf9825603b22d7e87494",
+ "sha256:a631fd36a9823638fe700d9225f9698fb59d049c942d322d4c09544dc2115356",
+ "sha256:a6523a23a205be0fe664b6b8747a5c86d55da960d9586db039eec9f5c269c0e6",
+ "sha256:a756ecf9f4b9b3ed49a680a649af45a8767ad038de39e6c030919c2f443eb000",
+ "sha256:b117287a5bdc81f1bac891187275ec7e829e961b8032c9e5ff38b70fd036c78f",
+ "sha256:ba04f57d1715ca5ff74bb7f8a818bf929a204b3b3c2c2826d1e1cc3b1c13398c",
+ "sha256:cd878195166723f30865e05d87cbaf9421614501a4bd48792c5ed28f90fd36ca",
+ "sha256:cee815cc62d136e96cf76771b9d3eb58e0777ec18ea50de5cfcede8a7c429aa8",
+ "sha256:d1722b7aa4b40cf93ac3c80d3edd48bf93b9208241d166a14ad8e7a20ee1d4f3",
+ "sha256:d7c1c06246b05529f9984435fc4fa5a545ea26606e7f450bdbe00c153f5aeaad",
+ "sha256:e9c8066249c040efdda84793a2a669076f92a301ceabe69202446abb4c5c5ef9",
+ "sha256:f227d7e574d050ff3996049e086e1f18c7bd2d067ef24131e50a1d3fe5831fbc",
+ "sha256:fc9a12aad714af36cf3ad0275a96a733526571e52710319855628f476dcb144e"
],
"index": "pypi",
- "version": "==5.3.0"
+ "version": "==5.4.1"
},
"pycares": {
"hashes": [
- "sha256:0e81c971236bb0767354f1456e67ab6ae305f248565ce77cd413a311f9572bf5",
- "sha256:11c0ff3ccdb5a838cbd59a4e59df35d31355a80a61393bca786ca3b44569ba10",
- "sha256:170d62bd300999227e64da4fa85459728cc96e62e44780bbc86a915fdae01f78",
- "sha256:36f4c03df57c41a87eb3d642201684eb5a8bc194f4bafaa9f60ee6dc0aef8e40",
- "sha256:371ce688776da984c4105c8ca760cc60944b9b49ccf8335c71dc7669335e6173",
- "sha256:3a2234516f7db495083d8bba0ccdaabae587e62cfcd1b8154d5d0b09d3a48dfc",
- "sha256:3f288586592c697109b2b06e3988b7e17d9765887b5fc367010ee8500cbddc86",
- "sha256:40134cee03c8bbfbc644d4c0bc81796e12dd012a5257fb146c5a5417812ee5f7",
- "sha256:722f5d2c5f78d47b13b0112f6daff43ce4e08e8152319524d14f1f917cc5125e",
- "sha256:7b18fab0ed534a898552df91bc804bd62bb3a2646c11e054baca14d23663e1d6",
- "sha256:8a39d03bd99ea191f86b990ef67ecce878d6bf6518c5cde9173fb34fb36beb5e",
- "sha256:8ea263de8bf1a30b0d87150b4aa0e3203cf93bc1723ea3e7408a7d25e1299217",
- "sha256:943e2dc67ff45ab4c81d628c959837d01561d7e185080ab7a276b8ca67573fb5",
- "sha256:9d56a54c93e64b30c0d31f394d9890f175edec029cd846221728f99263cdee82",
- "sha256:b95b339c11d824f0bb789d31b91c8534916fcbdce248cccce216fa2630bb8a90",
- "sha256:bbfd9aba1e172cd2ab7b7142d49b28cf44d6451c4a66a870aff1dc3cb84849c7",
- "sha256:d8637bcc2f901aa61ec1d754abc862f9f145cb0346a0249360df4c159377018e",
- "sha256:e2446577eeea79d2179c9469d9d4ce3ab8a07d7985465c3cb91e7d74abc329b6",
- "sha256:e72fa163f37ae3b09f143cc6690a36f012d13e905d142e1beed4ec0e593ff657",
- "sha256:f32b7c63094749fbc0c1106c9a785666ec8afd49ecfe7002a30bb7c42e62b47c",
- "sha256:f50be4dd53f009cfb4b98c3c6b240e18ff9b17e3f1c320bd594bb83eddabfcb2"
- ],
- "version": "==2.3.0"
+ "sha256:080ae0f1b1b754be60b6ef31b9ab2915364c210eb1cb4d8e089357c89d7b9819",
+ "sha256:0eccb76dff0155ddf793a589c6270e1bdbf6975b2824d18d1d23db2075d7fc96",
+ "sha256:223a03d69e864a18d7bb2e0108bca5ba069ef91e5b048b953ed90ea9f50eb77f",
+ "sha256:289e49f98adfd7a2ae3656df26e1d62cf49a06bbc03ced63f243c22cd8919adf",
+ "sha256:292ac442a1d4ff27d41be748ec19f0c4ff47efebfb715064ba336564ea0f2071",
+ "sha256:34771095123da0e54597fe3c5585a28d3799945257e51b378a20778bf33573b6",
+ "sha256:34c8865f2d047be4c301ce90a916c7748be597e271c5c7932e8b9a6de85840f4",
+ "sha256:36af260b215f86ebfe4a5e4aea82fd6036168a5710cbf8aad77019ab52156dda",
+ "sha256:5e8e2a461717da40482b5fecf1119116234922d29660b3c3e01cbc5ba2cbf4bd",
+ "sha256:61e77bd75542c56dff49434fedbafb25604997bc57dc0ebf791a5732503cb1bb",
+ "sha256:691740c332f38a9035b4c6d1f0e6c8af239466ef2373a894d4393f0ea65c815d",
+ "sha256:6bc0e0fdcb4cdc4ca06aa0b07e6e3560d62b2af79ef0ea4589835fcd2059012b",
+ "sha256:96db5c93e2fe2e39f519efb7bb9d86aef56f5813fa0b032e47aba329fa925d57",
+ "sha256:af701b22c91b3e36f65ee9f4b1bc2fe4800c8ed486eb6ef203624acbe53d026d",
+ "sha256:b25bd21bba9c43d44320b719118c2ce35e4a78031f61d906caeb01316d49dafb",
+ "sha256:c42f68319f8ea2322ed81c31a86c4e60547e6e90f3ebef479a7a7540bddbf268",
+ "sha256:cc9a8d35af12bc5f484f3496f9cb3ab5bedfa4dcf3dfff953099453d88b659a7",
+ "sha256:dfee9d198ba6d6f29aa5bf510bfb2c28a60c3f308116f114c9fd311980d3e870",
+ "sha256:e1dd02e110a7a97582097ebba6713d9da28583b538c08e8a14bc82169c5d3e10",
+ "sha256:e48c586c80a139c6c7fb0298b944d1c40752cf839bc8584cc793e42a8971ba6c",
+ "sha256:f509762dec1a70eac32b86c098f37ac9c5d3d4a8a9098983328377c9e71543b2",
+ "sha256:f8e0d61733843844f9019c911d5676818d99c4cd2c54b91de58384c7d962862b",
+ "sha256:fe20280fed496deba60e0f6437b7672bdc83bf45e243bb546af47c60c85bcfbc"
+ ],
+ "version": "==2.4.0"
},
"python-dateutil": {
"hashes": [
@@ -115,21 +117,43 @@
},
"pytz": {
"hashes": [
- "sha256:31cb35c89bd7d333cd32c5f278fca91b523b0834369e757f4c5641ea252236ca",
- "sha256:8e0f8568c118d3077b46be7d654cc8167fa916092e28320cde048e54bfc9f1e6"
+ "sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9",
+ "sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c"
],
"index": "pypi",
- "version": "==2018.7"
+ "version": "==2018.9"
},
"six": {
"hashes": [
- "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
- "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
+ "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
+ "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
+ ],
+ "version": "==1.12.0"
+ },
+ "soupsieve": {
+ "hashes": [
+ "sha256:10687fc53eeb3518e01a0ac84d3d711da623d3298a3039459d3f649927c4a270",
+ "sha256:b23a0d7da0247200fe83c67c34de9d7599ad404106367313d8e65e04174d0b4b"
],
- "version": "==1.11.0"
+ "version": "==1.7.2"
+ },
+ "typing": {
+ "hashes": [
+ "sha256:4027c5f6127a6267a435201981ba156de91ad0d1d98e9ddc2aa173453453492d",
+ "sha256:57dcf675a99b74d64dacf6fba08fb17cf7e3d5fdff53d4a30ea2a5e7e52543d4",
+ "sha256:a4c8473ce11a65999c8f59cb093e70686b6c84c98df58c1dae9b3b196089858a"
+ ],
+ "version": "==3.6.6"
}
},
"develop": {
+ "aspy.yaml": {
+ "hashes": [
+ "sha256:04d26279513618f1024e1aba46471db870b3b33aef204c2d09bcf93bea9ba13f",
+ "sha256:0a77e23fafe7b242068ffc0252cee130d3e509040908fc678d9d1060e7494baa"
+ ],
+ "version": "==1.1.1"
+ },
"attrs": {
"hashes": [
"sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69",
@@ -137,6 +161,13 @@
],
"version": "==18.2.0"
},
+ "cfgv": {
+ "hashes": [
+ "sha256:39d9055c47e3932908fe25abd5807e21dc002630db01c7a5f05738d027e2b706",
+ "sha256:41d22dd864c474f919ecb88900000d2410d640315f75bdb79b3abf9347089641"
+ ],
+ "version": "==1.4.0"
+ },
"flake8": {
"hashes": [
"sha256:6a35f5b8761f45c5513e3405f110a86bea57982c3b75b766ce7b65217abe1670",
@@ -184,6 +215,20 @@
"index": "pypi",
"version": "==0.7"
},
+ "identify": {
+ "hashes": [
+ "sha256:0b2bb67c857b8048d979caeef4d20a3dfdb0337f154d16a8f9e31cd6e04ae554",
+ "sha256:113622f73da90a723e9baf764553f807051ad80c3a9e8a7edd15aa4309861f4d"
+ ],
+ "version": "==1.2.0"
+ },
+ "importlib-metadata": {
+ "hashes": [
+ "sha256:a17ce1a8c7bff1e8674cb12c992375d8d0800c9190177ecf0ad93e0097224095",
+ "sha256:b50191ead8c70adfa12495fba19ce6d75f2e0275c14c5a7beb653d6799b512bd"
+ ],
+ "version": "==0.8"
+ },
"mccabe": {
"hashes": [
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
@@ -191,6 +236,20 @@
],
"version": "==0.6.1"
},
+ "nodeenv": {
+ "hashes": [
+ "sha256:ad8259494cf1c9034539f6cced78a1da4840a4b157e23640bc4a0c0546b0cb7a"
+ ],
+ "version": "==1.3.3"
+ },
+ "pre-commit": {
+ "hashes": [
+ "sha256:2cb7a588fdc78e4ec4e624932765e65d285159f4b3425121106cbd9060e40e04",
+ "sha256:74ee5779a17ef540efdf9a832911fe9057b1bb57d5d0152eace6534a228a863b"
+ ],
+ "index": "pypi",
+ "version": "==1.14.2"
+ },
"pycodestyle": {
"hashes": [
"sha256:cbc619d09254895b0d12c2c691e237b2e91e9b2ecf5e84c26b35400f93dcfb83",
@@ -204,6 +263,50 @@
"sha256:f661252913bc1dbe7fcfcbf0af0db3f42ab65aabd1a6ca68fe5d466bace94dae"
],
"version": "==2.0.0"
+ },
+ "pyyaml": {
+ "hashes": [
+ "sha256:3d7da3009c0f3e783b2c873687652d83b1bbfd5c88e9813fb7e5b03c0dd3108b",
+ "sha256:3ef3092145e9b70e3ddd2c7ad59bdd0252a94dfe3949721633e41344de00a6bf",
+ "sha256:40c71b8e076d0550b2e6380bada1f1cd1017b882f7e16f09a65be98e017f211a",
+ "sha256:558dd60b890ba8fd982e05941927a3911dc409a63dcb8b634feaa0cda69330d3",
+ "sha256:a7c28b45d9f99102fa092bb213aa12e0aaf9a6a1f5e395d36166639c1f96c3a1",
+ "sha256:aa7dd4a6a427aed7df6fb7f08a580d68d9b118d90310374716ae90b710280af1",
+ "sha256:bc558586e6045763782014934bfaf39d48b8ae85a2713117d16c39864085c613",
+ "sha256:d46d7982b62e0729ad0175a9bc7e10a566fc07b224d2c79fafb5e032727eaa04",
+ "sha256:d5eef459e30b09f5a098b9cea68bebfeb268697f78d647bd255a085371ac7f3f",
+ "sha256:e01d3203230e1786cd91ccfdc8f8454c8069c91bee3962ad93b87a4b2860f537",
+ "sha256:e170a9e6fcfd19021dd29845af83bb79236068bf5fd4df3327c1be18182b2531"
+ ],
+ "version": "==3.13"
+ },
+ "six": {
+ "hashes": [
+ "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
+ "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
+ ],
+ "version": "==1.12.0"
+ },
+ "toml": {
+ "hashes": [
+ "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
+ "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
+ ],
+ "version": "==0.10.0"
+ },
+ "virtualenv": {
+ "hashes": [
+ "sha256:34b9ae3742abed2f95d3970acf4d80533261d6061b51160b197f84e5b4c98b4c",
+ "sha256:fa736831a7b18bd2bfeef746beb622a92509e9733d645952da136b0639cd40cd"
+ ],
+ "version": "==16.2.0"
+ },
+ "zipp": {
+ "hashes": [
+ "sha256:55ca87266c38af6658b84db8cfb7343cdb0bf275f93c7afaea0d8e7a209c7478",
+ "sha256:682b3e1c62b7026afe24eadf6be579fb45fec54c07ea218bded8092af07a68c4"
+ ],
+ "version": "==0.3.3"
}
}
}
diff --git a/README.md b/README.md
index 6227a36d..877471ed 100755
--- a/README.md
+++ b/README.md
@@ -3,105 +3,298 @@
[![Build Status](https://dev.azure.com/python-discord/Python%20Discord/_apis/build/status/Seasonal%20Bot%20(Mainline))](https://dev.azure.com/python-discord/Python%20Discord/_build/latest?definitionId=3)
[![Discord](https://discordapp.com/api/guilds/267624335836053506/embed.png)](https://discord.gg/2B963hn)
-A community project initially started for [Hacktoberfest 2018](https://hacktoberfest.digitalocean.com). A Discord bot for the Python Discord community which changes with the seasons, and provides useful event features.
+A Discord bot for the Python Discord community which changes with the seasons, and provides useful event features.
You can find our community by going to https://discord.gg/python
## Motivations
-We know it can be difficult to get into the whole open source thing at first. To help out, we've decided to start a little community project during hacktober that you can all choose to contribute to if you're finding the event a little overwhelming, or if you're new to this whole thing and just want someone to hold your hand at first.
-This later evolved into a bot that will be running all through the year, providing season-appropriate functionality and issues that beginners can work on.
+We know it can be difficult to get into the whole open source thing at first. To help out, we started the HacktoberBot community project during [Hacktoberfest 2018](https://hacktoberfest.digitalocean.com) to help introduce and encourage members to participate in contributing to open source, providing a calmer and helpful environment for those who want to be part of it.
+
+This later evolved into a bot that runs all year, providing season-appropriate functionality and issues that beginners can work on.
## Getting started
-If you are new to this you will find it far easier using PyCharm:
+Please ensure you read the [contributing guidelines](CONTRIBUTING.md) in full.
+
+If you are new to this you may find it easier to use PyCharm. [What is PyCharm?](#what-is-pycharm)
+
+1. [Fork the Project](#fork-the-project)
+2. [Clone & Install Packages](#clone--install-packages)
+3. [Test Server](#test-server)
+4. [Bot Account](#bot-account)
+5. [Configuration](#configuration)
+6. [Working with Git](#working-with-git)
+7. [Pull Requests (PRs)](#pull-requests-prs)
+8. [Further Reading](#further-reading)
+
+### What is PyCharm?
+
+PyCharm is a Python IDE (integrated development environment) that is used to make python development quicker and easier.
+
+Our instruction include PyCharm specific steps for those who have it, so you're welcome to give it a go if you haven't tried it before.
-### With PyCharm
+You can download PyCharm for free [here](https://www.jetbrains.com/pycharm/).
-First things first, what is PyCharm?
-PyCharm is a Python IDE(integrated development environment) that is used to make python development quicker and easier overall.
-So now your going to need to download it [here](https://www.jetbrains.com/pycharm/).
+### Requirements
+
+- [Python 3.7](https://www.python.org/downloads/)
+- [Pipenv](https://pipenv.readthedocs.io/en/latest/install/#installing-pipenv)
+- Git
+ - [Windows Installer](https://git-scm.com/download/win)
+ - [MacOS Installer](https://sourceforge.net/projects/git-osx-installer/files/) or `brew install git`
+
+### Fork the Project
+
+To fork this project, press the Fork button at the top of the [repository page](https://github.com/python-discord/seasonalbot):
+
+![](https://i.imgur.com/gPfSW8j.png)
+
+In your new forked repository, copy the git url by clicking the green `clone or download` button and then the copy link button:
-#### 1. Fork
-Ok, now you have got PyCharm downloading you are going to want to fork this project and find its git URL. To fork scroll to the top of the page and press this button.
-![](https://i.imgur.com/Saf9pgJ.png)
-Then when you new forked repository loads you are going to want to get the Git Url by clicking the green `clone or download` button and then the copy link button as seen below:
![](https://i.imgur.com/o6kuQcZ.png)
-#### 2. Clone
-Now that you have done that you are going to want to load up Pycharm and you'll get something like this without the left sidebar:
-![](https://i.imgur.com/xiGERvR.png)
-You going to want to click Check Out from Version `Control->Git` and you'll get a popup like the one below:
-![](https://i.imgur.com/d4U6Iw7.png)
-Now paste your link in, test the connection and hit `clone`. You now have a copy of your repository to work with and it should setup your Pipenv automatically.
-#### 3. Bot
-Now we have setup our repository we need somewhere to test out bot.
-You'll need to make a new discord server:
-![](https://i.imgur.com/49gBlQI.png)
-We need to make the applicaiton for our bot... navigate over to [discordapp.com/developers](https://discordapp.com/developers) and hit new application
-![](https://i.imgur.com/UIeGPju.png)
-Now we have our discord application you'll want to name your bot as below:
-![](https://i.imgur.com/odTWSMV.png)
-To actually make the bot hit `Bot->Add Bot->Yes, do It!` as below:
-![](https://i.imgur.com/frAUbTZ.png)
-Copy that Token and put to somewhere for safe keeping.
-![](https://i.imgur.com/oEpIqND.png)
-Now to add that robot to out new discord server we need to generate an OAuth2 Url to do so navigate to the OAuth2 tab, Scroll to the OAUTH2 URL GENERATOR section, click the `Bot` checkbox in the scope section and finally hit the `administrator` checkbox in the newly formed Bot Permissions section.
-![](https://i.imgur.com/I2XzYPj.png)
-Copy and paste the link into your browser and follow the instructions to add the bot to your server - ensure it is the server you have just created.
-#### 4. Run Configurations
-Go back to PyCharm and you should have something a bit like below, Your going to want to hit the `Add Configuration` button in the top right.
-![](https://i.imgur.com/nLpDfQO.png)
-We are going to want to choose a python config as below:
-![](https://i.imgur.com/9FgCuP1.png)
-The first setting we need to change is script path as below (the start script may have changed from bot.py so be sure to click the right one
-![](https://i.imgur.com/napKLar.png)
-Now we need to add an enviroment variable - what this will do is allow us to set a value without it affact the main repository.
-To do this click the folder icon to the right of the text, then on the new window the plus icon. Now name the var `HACKTOBERBOT_TOKEN` and give the value the token we kept for safe keeping earilier.
-![](https://i.imgur.com/nZFWNaQ.png)
-Now hit apply on that window and your ready to get going!
-#### 5. Git in PyCharm
-As we work on our project we are going to want to make commits. Commits are effectively a list of changes you made to the pervious version. To make one first hit the green tick in the top right
-![](https://i.imgur.com/BCiisvN.png)
-1. Select the files you wish to commit
-2. Write a brief description of what your commit is
-3. See the actual changes you commit does here (you can also turn some of them off or on if you wish)
-4. Hit commit
-
-![](https://i.imgur.com/xA5ga4C.png)
-Now once you have made a few commits and are happy with your changes you are going to want to push them back to your fork.
-There are three ways of doing this.
-1. Using the VSC Menu `VSC->Git->Push`
-2. Using the VSC popup <code>alt-\`->Push</code>
-3. A shortcut: `ctrl+shift+K`
-
-You should get a menu like below:
-1. List of commits
-2. List of changed files
-3. Hit Push to send to fork!
-
-![](https://i.imgur.com/xA5ga4C.png)
-#### 6. Pull Requests (PR or PRs)
-Goto https://github.com/python-discord/seasonalbot/pulls and the green New Pull Request button!
+
+### Clone & Install Packages
+
+#### With PyCharm:
+
+Load up PyCharm and click `Check Out from Version Control` -> `Git`:
+
+![](https://i.imgur.com/ND7Z5rN.png)
+
+Test the URL with `Test` first and then click `Clone`.
+
+![](https://i.imgur.com/gJkwHA0.png)
+
+Note: PyCharm automatically detects the Pipfile in the project and sets up your packages for you.
+
+#### With console:
+
+Open a console at the directory you want your project to be in and use the git clone command to copy the project files from your fork repository:
+
+```bash
+git clone https://github.com/yourusername/seasonalbot.git
+```
+
+Change your console directory to be within the new project folder (should be named `seasonalbot` usually) and run the pipenv sync command to install and setup your environment.
+```bash
+cd seasonalbot
+pipenv sync --dev
+```
+
+### Test Server
+
+Create a test discord server if you haven't got one already:
+
+![](https://i.imgur.com/49gBlQIl.png)
+
+In your test server, ensure you have the following:
+ - `#announcements` channel
+ - `#dev-logs` channel
+ - `admins` role
+ - A channel to test your commands in.
+
+### Bot Account
+
+Go to the [Discord Developers Portal](https://discordapp.com/developers/applications/) and click the `New Application` button, enter the new bot name and click `Create`:
+
+![](https://i.imgur.com/q5wlDPs.png)
+
+In your new application, go to the `Bot` tab, click `Add Bot` and confirm `Yes, do it!`:
+
+![](https://i.imgur.com/PxlscgF.png)
+
+Change your bot's `Public Bot` setting off so only you can invite it, save, and then get your token with the `Copy` button:
+
+![](https://i.imgur.com/EM3NIiB.png)
+
+Save your token somewhere safe so we can put it in the project settings later.
+
+In the `General Information` tab, grab the Client ID:
+
+![](https://i.imgur.com/pG7Ix3n.png)
+
+Add the Client ID to the following invite url and visit it in your browser to invite the bot to your new test server:
+```
+https://discordapp.com/api/oauth2/authorize?client_id=CLIENT_ID_HERE&permissions=8&scope=bot
+```
+
+Optionally, you can generate your own invite url in the `OAuth` tab, after selecting `bot` as the Scope.
+
+### Configuration
+
+#### Environment Variables:
+
+Create a `.env` file in your project root and copy the following text into it:
+```
+SEASONALBOT_DEBUG=True
+SEASONALBOT_TOKEN=0
+SEASONALBOT_GUILD=0
+SEASONALBOT_ADMIN_ROLE_ID=0
+CHANNEL_ANNOUNCEMENTS=0
+CHANNEL_DEVLOG=0
+```
+
+Edit the variable values to match your own bot token that you put aside earlier, the ID of your Guild, the Announcements and Dev-logs channel IDs and the admin role ID. To get the role ID, you can make it mentionable, and send an escaped mention like `\@admin`:
+
+![](https://i.imgur.com/qANC8jA.png)
+
+Note: These are only the basic environment variables. Each season will likely have their own variables needing to set, so be sure to check out the respective seasonal setups that you're working on.
+
+#### PyCharm Project Config:
+
+Copy the contents of your `.env` file first.
+Click the `Add Configuration` button in the top right of the window.
+
+![](https://i.imgur.com/Qj2RDwU.png)
+
+In the Configuration Window do the following:
+1. Click the plus on the top left and select `Python`.
+2. Add `bot` to the `Script path` input box.
+5. Add `-m` to the `Interpreter options` input box.
+
+![](https://i.imgur.com/3Cs05fg.png)
+
+Click the folder icon for the `Environment variables` input box. In the new window, click the paste icon on the right side and click `OK`:
+
+![](https://i.imgur.com/ET6gC3e.png)
+
+Note: This should have your actual bot token and IDs as the values. If you copied the blank template from above and didn't update it, go back to do so first.
+
+Click `OK` to save and close the Configuration window.
+
+From now on, to run the bot you only need to click the `Run` button in the top right of PyCharm:
+
+![](https://i.imgur.com/YmqnLqC.png)
+
+#### In console:
+
+Since we have the `.env` file in the project root, anytime we run Pipenv and it'll automatically load the environment variables present within.
+
+To run the the bot, use:
+```bash
+pipenv run start
+```
+
+### Working with Git
+
+Working with git can be intimidating at first, so feel free to ask for any help in the server.
+
+For each push request you work on, you'll want to push your commits to a branch just for it in your repository. This keeps your `master` branch clean, so you can pull changes from the original repository without pulling the same commits you've already added to your fork.
+
+Once you finish a push request, you can delete your branch, and pull the changes from the original repository into your master branch. With the master branch updated, you can now create a new branch to work on something else.
+
+Note: You should never commit directly to the original repository's `master` branch. All additions to the `master` branch of the original repository **must** be put through a PR first.
+
+#### Precommit Hook
+
+Projects need to pass linting checks to be able to successfully build and pass as in push requests.
+Read [CONTRIBUTING.md](CONTRIBUTING.md) for more details.
+
+To install the precommit hook, which checks your code before commits are submitted, do the following:
+```bash
+pipenv run precommit
+```
+
+#### In PyCharm
+
+The PyCharm interface has a set of Git actions on the top right section of your workspace:
+
+![](https://i.imgur.com/1LsyMx9.png)
+
+In order from left to right these are:
+- Update
+- Commit
+- History
+- Revert
+
+As we work on our project we are going to want to make commits. Commits are effectively a log of changes you made over time.
+
+Before making any commits, you should make the branch for the PR you're about to work on. At the bottom of the PyCharm workspace, you'll see the current git branch, which you can click to show a menu of actions. You can click `New Branch` to create a new one, and name it something short that references what the PR is to accomplish. Before making new branches, be sure you change to `master` and ensure it's up to date before creating a new branch.
+
+![](https://i.imgur.com/A9zV4lF.png)
+
+After making changes to the project files, you can commit by clicking the `Commit` button that's part of the git actions available on the top right of your workspace.
+
+The flow of making a commit is as follows:
+
+1. Select the files you wish to commit.
+2. Write a brief description of what your commit is.
+3. See the actual changes you commit does, and optionally tick/untick specific changes to only commit the changes you want.
+4. Click `Commit`.
+
+![](https://i.imgur.com/MvGQKT9.png)
+
+To create a new branch,
+
+Once you have made a few commits and are ready to push them to your fork repository, you can do any of the following:
+- Use the VSC Menu `VSC` -> `Git` -> `Push`
+- Use the VSC popup `alt-\ ` -> `Push`
+- Use the Push shortcut: `CTRL+SHIFT+K`
+
+You should see a window listing commits and changed files.
+Click `Push` when you're ready and it'll submit the commits to your forked repository.
+
+#### With console:
+
+When you create a commit, you will either want to add certain files for that commit, or apply all changes to every file.
+
+It's recommended to add only the files that you want to apply changes to before committing, rather than committing everything tracked. This brings more caution, and allows you to check first each file is relevant to the PR you're working on.
+
+To only add certain files, you will need to do that first before committing:
+```bash
+# create/use feature branch
+git checkout some_new_feature
+
+# stage a single file
+git add my_file.py
+
+# stage multiple files in one command
+git add file_one.py file_two.py file_three.py
+
+# stage all files in current directory
+git add .
+
+# remove a file from staging
+git rm --cached wrong_file.py
+
+# finally commit
+git commit -m "commit message here"
+```
+
+If you're absolutely sure that the tracked changes are all within scope of your PR, you can try using the commit all command.
+To commit all changes across all tracked files, just use the `-a` flag:
+```bash
+git commit -a -m "commit message here"
+```
+
+### Pull Requests (PRs)
+
+Go to the [Pull requests](https://github.com/python-discord/seasonalbot/pulls) and click the green New Pull Request button:
+
![](https://i.imgur.com/fB4a2wQ.png)
-Now you should hit `Compare across forks` then on the third dropdown select your fork (it will be `your username/seasonalbot`) then hit Create Pull request.
-1[](https://i.imgur.com/N2X9A9v.png)
-Now to tell other people what your PR does
-1. Title - be concise and informative
-2. Description - write what the PR changes as well as what issues it relates to
-3. Hit `Create pull request`
-![](https://i.imgur.com/OjKYdsL.png)
-#### 7. Wait & further reading
-At this point your PR will either be accepted or a maintainer might request some changes.
+Click `Compare across forks`.
+Change the `head fork` dropdown box to your fork (`your_username/seasonalbot`).
+Click `Create pull request`.
+
+![](https://i.imgur.com/N2X9A9v.png)
+
+In the PR details, put a concise and informative title.
+Write a description for the PR regarding the changes and any related issues.
+Ensure `Allow edits from maintainers.` has been ticked.
+Click `Create pull request`.
+
+![](https://i.imgur.com/yMJVMNj.png)
-You can now read up some more on [Git](https://try.github.io), [PyCharm](https://www.jetbrains.com/help/pycharm/quick-start-guide.html),
-or learn more about Python and Discord: [discord.py rewrite](https://discordpy.readthedocs.io/en/rewrite)
+A maintainer or other participants may comment or review your PR, and may suggest changes. Communicate in the PR ticket and try ensure you respond timely on any questions or feedback you might get to prevent a PR being marked stale.
+After a successful review by a maintainer, the PR may be merged successfully, and the new feature will deploy to the live bot within 10 minutes usually.
-### Without PyCharm
-The process above can be completed without PyCharm however it will be necessary to learn how to use Git, Pipenv and Environment variables.
+### Further Reading
-You can find tutorials for the above below:
-- Git: [try.github](http://try.github.io/)
-- Pipenv [Pipenv.readthedocs](https://pipenv.readthedocs.io)
-- Environment Variables: [youtube](https://youtu.be/bEroNNzqlF4?t=27)
+- [Git](https://try.github.io)
+- [PyCharm](https://www.jetbrains.com/help/pycharm/quick-start-guide.html)
+- [Pipenv](https://pipenv.readthedocs.io)
+- [discord.py rewrite](https://discordpy.readthedocs.io/en/rewrite)
+- [Environment Variables (youtube)](https://youtu.be/bEroNNzqlF4?t=27)
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 2b43cc57..15fac3b9 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -65,4 +65,3 @@ jobs:
inputs:
scriptPath: scripts/deploy-azure.sh
- args: '$(SALTAPI_TARGET) $(SALTAPI_USER) $(SALTAPI_PASS) $(SALTAPI_URL)'
diff --git a/bot/bot.py b/bot/bot.py
index 2df550dc..12291a5f 100644
--- a/bot/bot.py
+++ b/bot/bot.py
@@ -5,6 +5,7 @@ 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
@@ -61,3 +62,10 @@ 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:
+ super().on_command_error(context, exception)
diff --git a/bot/constants.py b/bot/constants.py
index 917b82af..0ffc9081 100644
--- a/bot/constants.py
+++ b/bot/constants.py
@@ -61,8 +61,10 @@ class Client(NamedTuple):
class Colours:
soft_red = 0xcd6d6d
soft_green = 0x68c290
+ bright_green = 0x01d277
dark_green = 0x1f8b4c
orange = 0xe67e22
+ pink = 0xcf84e0
class Emojis:
@@ -70,6 +72,18 @@ class Emojis:
christmas_tree = "\U0001F384"
check = "\u2611"
+ terning1 = "<:terning1:431249668983488527>"
+ terning2 = "<:terning2:462339216987127808>"
+ terning3 = "<:terning3:431249694467948544>"
+ terning4 = "<:terning4:431249704769290241>"
+ terning5 = "<:terning5:431249716328792064>"
+ terning6 = "<:terning6:431249726705369098>"
+
+
+class Lovefest:
+ channel_id = int(environ.get("LOVEFEST_CHANNEL_ID", 542272993192050698))
+ role_id = int(environ.get("LOVEFEST_ROLE_ID", 542431903886606399))
+
class Hacktoberfest(NamedTuple):
channel_id = 498804484324196362
diff --git a/bot/resources/valentines/bemyvalentine_valentines.json b/bot/resources/valentines/bemyvalentine_valentines.json
new file mode 100644
index 00000000..7d5d3705
--- /dev/null
+++ b/bot/resources/valentines/bemyvalentine_valentines.json
@@ -0,0 +1,45 @@
+{
+ "valentine_poems": [
+
+ "If you were my rose,\nthen I'd be your sun,\npainting you rainbows when the rains come.\nI'd change my orbit to banish the night,\nas to keep you in my nurturing light.",
+ "If you were my world, then I'd be your moon,\nyour silent protector, a night-light in the gloom.\nOur fates intertwined, two bodies in motion through time and space,\nour dance of devotion.",
+ "If you were my island, then I'd be your sea,\ncaressing your shores, soft and gentle I'd be.\nMy tidal embrace would leave gifts on your sands,\nbut by current and storm, I'd ward your gentle lands.",
+ "If you were love's promise, then I would be time,\nyour constant companion till stars align.\nAnd though we are mere mortals,\ntrue love is divine,and my devotion eternal,\nto my one valentine.",
+ "Have I told you,\nValentine, That I’m all wrapped up in you?\nMy feelings for you bring to me A joy I never knew.\n \n You light up everything for me; In my heart you shine;\nIlluminating my whole life,\nMy darling Valentine.",
+ "My days are filled with yearning;\nMy nights are full of dreams.\nI’m always thinking of you;\nI’m in a trance, it seems.\n\n You’re all I ever wanted;\nI wish you could be mine;\nAnd so I have to ask you: Will you be my Valentine?",
+ "My Valentine, I love just you;\nMy devotion I declare.\nI’ll spend my life looking for ways To show you that I care.\n\nPlease say you feel the same for me;\nSay you’ll be forever mine;\nWe’ll share a life of happiness,\nMy treasured Valentine.",
+ "Every day with you is Valentine's Day, my love.\nEvery day is filled with romance, with love, with sharing and caring.\nEvery day I am reminded how blessed I am to have you as my,\nValentine, my sweetheart, my lover, my friend, my playmate, my companion.\nNo Valentine card, no words at all could express how much I love You,\nhow ecstatic I feel to know that you are mine.\nMy Valentine, every day,\nI'll try to show you that every day I love you more.",
+ "I lucked out when I met you, sweet thing.\nYou've brought richness to each day I exist.\nYou fill my days with the excitement of love,\nAnd you have since the moment we kissed.\nSo I celebrate Valentine's Day with you,\nWith a love that will always stay fresh and new.",
+ "You are my everything, Valentine.\nAs a desert creature longs for water, my thirst for you can never be slaked.\nIn a ho-hum day dragging on, thoughts of you bring excitement, joy and pleasure.\nAs a child opens the birthday gift he most wanted,\nI see everything I want in you.\nYou are my everything, Valentine.",
+ "My love for you is like the raging sea,\nSo powerful and deep it will forever be.\nThrough storm, wind, and heavy rain, It will withstand every pain.\nOur hearts are so pure and love so sweet.\nI love you more with every heartbeat!",
+ "A million stars up in the sky.\nOne shines brighter - I can't deny.\nA love so precious, a love so true,\na love that comes from me to you.\nThe angels sing when you are near.\nWithin your arms I have nothing to fear.\nYou always know just what to say.\nJust talking to you makes my day.\nI love you, honey, with all of my heart.\nTogether forever and never to part.",
+ "What do I do,\nWhen I'm still in love with you?\nYou walked away, Cause you didn't want to stay.\nYou broke my heart, you tore me apart.\nEvery day I wait for you, Telling myself our love was true.\nBut when you don't show, more tears start to flow.\nThat's when I know I have to let go.",
+ "When I say I love you, please believe it's true.\nWhen I say forever, know I'll never leave you.\nWhen I say goodbye, promise me you won't cry,\nBecause the day I'll be saying that will be the day I die.",
+ "Beauty isn't seen by eyes.\nIt's felt by hearts,\nRecognized by souls,\nIn the presence of love.",
+ "L is for \"laughter\" we had along the way.\nO is for \"optimism\" you gave me every day.\nV is for \"value\" of being my best friend.\nE is for \"eternity,\" a love that has no end.",
+ "If roses were red and violets could be blue,\nI'd take us away to a place just for two.\nYou'd see my true colors and all that I felt.\nI'd see that you could love me and nobody else.\nWe'd build ourselves a fortress where we'd run and play.\nYou'd be mine and I'd be yours till our dying day.\nThen I wake and realize you were never here.\nIt's all just my thoughts, my dreams, my hopes...\nBut now it's only tears!"
+
+ ],
+ "valentine_compliments": [
+
+ "To the love of my life. I’m the luckiest person on Earth because I have you! I adore you! You’ve taught me that the best thing to hold onto in life is each other. You are my sweetheart, you are my life, you are my everything.",
+ "It was a million tiny little things that, when you added them all up, they meant we were supposed to be together.",
+ "When you smile, the whole world stops and stares for a while, cause you're amazing, just the way you are.",
+ "Take love, multiply it by infinity and take it to the depths of forever... and you still have only a glimpse of how I feel for you.",
+ "When you kiss me, the world just melts away. When you hold me in your arms, I feel safe. Being in love with you has made me see how wonderful life can be. I love you.",
+ "No matter how dark the days get, you still know how to make me smile. Even after all this time, you still take my breath away.",
+ "I don't know what my future holds, but I know I want you in it. I would travel to the moon and back just to see you smile.",
+ "I may not always say it, but know that with every breath in my body and every beat of my heart I know I will always love you.",
+ "Darling I will be loving you till we're 70. And baby my heart could still fall as hard at 23. And I'm thinking about how people fall in love in mysterious ways. Maybe just the touch of a hand. Oh me, I fall in love with you every single day. And I just wanna tell you I am. So honey now. Take me into your loving arms. Kiss me under the light of a thousand stars. Place your head on my beating heart. I'm thinking out loud. Maybe we found love right where we are.",
+ "I love you. I knew it the minute I met you. I'm sorry it took so long for me to catch up. I just got stuck.",
+ "You are truly beautiful from within. I am serious! It's not just about how pretty you are (which you are, of course), but you have a beautiful heart.",
+ "If you could see yourself through my eyes, you would know how much I love you. You hold a very special place in my heart! I will love you forever!",
+ "I don’t need a thousand reasons to feel special. All I need is you to live in this world. You are the sunshine of my life.",
+ "I wish to be everything that brings a smile on your face and happiness in your heart. I want to love you like no else ever did!",
+ "Every morning of my life gives you a new reason to love you and to appreciate you for what you’ve given me. You are the one that holds the key to my heart!",
+ "Each time I look at you, I just smile to myself and think, ‘I certainly could not have done better’. You are perfect the way you are. I love you honey.",
+ "Look at the computer keyboard, U and I were placed side by side. That’s how the alphabets should be arranged because my love will never cease to exist as long as it’s you and me."
+
+ ]
+
+}
diff --git a/bot/resources/valentines/date_ideas.json b/bot/resources/valentines/date_ideas.json
new file mode 100644
index 00000000..09d31067
--- /dev/null
+++ b/bot/resources/valentines/date_ideas.json
@@ -0,0 +1,127 @@
+{
+ "ideas": [
+ {
+ "name": "Chick flick marathon",
+ "description": "This is a very basic yet highly romantic way of spending the day with your partner. Take a few days to prepare the right playlist and create a romantic atmosphere at home. You can order out some food, open a bottle of wine and cuddle up in front of the TV."
+ },
+
+ {
+ "name": "Recreate your first date",
+ "description": "Rated pretty high on the “romantic gestures scale,” this is guaranteed to impress your significant other. It requires a good memory and a bit of work to make it just right, but it is well worth it. Walk down the same streets where you first kissed and have a couple of drinks in that old coffee shop where you had your first drinks together. Don’t be afraid to spend a bit extra and add a little romantic gift into the mix."
+ },
+ {
+ "name": "Cook for your loved one",
+ "description": "Start researching good recipes for a romantic dinner for two, get the right ingredients and prepare a couple of practice dinners to make sure you’ve got your technique and presentation down pat. Cooking for someone can be a big turn on and you can create some incredible meals without spending too much money. Take it up a notch by dressing classy, decorating your dining room and presenting your partner with a printed menu."
+ },
+ {
+ "name": "Organize your very own ancient Greek party",
+ "description": "Here’s another one of those creative date ideas for the stay-at-home couple. The ancient Greek private party can be a very fun and erotic experience. You can decorate by using big bowls full of grapes, spreading some white sheets all over the place, placing some plastic vines here and there, putting up a few posters depicting Greek parties and having plenty of wine lying around. Wear nothing but light sheets or costumes and channel some of that hot-blooded Greek spirit."
+ },
+ {
+ "name": "A romantic weekend getaway in the mountains",
+ "description": "For those looking for a change of scenery and an escape from the busy city, there is nothing better than a quiet, romantic weekend in the mountains. There are plenty of fun activities like skiing that will keep you active. You can have fun making a snowman or engaging in a snowball fight, and, of course, there is plenty of privacy and great room service waiting for you back at your room."
+ },
+ {
+ "name": "Fun day at the shooting range",
+ "description": "A bit unconventional but an incredibly fun and exciting date that will get your blood pumping and put a huge smile on your faces. Try out a number of guns and have a bit of a competition. Some outdoor ranges have fully automatic rifles, which are a blast to shoot."
+ },
+ {
+ "name": "Rent an expensive sports car for a day",
+ "description": "Don’t be afraid to live large from time to time—even if you can’t afford the glamorous lifestyle of the stars, you can most definitely play pretend for a day. Put on some classy clothes and drive around town in a rented sports car. The quick acceleration and high speed are sure to provide an exhilarating experience. "
+ },
+ {
+ "name": "Go on a shopping spree together",
+ "description": "Very few things can elicit such a huge dopamine rush as a good old shopping spree. Get some new sexy lingerie, pretty shoes, a nice shirt and tie, a couple of new video games or whatever else you need or want. This is a unique chance to bond, have fun and get some stuff that you’ve been waiting to buy for a while now."
+ },
+ {
+ "name": "Hit the clubs",
+ "description": "For all the party animals out there, one of the best date ideas is to go out drinking, dancing, and just generally enjoying the night life. Visit a few good clubs, then go to an after-party and keep that party spirit going for as long as you can."
+ },
+ {
+ "name": "Spend the day driving around the city and visiting new places",
+ "description": "This one is geared towards couples who have been together for a year or two and want to experience a few new things together. Visit a few cool coffee places on the other side of town, check out interesting restaurants you’ve never been to, and consider going to see a play or having fun at a comedy club on open mic night."
+ },
+ {
+ "name": "Wine and chocolates at sunset",
+ "description": "Pick out a romantic location, such as a camping spot on a hill overlooking the city or a balcony in a restaurant with a nice view, open a bottle of wine and a box of chocolates and wait for that perfect moment when the sky turns fiery red to embrace and share a passionate kiss."
+ },
+ {
+ "name": "Ice skating",
+ "description": "There is something incredibly fun about ice skating that brings people closer together and just keeps you laughing (maybe it’s all the falling and clinging to the other person for dear life). You can have some great fun and then move on to a more private location for some alone time."
+ },
+ {
+ "name": "Model clothes for each other",
+ "description": "This one goes well when combined with a shopping spree, but you can just get a bunch of your clothes—old and new—from the closet, set up a catwalk area and then try on different combinations. You can be stylish, funny, handsome and sexy. It’s a great after-dinner show and a good way to transition into a more intimate atmosphere."
+ },
+ {
+ "name": "Dance the night away",
+ "description": "If you and your significant other are relatively good dancers, or if you simply enjoy moving your body to the rhythm of the music, then a night at salsa club or similar venue is the perfect thing for you. Alternatively, you can set up dance floor at home, play your favorite music, have a few drinks and dance like there is no tomorrow."
+ },
+ {
+ "name": "Organize a nature walk",
+ "description": "Being outside has many health benefits, but what you are going for is the beautiful view, seclusion, and the thrill of engaging in some erotic behavior out in the open. You can rent a cottage far from the city, bring some food and drinks, and explore the wilderness. This is nice way to spice things up a bit and get away from the loud and busy city life."
+ },
+ {
+ "name": "Travel abroad",
+ "description": "This takes a bit of planning in advance and may be a bit costly, but if you can afford it, there are very few things that can match a trip to France, Italy, Egypt, Turkey, Greece, or a number of other excellent locations."
+ },
+ {
+ "name": "Go on a hot-air balloon ride",
+ "description": "These are very fun and romantic—you get an incredible view, get to experience the thrill of flying, and you’ve got enough room for a romantic dinner and some champagne. Just be sure to wear warm clothes, it can get pretty cold high up in the air."
+ },
+ {
+ "name": "A relaxing day at the spa",
+ "description": "Treat your body, mind and senses to a relaxing day at the spa. You and your partner will feel fresh, comfortable, relaxed, and sexy as hell—a perfect date for the more serious couples who don’t get to spend as much time with each other as they’d like."
+ },
+ {
+ "name": "Fun times at a karaoke bar",
+ "description": "A great choice for couples celebrating their first Valentine’s Day together—it’s fairly informal and inexpensive, yet incredibly fun and allows for deeper bonding. Once you have a few drinks in your system and come to terms with the fact that you are making a complete fool of yourself, you’ll have the time of your life!"
+ },
+ {
+ "name": "Horseback riding",
+ "description": "Horseback riding is incredibly fun, especially if you’ve never done it before. And what girl doesn’t dream of a prince coming to take her on an adventure on his noble steed? It evokes a sense of nobility and is a very good bonding experience."
+ },
+ {
+ "name": "Plan a fun date night with other couples",
+ "description": "Take a break and rent a cabin in the woods, go to a mountain resort, a couple’s retreat, or just organize a huge date night at someone’s place and hang out with other couples. This is a great option for couples who have spent at least one Valentine’s Day together and allows you to customize your experience to suit your needs. Also, you can always retire early and get some alone time with your partner if you so desire."
+ },
+ {
+ "name": "Go to a concert",
+ "description": "There are a whole bunch of things happening around Valentine’s Day, so go online and check out what’s happening near you. You’ll surely be able to find tickets for a cool concert or some type of festival with live music."
+ },
+ {
+ "name": "Fancy night on the town",
+ "description": "Buy some elegant new clothes, rent a limo for the night and go to a nice restaurant, followed by a jazz club or gallery exhibition. Walk tall, make a few sarcastic quips, and have a few laughs with your partner while letting your inner snob take charge for a few hours."
+ },
+ {
+ "name": "Live out a James Bond film at a casino",
+ "description": "A beautiful lady in a simple yet sensual, form-fitting, black dress, and a strong and handsome, if somewhat stern-looking man in a fine suit walk up to a roulette table with drinks in hand and place bets at random as they smile at each other seductively. This is a scenario most of us wish to play out, but rarely get a chance. It can be a bit costly, but this is one of the most incredibly adventurous and romantic date ideas."
+ },
+ {
+ "name": "Go bungee jumping",
+ "description": "People in long-term relationships often talk about things like keeping a relationship fun and exciting, doing new things together, trusting each other and using aphrodisiacs. Well, bungee jumping is a fun, exhilarating activity you can both enjoy; it requires trust and the adrenaline rush you get from it is better than any aphrodisiac out there. Just saying, give it a shot and you won’t regret it. "
+ },
+ {
+ "name": "Play some sports",
+ "description": "Some one-on-one basketball, a soccer match against another couple, a bit of tennis, or even something as simple as a table tennis tournament (make it fun by stripping off items of clothing when you lose a game). You can combine this with date idea #13 and paint team uniforms on each other and play in the nude."
+ },
+ {
+ "name": "Take skydiving lessons",
+ "description": "An adrenaline-filled date, skydiving is sure to get your heart racing like crazy and leave you with a goofy grin for the rest of the day. You can offset all the excitement by ending the day with a quiet dinner at home."
+ },
+ {
+ "name": "Go for some paintball",
+ "description": "Playing war games is an excellent way to get your body moving, focus on some of that hand-eye-coordination, and engage your brain in coming up with tactical solutions in the heat of the moment. It is also a great bonding experience, adrenaline-fueled fun, and role-playing all wrapped into one. And when you get back home, you can always act out the wounded soldier scenario."
+ },
+ {
+ "name": "Couples’ Yoga",
+ "description": "Getting up close, hot, and sweaty? Sounds like a Valentine’s Day movie to me. By signing up with your partner for a couples’ yoga class, you can sneak in a workout while getting some face-to-face time with your date.This type of yoga focuses on poses that can be done with a partner, such as back-to-back bends, assisted stretches, and face-to-face breathing exercises. By working out together, you strengthen your bond while stretching away the stress of the week. Finish the date off by heading to the juice bar for a smoothie, or indulging in healthy salads for two. Expect to spend around $35 per person, or approximately $50 to $60 per couple."
+ },
+ {
+ "name": "Volunteer Together",
+ "description": "Getting your hands dirty for a good cause might not be the first thing that pops into your mind when you think “romance,” but there’s something to be said for a date that gives you a glimpse of your partner’s charitable side. Consider volunteering at an animal rescue, where you might be able to play with pups or help a few lovebirds pick out their perfect pet. Or, sign up to visit the elderly at a care center, where you can be a completely different kind of Valentine for someone in need."
+ }
+ ]
+}
+
+
diff --git a/bot/resources/valentines/love_matches.json b/bot/resources/valentines/love_matches.json
new file mode 100644
index 00000000..8d50cd79
--- /dev/null
+++ b/bot/resources/valentines/love_matches.json
@@ -0,0 +1,58 @@
+{
+ "0": {
+ "titles": [
+ "\ud83d\udc94 There's no real connection between you two \ud83d\udc94"
+ ],
+ "text": "The chance of this relationship working out is really low. You can get it to work, but with high costs and no guarantee of working out. Do not sit back, spend as much time together as possible, talk a lot with each other to increase the chances of this relationship's survival."
+ },
+ "5": {
+ "titles": [
+ "\ud83d\udc99 A small acquaintance \ud83d\udc99"
+ ],
+ "text": "There might be a chance of this relationship working out somewhat well, but it is not very high. With a lot of time and effort you'll get it to work eventually, however don't count on it. It might fall apart quicker than you'd expect."
+ },
+ "20": {
+ "titles": [
+ "\ud83d\udc9c You two seem like casual friends \ud83d\udc9c"
+ ],
+ "text": "The chance of this relationship working is not very high. You both need to put time and effort into this relationship, if you want it to work out well for both of you. Talk with each other about everything and don't lock yourself up. Spend time together. This will improve the chances of this relationship's survival by a lot."
+ },
+ "30": {
+ "titles": [
+ "\ud83d\udc97 You seem like you are good friends \ud83d\udc97"
+ ],
+ "text": "The chance of this relationship working is not very high, but its not that low either. If you both want this relationship to work, and put time and effort into it, meaning spending time together, talking to each other etc., than nothing shall stand in your way."
+ },
+ "45": {
+ "titles": [
+ "\ud83d\udc98 You two are really close aren't you? \ud83d\udc98"
+ ],
+ "text": "Your relationship has a reasonable amount of working out. But do not overestimate yourself there. Your relationship will suffer good and bad times. Make sure to not let the bad times destroy your relationship, so do not hesitate to talk to each other, figure problems out together etc."
+ },
+ "60": {
+ "titles": [
+ "\u2764 So when will you two go on a date? \u2764"
+ ],
+ "text": "Your relationship will most likely work out. It won't be perfect and you two need to spend a lot of time together, but if you keep on having contact, the good times in your relationship will outweigh the bad ones."
+ },
+ "80": {
+ "titles": [
+ "\ud83d\udc95 Aww look you two fit so well together \ud83d\udc95"
+ ],
+ "text": "Your relationship will most likely work out well. Don't hesitate on making contact with each other though, as your relationship might suffer from a lack of time spent together. Talking with each other and spending time together is key."
+ },
+ "95": {
+ "titles": [
+ "\ud83d\udc96 Love is in the air \ud83d\udc96",
+ "\ud83d\udc96 Planned your future yet? \ud83d\udc96"
+ ],
+ "text": "Your relationship will most likely work out perfect. This doesn't mean thought that you don't need to put effort into it. Talk to each other, spend time together, and you two won't have a hard time."
+ },
+ "100": {
+ "titles": [
+ "\ud83d\udc9b When will you two marry? \ud83d\udc9b",
+ "\ud83d\udc9b Now kiss already \ud83d\udc9b"
+ ],
+ "text": "You two will most likely have the perfect relationship. But don't think that this means you don't have to do anything for it to work. Talking to each other and spending time together is key, even in a seemingly perfect relationship."
+ }
+} \ No newline at end of file
diff --git a/bot/resources/valentines/valentine_facts.json b/bot/resources/valentines/valentine_facts.json
new file mode 100644
index 00000000..d2ffa980
--- /dev/null
+++ b/bot/resources/valentines/valentine_facts.json
@@ -0,0 +1,24 @@
+{
+ "whois": "Saint Valentine, officially Saint Valentine of Rome, was a widely recognized 3rd-century christian saint, commemorated on February 14. He was a priest and bishop, ministering persecuted Christians in the Roman Empire, and is associated with a tradition of courtly love since the High Middle Ages, a period commenced around the year 1000AD and lasting until around 1250AD. He was martyred and buried at a Christian cemetery on the Via Flaminia on February 14.\n\nThere are a bunch of inconsistencies in the identification of the saint, however there are evidences for 3 saints that appear in connection with February 14. One of them, Saint Valentine of Terni, is believed to be the one associated with a vision restoration miracle, which happening during his imprisonment. In that, he restored the eyesight of his jailer's daughter, and, on the evening before his execution, supposedly sent her a letter signed with 'Your Valentine' (tuum valentinum). This makes this saint the one we today associate with Saint Valentine's Day.\n\nThe artist Cicero Moraes attempted a facial reconstruction of Saint Valentine, which can be seen in the thumbnail.",
+ "titles": [
+ "\u2764 Facts \u00e1 la carte \u2764",
+ "\u2764 Would you like some cheese with your wi... facts? \u2764",
+ "\u2764 Facts to raise your pulse \u2764",
+ "\u2764 Love Facts, Episode #42 \u2764",
+ "\u2764 It's a fact not a fact, duh \u2764",
+ "\u2764 Candlelight din... facts! \u2764"
+ ],
+ "text": [
+ "The expression 'From your Valentine' derives from a legend in which Saint Valentine, imprisoned after persecution and not wanting to convert to Roman paganism, performed a miracle on Julia, his jailer Asterius's blind daughter, restoring her eyesight. On the evening before his execution, he is supposed to have written a letter to the jailers daughter, signing as 'Your Valentine' (tuum valentinum).",
+ "Valentine's Day wasn't really associated with anything romantic, until the 14th century England where it's association with romantic love had begun from within the circle of Geoffrey Chaucer, a famous english poet and author, also called 'Father of English literature' for his work. He is best known for 'The Caunterbury Tales', a collection of 24 stories, which are presented as part of a story-telling contest by a group of pilgrims on their travel from London to Canterbury.",
+ "It's only been roughly 300 years, that the Valentine's Day evolved into what we know today.",
+ "The wide usage of hearts on Valentine's day derived from a legend, in which Saint Valentine cut hearts from parchment, giving them to persecuted Christians and soldiers married by him. He did that \"to remind these man of their vows and God's love\"",
+ "In 1797, a British publisher developed \"The Young Man's Valentine Writer\", to assist young men in composing their own sentimental verses to ladies they felt attracted to.",
+ "If you've never gotten any handwritten Valentine cards, this may be due to the fact, that in the 19th century, handwritten notes have given away to mass-produced greeting cards.",
+ "In 1868, a British chocolate company called Cadbury created so called 'Fancy Boxes', which essentially were a decorated box of chocolates in the shape of a heart. This set a trend, such that these boxes were quickly associated with Valentine's Day.",
+ "Roses are red,\nviolet's are blue,\nI can't rhyme,\nbut I still do.\n\u200b\nThis poem in particular,\nit will stay forever,\nderives from The Faerie Queene,\nan epic poem you probably have never seen.\n\n\"She bath'd with roses red, and violets blew,\nAnd all the sweetest flowres, that in the forrest grew.\"\n\nThese verses, with most immense sway,\nlead to the poem we still hear today.",
+ "The earliest Valentine poem known is a rondeau, a form of medieval/renaissance French poetry, composed by Charles, Duke of Orl\u00e9ans to his wife:\n\n\"Je suis desja d'amour tann\u00e9,\nMa tres doulce Valentin\u00e9e\"",
+ "There's a form of cryptological communication called 'Floriography', in which you communicate through flowers. Meaning has been attributed to flowers for thousands of years, and some form of floriography has been practiced in traditional cultures throughout Europe, Asia, and Africa. Here are some meanings for roses you might want to take a look at, if you plan on gifting your loved one a bouquet of roses on Valentine's Day:\n\u200b\nRed: eternal love\nPink: young, developing love\nWhite: innocence, fervor, loyalty\nOrange: happiness, security\nViolet: love at first sight\nBlue: unfulfilled longing, quiet desire\nYellow: friendship, jealousy, envy, infidelity\nBlack: unfulfilled longing, quiet desire, grief, hatred, misfortune, death",
+ "Traditionally, young girls in the U.S. and the U.K. believed they could tell what type of man they would marry depending on the type of bird they saw first on Valentine's Day. If they saw a blackbird, they would marry a clergyman, a robin redbreast indicated a sailor, and a goldfinch indicated a rich man. A sparrow meant they would marry a farmer, a bluebird indicated a happy man, and a crossbill meant an argumentative man. If they saw a dove, they would marry a good man, but seeing a woodpecker meant they would not marry at all."
+ ]
+} \ No newline at end of file
diff --git a/bot/resources/valentines/zodiac_compatibility.json b/bot/resources/valentines/zodiac_compatibility.json
new file mode 100644
index 00000000..4e337714
--- /dev/null
+++ b/bot/resources/valentines/zodiac_compatibility.json
@@ -0,0 +1,262 @@
+{
+ "aries":[
+ {
+ "Zodiac" : "Sagittarius",
+ "description" : "The Archer is one of the most compatible signs Aries should consider when searching out relationships that will bear fruit. Sagittarians share a certain love of freedom with Aries that will help the two of them conquer new territory together.",
+ "compatibility_score" : "87%"
+ },
+ {
+ "Zodiac" : "Leo",
+ "description" : "Leo is the center of attention and typically the life of the party, but can also be courageous, bold, and powerful. These two signs share a gregarious nature that helps them walk life's path in sync. They may vie occasionally to see which one leads the way, but all things considered, they share a great capacity for compatibility.",
+ "compatibility_score" : "83%"
+ },
+ {
+ "Zodiac" : "Aquarius",
+ "description" : "Aquarius' need for personal space dovetails nicely with Aries' love of freedom. This doesn't mean that the Ram and the Water Bearer can't forge a deep bond; in fact, quite the opposite. Their mutual respect for one another's needs and space leaves room for both to grow on their own and together.",
+ "compatibility_score" : "68%"
+ },
+ {
+ "Zodiac" : "Gemini",
+ "description" : "The Twins are known for their adaptability, a trait that easily follows Aries' need to lead their closest companions down new paths. Geminis are also celebrated for their personal charm and intellect, traits the discerning Ram is also capable of fully appreciating.",
+ "compatibility_score" : "74%"
+ }
+ ],
+ "taurus":[
+ {
+ "Zodiac" : "Virgo",
+ "description" : "Although these signs have their set of differences, the Virgo Taurus compatibility is usually pretty strong. This is because both the signs want the same thing ultimately and have generally synchronous ways of reaching those points. This helps them complement each other and create a healthy relationship between them.",
+ "compatibility_score" : "73%"
+ },
+ {
+ "Zodiac" : "Capricorn",
+ "description" : "A great compatibility is seen in this match as far as the philosophical and spiritual aspects of life are concerned. Taurus and Capricorn have a practical approach towards life. The ambitions and calmness of the Goat will attract the Bull, who will attract the former with his strong determination.The compatibility between these two zodiac signs is at the greatest height due to their mutual understanding, faith and consonance.",
+ "compatibility_score" : "89%"
+ },
+ {
+ "Zodiac" : "Pisces",
+ "description" : "This relationship will survive the test of time if both parties involved have unbreakable trust in each other and nurture that connection they have painstakingly built over the years. They must remember to be honest and committed to their partner through all times.If natural communication flows between them like clockwork, this will be a beautiful love story with a prominent tag of 'happily-ever-after' pinned right to it!",
+ "compatibility_score" : "88%"
+ },
+ {
+ "Zodiac" : "Cancer",
+ "description" : "This is a peaceful union of two reliable and kind souls. It is one of the strong zodiac pairings in terms of compatibility, and certainly has a real chance of lasting a lifetime in astrological charts. If Taurus man and Cancer woman and vice-versa are mature enough to handle the occasional friction, which they usually are, their bond will transcend all boundaries and is likely to grow in strength through the years.",
+ "compatibility_score" : "91%"
+ }
+ ],
+ "gemini":[
+ {
+ "Zodiac" : "Aries",
+ "description" : "The theorem of astrology says that Aries and Gemini have a zero tolerance for boredom and will at once get rid of anything dull. An Arian will let a Geminian enjoy his personal freedom and the Gemini will respect his individuality.",
+ "compatibility_score" : "74%"
+ },
+ {
+ "Zodiac" : "Leo",
+ "description" : "Gemini and Leo love match that can go through extravagant heights and crushing lows, but one is sure to leave both of them breathless. This is a pair that forms one of the most exciting relationships of the zodiac chart, but the converse is equally true; they are vulnerable to experiencing terrible lows.",
+ "compatibility_score" : "82%"
+ },
+ {
+ "Zodiac" : "Libra",
+ "description" : "It is relatively easier for them to adjust to each other as while they might have different outlooks to some things in life, they rarely collide or crash against one another. Hence, it is easy for them to go forward in harmony, forming a formidable bond in the process.",
+ "compatibility_score" : "78%"
+ },
+ {
+ "Zodiac" : "Aquarius",
+ "description" : "This is one of the most successful pairs of the zodiac chart.If they manage to smooth this solitary wrinkle, this will be a beautiful union for both the parties involved. It will turn into a gift that will keep giving happiness, contentment and encouragement to the air signs.",
+ "compatibility_score" : "91%"
+ }
+ ],
+ "cancer":[
+ {
+ "Zodiac" : "Taurus",
+ "description" : "The Cancer Taurus zodiac relationship compatibility is strong because of their mutual love for safety, stability, and comfort. Their mutual understanding will always be powerful, which will be the pillar of strength of their relationship.",
+ "compatibility_score" : "91%"
+ },
+ {
+ "Zodiac" : "Scorpio",
+ "description" : "The Cancer man and Scorpio woman and vice-versa connect on an intuitive level and will find refuge in each other from the outset of the relationship.",
+ "compatibility_score" : "79%"
+ },
+ {
+ "Zodiac" : "Virgo",
+ "description" : "This is a match made in heaven! Very few zodiac combinations work as well as Cancer and Virgo. Their blissful union is one which is generally filled with memories of happiness, contentment, loyalty, and harmony. They will build an extremely strong bond with each other that can pass all the tests of time and emerge stronger after every challenge that is thrown in its way!",
+ "compatibility_score" : "77%"
+ }
+ ],
+ "leo":[
+ {
+ "Zodiac" : "Aries",
+ "description" : "A Leo is generous and an Arian is open to life. Sharing the same likes and dislikes, they both crave for fun, romance and excitement. A Leo respects an Arian's need for freedom because an Arian does not interfere much in the life of a Leo. Aries will love the charisma and ideas of the Leo.",
+ "compatibility_score" : "83%"
+ },
+ {
+ "Zodiac" : "Gemini",
+ "description" : "This is one of the most compatible pairings in the zodiac chart and if the minor friction is handled with maturity by both the parties, the Leo and Gemini compatibility relationship can last a lifetime. A lifetime, which will be filled with love, happiness, and success.",
+ "compatibility_score" : "82%"
+ },
+ {
+ "Zodiac" : "Libra",
+ "description" : "he thing that attributes for the greater compatibility of this love match is that both the signs like to complement each other. Leo and Libra love to attend social gatherings. They like to engage themselves in romance and act as lovers. This love match is strong enough to face adverse situations due to their opposite individual characters. This pair likes to enjoy candlelit dinners, long drives, dancing",
+ "compatibility_score" : "75%"
+ },
+ {
+ "Zodiac" : "Sagittarius",
+ "description" : "The key for this relationship to work is that both Leo star sign and Sagittarius star sign natives should know where they want it to go in the long run. If they are ready for a long-time commitment and are mutually aware of the same, they will form a brilliant and buoyant bond together. It can last for a lifetime and will be characterized by the excitement, love, happiness, and unending loyalty that Leo and Sagittarius will bring to each other’s life.",
+ "compatibility_score" : "75%"
+ }
+ ],
+ "virgo":[
+ {
+ "Zodiac" : "Taurus",
+ "description" : "Although these signs have their set of differences, the Virgo Taurus compatibility is usually pretty strong. This is because both the signs want the same thing ultimately and have generally synchronous ways of reaching those points. This helps them complement each other and create a healthy relationship between them.",
+ "compatibility_score" : "73%"
+ },
+ {
+ "Zodiac" : "Cancer",
+ "description" : "This is one of the few zodiac compatibilities that look great not only on paper, but also in reality. The shared outlook that Cancer and Virgo have towards life makes their relationship a positive, reliable, and beautiful entity for both the sides.",
+ "compatibility_score" : "77%"
+ },
+ {
+ "Zodiac" : "Scorpio",
+ "description" : "The Virgo Scorpio love match definitely has something unique to offer to the zodiac world, and to both the parties involved in this pair. If they manage to control their negative traits, this relationship truly has the chance to survive everything life throws at it.",
+ "compatibility_score" : "76%"
+ },
+ {
+ "Zodiac" : "Capricorn",
+ "description" : "The compatibility of this match is quite well as both rely on each other and possess the maturity to understand their individual outlook. Capricorn plans in advance and steadily reaches his goal and a Virgo has faith in his abilities and skills.",
+ "compatibility_score" : "77%"
+ }
+ ],
+ "libra":[
+ {
+ "Zodiac" : "Leo",
+ "description" : "Libra and Leo love match can work well for both the partners and truly help them learn from each other and grow individually, as well as together. Libra and Leo, when in the right frame of mind, form a formidable couple that attracts admiration and respect everywhere it goes.",
+ "compatibility_score" : "75%"
+ },
+ {
+ "Zodiac" : "Aquarius",
+ "description" : "Libra Aquarius Relationship has a good chance of working because of the immense understanding that both the zodiac signs have for each other. They are likely to fight less as Libra believes in avoiding conflict and Aquarius usually is an extremely open-minded and non-judgmental sign.",
+ "compatibility_score" : "68%"
+ },
+ {
+ "Zodiac" : "Gemini",
+ "description" : "It is relatively easier for them to adjust to each other as while they might have different outlooks to some things in life, they rarely collide or crash against one another. Hence, it is easy for them to go forward in harmony, forming a formidable bond in the process.",
+ "compatibility_score" : "78%"
+ },
+ {
+ "Zodiac" : "sagittarius",
+ "description" : "If Sagittarius and Libra want to make it with each other, they must give themselves the time to understand each other fully. Rushing through the relationship will only need to problems in the future and that can be avoided if both the parties are careful not to get too ahead of themselves at any point.",
+ "compatibility_score" : "71%"
+ }
+ ],
+ "scorpio":[
+ {
+ "Zodiac" : "Cancer",
+ "description" : "This union is not unusual, but will take a fair share of work in the start. A strong foundation of clear cut communication is mandatory to make this a loving and stress free relationship!",
+ "compatibility_score" : "79%"
+ },
+ {
+ "Zodiac" : "Virgo",
+ "description" : "Very rarely does it so happen that a pairing can be so perfect and perfectly horrid at the same time. As such, it is imperative that the individuals involved take time before they jump into a serious commitment. Knowing these two zodiacs, commitments last for a lifetime.",
+ "compatibility_score" : "76%"
+ },
+ {
+ "Zodiac" : "Capricorn",
+ "description" : "The relationship of Scorpio and Capricorn can be inspiring for both partners to search for the truth, dig up under their family tree and deal with any unresolved karma and debt. They are both deep and don’t take things lightly, and this will help them build a strong foundation for a relationship that can last for a long time.",
+ "compatibility_score" : "72%"
+ },
+ {
+ "Zodiac" : "Pisces",
+ "description" : "Scorpio-Pisces is one of the most compatible signs of the zodiac calendar. They have a gamut of intense emotions that are consistently in sync with each other. Only if the Scorpion learns to let it go once in a while and the Pisces develops a sense of self-assurance, it will be a successful partnership that will meet its short-term as well as long-term goals.",
+ "compatibility_score" : "81%"
+ }
+ ],
+ "sagittarius":[
+ {
+ "Zodiac" : "Aries",
+ "description" : "Sagittarius and Aries can make a very compatible pair. Their relationship will have a lot of passion, enthusiasm, and energy. These are very good traits to make their relationship deeper and stronger. Both Aries and Sagittarius will enjoy each other's company and their energy level rises as the relationship grows. Both will support and help in fighting hardships and failures.",
+ "compatibility_score" : "87%"
+ },
+ {
+ "Zodiac" : "Leo",
+ "description" : "While there might be a few blips in the Sagittarius Leo relationship, they are certainly one of the most compatible couples when it comes to astrological studies. They can bring the best out of each other and shine in the light of their positive all exciting relationship.",
+ "compatibility_score" : "75%"
+ },
+ {
+ "Zodiac" : "Libra",
+ "description" : "The connectivity between Sagittarians and Librans is amazing. Librans are inclined towards maintaining balance and harmony. Sagittarians are intelligent and fun loving. But, Librans have a strange trait of transiting from one phase of emotion to other.",
+ "compatibility_score" : "71%"
+ },
+ {
+ "Zodiac" : "Aquarius",
+ "description" : "This is a combination where the positives outweigh the negatives by a considerable margin. As long as this pair builds and retains their passion for each other in the relationship, it will be a hit that can pass every test that is thrown at it.The Sagittarius Aquarius compatibility is one of a kind and can go the distance in most cases.",
+ "compatibility_score" : "83%"
+ }
+ ],
+ "capricorn":[
+ {
+ "Zodiac" : "Taurus",
+ "description" : "This is one of the most grounded and reliable bonds of the zodiac chart. If Capricorn and Taurus do find a way to handle their minor issues, they have a good chance of making it together and that too, in a happy, peaceful, and healthy relationship.",
+ "compatibility_score" : "89%"
+ },
+ {
+ "Zodiac" : "Virgo",
+ "description" : "Virgo and Capricorn will connect to each other on a visceral level and will find enduring stability in their relationship. Since this bond is something they can rely on, they tend to ease up individually and thus, are likely to be more relaxed and happier in life.",
+ "compatibility_score" : "77%"
+ },
+ {
+ "Zodiac" : "Scorpio",
+ "description" : "Though they have a couple of blips in the relationship, the combination of Scorpion and Capricorn is a match with powerful compatibility. They will shine with each other, develop each other, and build a beautiful bond with each other. If and when the two decide to commit to each other, their relationship is one that is likely to go the distance nine times out of ten!",
+ "compatibility_score" : "70%"
+ },
+ {
+ "Zodiac" : "Pisces",
+ "description" : "Pisces and Capricorn signs have primary difference of qualities, but their union is aided seamlessly due to that very discrepancy of their personas. It is a highly compatible relationship that can go a long way with mutual respect, amicable understanding and immense trust between the concerned parties.",
+ "compatibility_score" : "76%"
+ }
+ ],
+ "aquarius":[
+ {
+ "Zodiac" : "Aries",
+ "description" : "The relationship of Aries and Aquarius is very exciting, adventurous and interesting. They will enjoy each other's company as both of them love fun and freedom.This is a couple that lacks tenderness. They are not two brutes who let their relationship fade as soon as their passion does.",
+ "compatibility_score" : "72%"
+ },
+ {
+ "Zodiac" : "Gemini",
+ "description" : "If you want to see a relationship that is full of conversation, ideas and \"eureka moments\", you observe the Aquarius Gemini relationship. They will enjoy a camaraderie that you won't find in most zodiac relationships.",
+ "compatibility_score" : "85%"
+ },
+ {
+ "Zodiac" : "Libra",
+ "description" : "The couple will share a lot of similar qualities and will enjoy the relationship. Both of them love to socialize with people and to make new friends.",
+ "compatibility_score" : "68%"
+ },
+ {
+ "Zodiac" : "Sagittarius",
+ "description" : "These two will get together when it is time for both of them to go through a change in their lives or leave a partner they feel restricted with. Their relationship is often a shiny beacon to everyone around them because it gives priority to the future and brings hope of a better time.",
+ "compatibility_score" : "83%"
+ }
+ ],
+ "pisces":[
+ {
+ "Zodiac" : "Taurus",
+ "description" : "This relationship will survive the test of time if both parties involved have unbreakable trust in each other and nurture that connection they have painstakingly built over the years. They must remember to be honest and committed to their partner through all times.If natural communication flows between them like clockwork, this will be a beautiful love story with a prominent tag of ‘happily-ever-after’ pinned right to it!",
+ "compatibility_score" : "88%"
+ },
+ {
+ "Zodiac" : "Cancer",
+ "description" : "The Pisces Cancer union is a beautiful one. Both zodiacs are normally blessed with appealing looks and attractive minds. They are both prone to be drawn to each other. Mostly, for very valid reasons. However, both the zodiac star signs need to make sure that they do not let their love overpower them.",
+ "compatibility_score" : "72%"
+ },
+ {
+ "Zodiac" : "Scorpio",
+ "description" : "Though there are a few problems that might test this relationship, these star sign compatibility of two water signs has many features that can help it survive. Pisces and Scorpio are both passionate in their own ways and thus, end up striking the perfect rhythm with each other.If and when they decide to commit to the relationship, they can script a special story of camaraderie and bliss with each other.",
+ "compatibility_score" : "81%"
+ },
+ {
+ "Zodiac" : "Capricorn",
+ "description" : "A relationship between Capricorn and Pisces tells a story about possibilities of inspiration. If someone like Capricorn can be pulled into a crazy love story, exciting and unpredictable, this must be done by Pisces. In return, Capricorn will offer their Pisces partner stability, peace and some rest from their usual emotional tornadoes. There is a fine way in which Capricorn can help Pisces be more realistic and practical, while feeling more cheerful and optimistic themselves.",
+ "compatibility_score" : "76%"
+ }
+ ]
+
+} \ No newline at end of file
diff --git a/bot/seasons/christmas/adventofcode.py b/bot/seasons/christmas/adventofcode.py
index f0e4f0da..a926a6cb 100644
--- a/bot/seasons/christmas/adventofcode.py
+++ b/bot/seasons/christmas/adventofcode.py
@@ -29,7 +29,8 @@ def is_in_advent() -> bool:
Utility function to check if we are between December 1st
and December 25th.
"""
- return datetime.now(EST).day in range(1, 26) and datetime.now(EST).month == 12
+ # Run the code from the 1st to the 24th
+ return datetime.now(EST).day in range(1, 25) and datetime.now(EST).month == 12
def time_left_to_aoc_midnight() -> Tuple[datetime, timedelta]:
@@ -179,6 +180,14 @@ class AdventOfCode:
"""
Return time left until next day
"""
+ if not is_in_advent():
+ datetime_now = datetime.now(EST)
+ december_first = datetime(datetime_now.year + 1, 12, 1, tzinfo=EST)
+ delta = december_first - datetime_now
+ await ctx.send(f"The Advent of Code event is not currently running. "
+ f"The next event will start in {delta.days} days.")
+ return
+
tomorrow, time_left = time_left_to_aoc_midnight()
hours, minutes = time_left.seconds // 3600, time_left.seconds // 60 % 60
diff --git a/bot/seasons/evergreen/error_handler.py b/bot/seasons/evergreen/error_handler.py
index 6de35e60..47e18a31 100644
--- a/bot/seasons/evergreen/error_handler.py
+++ b/bot/seasons/evergreen/error_handler.py
@@ -42,9 +42,12 @@ class CommandErrorHandler:
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'
return await ctx.send(
"This command is on cooldown,"
- f" please retry in {math.ceil(error.retry_after)}s."
+ f" please retry in {time_remaining}."
)
if isinstance(error, commands.DisabledCommand):
logging.debug(
diff --git a/bot/seasons/evergreen/fun.py b/bot/seasons/evergreen/fun.py
new file mode 100644
index 00000000..4da01dd1
--- /dev/null
+++ b/bot/seasons/evergreen/fun.py
@@ -0,0 +1,38 @@
+import logging
+import random
+
+from discord.ext import commands
+
+from bot.constants import Emojis
+
+log = logging.getLogger(__name__)
+
+
+class Fun:
+ """
+ A collection of general commands for fun.
+ """
+
+ def __init__(self, bot):
+ self.bot = bot
+
+ @commands.command()
+ async def roll(self, ctx, num_rolls: int = 1):
+ """
+ Outputs a number of random dice emotes (up to 6)
+ """
+ output = ""
+ if num_rolls > 6:
+ num_rolls = 6
+ elif num_rolls < 1:
+ output = ":no_entry: You must roll at least once."
+ for _ in range(num_rolls):
+ terning = f"terning{random.randint(1, 6)}"
+ output += getattr(Emojis, terning, '')
+ await ctx.send(output)
+
+
+# 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")
diff --git a/bot/seasons/valentines/__init__.py b/bot/seasons/valentines/__init__.py
new file mode 100644
index 00000000..f1489cf9
--- /dev/null
+++ b/bot/seasons/valentines/__init__.py
@@ -0,0 +1,19 @@
+from bot.constants import Colours
+from bot.seasons import SeasonBase
+
+
+class Valentines(SeasonBase):
+ """
+ Love is in the air! We've got a new icon and set of commands for the season of love.
+
+ Get yourself into the bot-commands channel and check out the new features!
+ """
+ name = "valentines"
+ bot_name = "Tenderbot"
+ greeting = "Get loved-up!"
+
+ start_date = "01/02"
+ end_date = "01/03"
+
+ colour = Colours.pink
+ icon = "/logos/logo_seasonal/valentines/loved_up.png"
diff --git a/bot/seasons/valentines/be_my_valentine.py b/bot/seasons/valentines/be_my_valentine.py
new file mode 100644
index 00000000..0046ceb4
--- /dev/null
+++ b/bot/seasons/valentines/be_my_valentine.py
@@ -0,0 +1,241 @@
+import logging
+import random
+import typing
+from json import load
+from pathlib import Path
+
+import discord
+from discord.ext import commands
+from discord.ext.commands.cooldowns import BucketType
+
+from bot.constants import Client, Colours, Lovefest
+
+log = logging.getLogger(__name__)
+
+HEART_EMOJIS = [":heart:", ":gift_heart:", ":revolving_hearts:", ":sparkling_heart:", ":two_hearts:"]
+
+
+class BeMyValentine:
+ """
+ A cog that sends valentines to other users !
+ """
+
+ def __init__(self, bot):
+ self.bot = bot
+ self.valentines = self.load_json()
+
+ @staticmethod
+ def load_json():
+ p = Path('bot', 'resources', 'valentines', 'bemyvalentine_valentines.json')
+ with p.open() as json_data:
+ valentines = load(json_data)
+ return valentines
+
+ @commands.group(name="lovefest", invoke_without_command=True)
+ async def lovefest_role(self, ctx):
+ """
+ You can have yourself the lovefest role or remove it.
+ The lovefest role makes you eligible to receive anonymous valentines from other users.
+
+ 1) use the command \".lovefest sub\" to get the lovefest role.
+ 2) use the command \".lovefest unsub\" to get rid of the lovefest role.
+ """
+ await ctx.invoke(self.bot.get_command("help"), "lovefest")
+
+ @lovefest_role.command(name="sub")
+ async def add_role(self, ctx):
+ """
+ This command adds the lovefest role.
+ """
+ user = ctx.author
+ role = discord.utils.get(ctx.guild.roles, id=Lovefest.role_id)
+ if Lovefest.role_id not in [role.id for role in ctx.message.author.roles]:
+ await user.add_roles(role)
+ await ctx.send("The Lovefest role has been added !")
+ else:
+ await ctx.send("You already have the role !")
+
+ @lovefest_role.command(name="unsub")
+ async def remove_role(self, ctx):
+ """
+ This command removes the lovefest role.
+ """
+ user = ctx.author
+ role = discord.utils.get(ctx.guild.roles, id=Lovefest.role_id)
+ if Lovefest.role_id not in [role.id for role in ctx.message.author.roles]:
+ await ctx.send("You dont have the lovefest role.")
+ else:
+ await user.remove_roles(role)
+ await ctx.send("The lovefest role has been successfully removed !")
+
+ @commands.cooldown(1, 1800, BucketType.user)
+ @commands.group(name='bemyvalentine', invoke_without_command=True)
+ async def send_valentine(self, ctx, user: typing.Optional[discord.Member] = None, *, valentine_type=None):
+ """
+ This command sends valentine to user if specified or a random user having lovefest role.
+
+ syntax: .bemyvalentine [user](optional) [p/poem/c/compliment/or you can type your own valentine message]
+ (optional)
+
+ example: .bemyvalentine (sends valentine as a poem or a compliment to a random user)
+ example: .bemyvalentine Iceman#6508 p (sends a poem to Iceman)
+ example: .bemyvalentine Iceman Hey I love you, wanna hang around ? (sends the custom message to Iceman)
+ NOTE : AVOID TAGGING THE USER MOST OF THE TIMES.JUST TRIM THE '@' when using this command.
+ """
+
+ if ctx.guild is None:
+ # This command should only be used in the server
+ msg = "You are supposed to use this command in the server."
+ return await ctx.send(msg)
+
+ if user:
+ if Lovefest.role_id not in [role.id for role in user.roles]:
+ message = f"You cannot send a valentine to {user} as he/she does not have the lovefest role!"
+ return await ctx.send(message)
+
+ if user == ctx.author:
+ # Well a user can't valentine himself/herself.
+ return await ctx.send("Come on dude, you can't send a valentine to yourself :expressionless:")
+
+ emoji_1, emoji_2 = self.random_emoji()
+ lovefest_role = discord.utils.get(ctx.guild.roles, id=Lovefest.role_id)
+ channel = self.bot.get_channel(Lovefest.channel_id)
+ valentine, title = self.valentine_check(valentine_type)
+
+ if user is None:
+ author = ctx.author
+ user = self.random_user(author, lovefest_role.members)
+ if user is None:
+ return await ctx.send("There are no users avilable to whome your valentine can be sent.")
+
+ embed = discord.Embed(
+ title=f'{emoji_1} {title} {user.display_name} {emoji_2}',
+ description=f'{valentine} \n **{emoji_2}From {ctx.author}{emoji_1}**',
+ color=Colours.pink
+ )
+ await channel.send(user.mention, embed=embed)
+
+ @commands.cooldown(1, 1800, BucketType.user)
+ @send_valentine.command(name='secret')
+ async def anonymous(self, ctx, user: typing.Optional[discord.Member] = None, *, valentine_type=None):
+ """
+ This command DMs a valentine to be given anonymous to a user if specified or a random user having lovefest role.
+
+ **This command should be DMed to the bot.**
+
+ syntax : .bemyvalentine secret [user](optional) [p/poem/c/compliment/or you can type your own valentine message]
+ (optional)
+
+ example : .bemyvalentine secret (sends valentine as a poem or a compliment to a random user in DM making you
+ anonymous)
+ example : .bemyvalentine secret Iceman#6508 p (sends a poem to Iceman in DM making you anonymous)
+ example : .bemyvalentine secret Iceman#6508 Hey I love you, wanna hang around ? (sends the custom message to
+ Iceman in DM making you anonymous)
+ """
+
+ if ctx.guild is not None:
+ # This command is only DM specific
+ msg = "You are not supposed to use this command in the server, DM the command to the bot."
+ return await ctx.send(msg)
+
+ if user:
+ if Lovefest.role_id not in [role.id for role in user.roles]:
+ message = f"You cannot send a valentine to {user} as he/she does not have the lovefest role!"
+ return await ctx.send(message)
+
+ if user == ctx.author:
+ # Well a user cant valentine himself/herself.
+ return await ctx.send('Come on dude, you cant send a valentine to yourself :expressionless:')
+
+ guild = self.bot.get_guild(id=Client.guild)
+ emoji_1, emoji_2 = self.random_emoji()
+ lovefest_role = discord.utils.get(guild.roles, id=Lovefest.role_id)
+ valentine, title = self.valentine_check(valentine_type)
+
+ if user is None:
+ author = ctx.author
+ user = self.random_user(author, lovefest_role.members)
+ if user is None:
+ return await ctx.send("There are no users avilable to whome your valentine can be sent.")
+
+ embed = discord.Embed(
+ title=f'{emoji_1}{title} {user.display_name}{emoji_2}',
+ description=f'{valentine} \n **{emoji_2}From anonymous{emoji_1}**',
+ color=Colours.pink
+ )
+ try:
+ await user.send(embed=embed)
+ except discord.Forbidden:
+ await ctx.author.send(f"{user} has DMs disabled, so I couldn't send the message. Sorry!")
+ else:
+ await ctx.author.send(f"Your message has been sent to {user}")
+
+ def valentine_check(self, valentine_type):
+ if valentine_type is None:
+ valentine, title = self.random_valentine()
+
+ elif valentine_type.lower() in ['p', 'poem']:
+ valentine = self.valentine_poem()
+ title = 'A poem dedicated to'
+
+ elif valentine_type.lower() in ['c', 'compliment']:
+ valentine = self.valentine_compliment()
+ title = 'A compliment for'
+
+ else:
+ # in this case, the user decides to type his own valentine.
+ valentine = valentine_type
+ title = 'A message for'
+ return valentine, title
+
+ @staticmethod
+ def random_user(author, members):
+ """
+ Picks a random member from the list provided in `members`, ensuring
+ the author is not one of the options.
+
+ :param author: member who invoked the command
+ :param members: list of discord.Member objects
+ """
+ if author in members:
+ members.remove(author)
+
+ return random.choice(members) if members else None
+
+ @staticmethod
+ def random_emoji():
+ EMOJI_1 = random.choice(HEART_EMOJIS)
+ EMOJI_2 = random.choice(HEART_EMOJIS)
+ return EMOJI_1, EMOJI_2
+
+ def random_valentine(self):
+ """
+ Grabs a random poem or a compliment (any message).
+ """
+ valentine_poem = random.choice(self.valentines['valentine_poems'])
+ valentine_compliment = random.choice(self.valentines['valentine_compliments'])
+ random_valentine = random.choice([valentine_compliment, valentine_poem])
+ if random_valentine == valentine_poem:
+ title = 'A poem dedicated to'
+ else:
+ title = 'A compliment for '
+ return random_valentine, title
+
+ def valentine_poem(self):
+ """
+ Grabs a random poem.
+ """
+ valentine_poem = random.choice(self.valentines['valentine_poems'])
+ return valentine_poem
+
+ def valentine_compliment(self):
+ """
+ Grabs a random compliment.
+ """
+ valentine_compliment = random.choice(self.valentines['valentine_compliments'])
+ return valentine_compliment
+
+
+def setup(bot):
+ bot.add_cog(BeMyValentine(bot))
+ log.debug("Be My Valentine cog loaded")
diff --git a/bot/seasons/valentines/lovecalculator.py b/bot/seasons/valentines/lovecalculator.py
new file mode 100644
index 00000000..4df33b93
--- /dev/null
+++ b/bot/seasons/valentines/lovecalculator.py
@@ -0,0 +1,106 @@
+import bisect
+import hashlib
+import json
+import logging
+import random
+from pathlib import Path
+from typing import Union
+
+import discord
+from discord import Member
+from discord.ext import commands
+from discord.ext.commands import BadArgument, clean_content
+
+from bot.constants import Roles
+
+log = logging.getLogger(__name__)
+
+with Path('bot', 'resources', 'valentines', 'love_matches.json').open() as file:
+ LOVE_DATA = json.load(file)
+ LOVE_DATA = sorted((int(key), value) for key, value in LOVE_DATA.items())
+
+
+class LoveCalculator:
+ """
+ A cog for calculating the love between two people
+ """
+
+ def __init__(self, bot):
+ self.bot = bot
+
+ @commands.command(aliases=('love_calculator', 'love_calc'))
+ @commands.cooldown(rate=1, per=5, type=commands.BucketType.user)
+ async def love(self, ctx, who: Union[Member, str], whom: Union[Member, str] = None):
+ """
+ Tells you how much the two love each other.
+
+ This command accepts users or arbitrary strings as arguments.
+ Users are converted from:
+ - User ID
+ - Mention
+ - name#discrim
+ - name
+ - nickname
+
+ Any two arguments will always yield the same result, though the order of arguments matters:
+ Running .love joseph erlang will always yield the same result.
+ Running .love erlang joseph won't yield the same result as .love joseph erlang
+
+ If you want to use multiple words for one argument, you must include quotes.
+ .love "Zes Vappa" "morning coffee"
+
+ If only one argument is provided, the subject will become one of the helpers at random.
+ """
+
+ if whom is None:
+ staff = ctx.guild.get_role(Roles.helpers).members
+ whom = random.choice(staff)
+
+ def normalize(arg):
+ if isinstance(arg, Member):
+ # if we are given a member, return name#discrim without any extra changes
+ arg = str(arg)
+ else:
+ # otherwise normalise case and remove any leading/trailing whitespace
+ arg = arg.strip().title()
+ # this has to be done manually to be applied to usernames
+ return clean_content(escape_markdown=True).convert(ctx, arg)
+
+ who, whom = [await normalize(arg) for arg in (who, whom)]
+
+ # make sure user didn't provide something silly such as 10 spaces
+ if not (who and whom):
+ raise BadArgument('Arguments be non-empty strings.')
+
+ # hash inputs to guarantee consistent results (hashing algorithm choice arbitrary)
+ #
+ # hashlib is used over the builtin hash() function
+ # to guarantee same result over multiple runtimes
+ m = hashlib.sha256(who.encode() + whom.encode())
+ # mod 101 for [0, 100]
+ love_percent = sum(m.digest()) % 101
+
+ # We need the -1 due to how bisect returns the point
+ # see the documentation for further detail
+ # https://docs.python.org/3/library/bisect.html#bisect.bisect
+ index = bisect.bisect(LOVE_DATA, (love_percent,)) - 1
+ # we already have the nearest "fit" love level
+ # we only need the dict, so we can ditch the first element
+ _, data = LOVE_DATA[index]
+
+ status = random.choice(data['titles'])
+ embed = discord.Embed(
+ title=status,
+ description=f'{who} \N{HEAVY BLACK HEART} {whom} scored {love_percent}%!\n\u200b',
+ color=discord.Color.dark_magenta()
+ )
+ embed.add_field(
+ name='A letter from Dr. Love:',
+ value=data['text']
+ )
+
+ await ctx.send(embed=embed)
+
+
+def setup(bot):
+ bot.add_cog(LoveCalculator(bot))
diff --git a/bot/seasons/valentines/movie_generator.py b/bot/seasons/valentines/movie_generator.py
new file mode 100644
index 00000000..b52eba7f
--- /dev/null
+++ b/bot/seasons/valentines/movie_generator.py
@@ -0,0 +1,66 @@
+import logging
+import random
+from os import environ
+from urllib import parse
+
+import discord
+from discord.ext import commands
+
+TMDB_API_KEY = environ.get("TMDB_API_KEY")
+
+log = logging.getLogger(__name__)
+
+
+class RomanceMovieFinder:
+ """
+ A cog that returns a random romance movie suggestion to a user
+ """
+
+ def __init__(self, bot):
+ self.bot = bot
+
+ @commands.command(name="romancemovie")
+ async def romance_movie(self, ctx):
+ """
+ Randomly selects a romance movie and displays information about it
+ """
+ # selecting a random int to parse it to the page parameter
+ random_page = random.randint(0, 20)
+ # TMDB api params
+ params = {
+ "api_key": TMDB_API_KEY,
+ "language": "en-US",
+ "sort_by": "popularity.desc",
+ "include_adult": "false",
+ "include_video": "false",
+ "page": random_page,
+ "with_genres": "10749"
+ }
+ # the api request url
+ request_url = "https://api.themoviedb.org/3/discover/movie?" + parse.urlencode(params)
+ async with self.bot.http_session.get(request_url) as resp:
+ # trying to load the json file returned from the api
+ try:
+ data = await resp.json()
+ # selecting random result from results object in the json file
+ selected_movie = random.choice(data["results"])
+
+ embed = discord.Embed(
+ title=f":sparkling_heart: {selected_movie['title']} :sparkling_heart:",
+ description=selected_movie["overview"],
+ )
+ embed.set_image(url=f"http://image.tmdb.org/t/p/w200/{selected_movie['poster_path']}")
+ embed.add_field(name="Release date :clock1:", value=selected_movie["release_date"])
+ embed.add_field(name="Rating :star2:", value=selected_movie["vote_average"])
+ await ctx.send(embed=embed)
+ except KeyError:
+ warning_message = "A KeyError was raised while fetching information on the movie. The API service" \
+ " could be unavailable or the API key could be set incorrectly."
+ embed = discord.Embed(title=warning_message)
+ log.warning(warning_message)
+ await ctx.send(embed=embed)
+
+
+def setup(bot):
+ bot.add_cog(RomanceMovieFinder(bot))
+ log.debug("Random romance movie cog loaded!")
diff --git a/bot/seasons/valentines/savethedate.py b/bot/seasons/valentines/savethedate.py
new file mode 100644
index 00000000..b9484be9
--- /dev/null
+++ b/bot/seasons/valentines/savethedate.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__)
+
+HEART_EMOJIS = [":heart:", ":gift_heart:", ":revolving_hearts:", ":sparkling_heart:", ":two_hearts:"]
+
+
+class SaveTheDate:
+ """
+ A cog that gives random suggestion, for a valentines date !
+ """
+
+ def __init__(self, bot):
+ self.bot = bot
+
+ @commands.command()
+ async def savethedate(self, ctx):
+ """
+ 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)
+
+
+def setup(bot):
+ bot.add_cog(SaveTheDate(bot))
+ log.debug("Save the date cog loaded")
diff --git a/bot/seasons/valentines/valentine_zodiac.py b/bot/seasons/valentines/valentine_zodiac.py
new file mode 100644
index 00000000..06c0237d
--- /dev/null
+++ b/bot/seasons/valentines/valentine_zodiac.py
@@ -0,0 +1,59 @@
+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__)
+
+LETTER_EMOJI = ':love_letter:'
+HEART_EMOJIS = [":heart:", ":gift_heart:", ":revolving_hearts:", ":sparkling_heart:", ":two_hearts:"]
+
+
+class ValentineZodiac:
+ """
+ A cog that returns a counter compatible zodiac sign to the given user's zodiac sign.
+ """
+ def __init__(self, bot):
+ self.bot = bot
+ self.zodiacs = self.load_json()
+
+ @staticmethod
+ def load_json():
+ p = Path('bot', 'resources', 'valentines', 'zodiac_compatibility.json')
+ with p.open() as json_data:
+ zodiacs = load(json_data)
+ return zodiacs
+
+ @commands.command(name="partnerzodiac")
+ async def counter_zodiac(self, ctx, zodiac_sign):
+ """
+ Provides a counter compatible zodiac sign to the given user's zodiac sign.
+ """
+ try:
+ compatible_zodiac = random.choice(self.zodiacs[zodiac_sign.lower()])
+ except KeyError:
+ return await ctx.send(zodiac_sign.capitalize() + " zodiac sign does not exist.")
+
+ emoji1 = random.choice(HEART_EMOJIS)
+ emoji2 = random.choice(HEART_EMOJIS)
+ embed = discord.Embed(
+ title="Zodic Compatibility",
+ description=f'{zodiac_sign.capitalize()}{emoji1}{compatible_zodiac["Zodiac"]}\n'
+ f'{emoji2}Compatibility meter : {compatible_zodiac["compatibility_score"]}{emoji2}',
+ color=Colours.pink
+ )
+ embed.add_field(
+ name=f'A letter from Dr.Zodiac {LETTER_EMOJI}',
+ value=compatible_zodiac['description']
+ )
+ await ctx.send(embed=embed)
+
+
+def setup(bot):
+ bot.add_cog(ValentineZodiac(bot))
+ log.debug("Valentine Zodiac cog loaded")
diff --git a/bot/seasons/valentines/whoisvalentine.py b/bot/seasons/valentines/whoisvalentine.py
new file mode 100644
index 00000000..2fe07aba
--- /dev/null
+++ b/bot/seasons/valentines/whoisvalentine.py
@@ -0,0 +1,53 @@
+import json
+import logging
+from pathlib import Path
+from random import choice
+
+import discord
+from discord.ext import commands
+
+from bot.constants import Colours
+
+log = logging.getLogger(__name__)
+
+with open(Path("bot", "resources", "valentines", "valentine_facts.json"), "r") as file:
+ FACTS = json.load(file)
+
+
+class ValentineFacts:
+ def __init__(self, bot):
+ self.bot = bot
+
+ @commands.command(aliases=('whoisvalentine', 'saint_valentine'))
+ async def who_is_valentine(self, ctx):
+ """
+ Displays info about Saint Valentine.
+ """
+ embed = discord.Embed(
+ title="Who is Saint Valentine?",
+ description=FACTS['whois'],
+ color=Colours.pink
+ )
+ embed.set_thumbnail(
+ url='https://upload.wikimedia.org/wikipedia/commons/thumb/f/f1/Saint_Valentine_-_'
+ 'facial_reconstruction.jpg/1024px-Saint_Valentine_-_facial_reconstruction.jpg'
+ )
+
+ await ctx.channel.send(embed=embed)
+
+ @commands.command()
+ async def valentine_fact(self, ctx):
+ """
+ Shows a random fact about Valentine's Day.
+ """
+ embed = discord.Embed(
+ title=choice(FACTS['titles']),
+ description=choice(FACTS['text']),
+ color=Colours.pink
+ )
+
+ await ctx.channel.send(embed=embed)
+
+
+def setup(bot):
+ bot.add_cog(ValentineFacts(bot))
diff --git a/scripts/deploy-azure.sh b/scripts/deploy-azure.sh
index abefbf6b..ad7bfb26 100755
--- a/scripts/deploy-azure.sh
+++ b/scripts/deploy-azure.sh
@@ -2,11 +2,6 @@
cd ..
-export SALTAPI_USER=$2
-export SALTAPI_PASS=$3
-export SALTAPI_URL=$4
-export SALTAPI_EAUTH=pam
-
# Build and deploy on master branch, only if not a pull request
if [[ ($BUILD_SOURCEBRANCHNAME == 'master') && ($SYSTEM_PULLREQUEST_PULLREQUESTID == '') ]]; then
echo "Building image"
@@ -14,9 +9,6 @@ if [[ ($BUILD_SOURCEBRANCHNAME == 'master') && ($SYSTEM_PULLREQUEST_PULLREQUESTI
echo "Pushing image to Docker Hub"
docker push pythondiscord/seasonalbot:latest
-
- echo "Deploying on server"
- pepper $1 state.apply docker/seasonalbot --out=no_out --non-interactive &> /dev/null
else
echo "Skipping deploy"
fi