From 261526beddbdd6ad84c9f507b6169d2a8c116f91 Mon Sep 17 00:00:00 2001 From: Ian Burgwin Date: Mon, 6 Sep 2021 18:05:13 -0700 Subject: [PATCH] Implement Markdown command system (#1001) * Implement Markdown command system This contains the first implementation of the Markdown command system, designed for the assistance module. It creates commands based on Markdown files which are easily editable compared to Python commands. Command files contain the command name and the relevant console. An example is `ntrstream.3ds.md`. A command can have multiple files for different consoles, such as `update.3ds.md` and `update.switch.md`. If one console exists for the command, it will always return the contents for that file. If multiple consoles exist, it uses the current system for checking which file to print (based on channel name or requested consoles). A Markdown file contains a header with these fields (all optional): * `title` * `url` * `author.name` (if setting author fields, this is required) * `author.url` * `author.icon-url` * `help-desc` (shows in `.help`, last one to be loaded is the one actually used) * `aliases` (merged from all files for the command) * `color` (RGB hex code, defaults to console color) * `thumbnail-url` A few commands were ported to this new system as a test. This includes: * `dsp.3ds.md` * `nospace.3ds.md` * `ntrstream.3ds.md` * `update.3ds.md` * `update.switch.md` * `tutorial/layeredfs.3ds.md` This still needs: - [ ] using format_map to subtitute things like software versions - [ ] documentation - [ ] per-command cooldown * fix flake8 issues & remove useless code * Markdown command file documentation * Add example embed image * Use correct (soon-to-be-valid) image url * support format_map for Markdown command files * fix image width * wrap header with --- lines * port more commands to markdown files * port even more commands to markdown files * mention "all" as a valid console value * port more commands to markdown files * [skip ci] undo temp cooldown change --- .gitignore | 3 +- WritingAssistanceCommands.md | 59 ++ cogs/assistance-cmds/atmosphere.switch.md | 12 + cogs/assistance-cmds/atob.3ds.md | 11 + cogs/assistance-cmds/baninfo.3ds.md | 10 + cogs/assistance-cmds/baninfo.switch.md | 8 + cogs/assistance-cmds/bigsd.3ds.md | 14 + cogs/assistance-cmds/bigsd.switch.md | 14 + cogs/assistance-cmds/cfwuses.3ds.md | 5 + cogs/assistance-cmds/cfwuses.switch.md | 15 + cogs/assistance-cmds/cfwuses.wiiu.md | 11 + cogs/assistance-cmds/ctr.3ds.md | 11 + cogs/assistance-cmds/dsp.3ds.md | 11 + cogs/assistance-cmds/formatsd.3ds.md | 11 + cogs/assistance-cmds/hekate.switch.md | 11 + cogs/assistance-cmds/ip.3ds.md | 12 + cogs/assistance-cmds/lumabug.3ds.md | 11 + cogs/assistance-cmds/lumacheck.3ds.md | 13 + cogs/assistance-cmds/modmoon.3ds.md | 7 + cogs/assistance-cmds/newver.3ds.md | 14 + cogs/assistance-cmds/newver.switch.md | 14 + cogs/assistance-cmds/nospace.3ds.md | 12 + cogs/assistance-cmds/nsupdate.switch.md | 23 + cogs/assistance-cmds/ntrstream.3ds.md | 20 + cogs/assistance-cmds/readguide.all.md | 6 + cogs/assistance-cmds/seedminer.3ds.md | 10 + cogs/assistance-cmds/stock.3ds.md | 7 + cogs/assistance-cmds/stock.switch.md | 16 + cogs/assistance-cmds/transfersd.3ds.md | 16 + cogs/assistance-cmds/transfersd.switch.md | 10 + cogs/assistance-cmds/tutorial/ap.dsi.md | 10 + cogs/assistance-cmds/tutorial/cpcheats.3ds.md | 11 + .../tutorial/gbabiosdump.3ds.md | 11 + cogs/assistance-cmds/tutorial/gbadump.all.md | 9 + .../assistance-cmds/tutorial/layeredfs.3ds.md | 11 + cogs/assistance-cmds/tutorial/ninfs.3ds.md | 7 + cogs/assistance-cmds/tutorial/obscitra.3ds.md | 11 + cogs/assistance-cmds/tutorial/plugins.3ds.md | 11 + .../tutorial/transfersave.3ds.md | 9 + cogs/assistance-cmds/update.3ds.md | 19 + cogs/assistance-cmds/update.switch.md | 10 + cogs/assistance-cmds/updateb9s.3ds.md | 10 + cogs/assistance-cmds/vguides.all.md | 14 + cogs/assistance-cmds/vguides2.all.md | 15 + cogs/assistance-cmds/what.3ds.md | 8 + cogs/assistance-cmds/what.switch.md | 8 + cogs/assistance.py | 716 ++---------------- example-embed.png | Bin 0 -> 81411 bytes utils/mdcmd.py | 218 ++++++ 49 files changed, 839 insertions(+), 666 deletions(-) create mode 100644 WritingAssistanceCommands.md create mode 100644 cogs/assistance-cmds/atmosphere.switch.md create mode 100644 cogs/assistance-cmds/atob.3ds.md create mode 100644 cogs/assistance-cmds/baninfo.3ds.md create mode 100644 cogs/assistance-cmds/baninfo.switch.md create mode 100644 cogs/assistance-cmds/bigsd.3ds.md create mode 100644 cogs/assistance-cmds/bigsd.switch.md create mode 100644 cogs/assistance-cmds/cfwuses.3ds.md create mode 100644 cogs/assistance-cmds/cfwuses.switch.md create mode 100644 cogs/assistance-cmds/cfwuses.wiiu.md create mode 100644 cogs/assistance-cmds/ctr.3ds.md create mode 100644 cogs/assistance-cmds/dsp.3ds.md create mode 100644 cogs/assistance-cmds/formatsd.3ds.md create mode 100644 cogs/assistance-cmds/hekate.switch.md create mode 100644 cogs/assistance-cmds/ip.3ds.md create mode 100644 cogs/assistance-cmds/lumabug.3ds.md create mode 100644 cogs/assistance-cmds/lumacheck.3ds.md create mode 100644 cogs/assistance-cmds/modmoon.3ds.md create mode 100644 cogs/assistance-cmds/newver.3ds.md create mode 100644 cogs/assistance-cmds/newver.switch.md create mode 100644 cogs/assistance-cmds/nospace.3ds.md create mode 100644 cogs/assistance-cmds/nsupdate.switch.md create mode 100644 cogs/assistance-cmds/ntrstream.3ds.md create mode 100644 cogs/assistance-cmds/readguide.all.md create mode 100644 cogs/assistance-cmds/seedminer.3ds.md create mode 100644 cogs/assistance-cmds/stock.3ds.md create mode 100644 cogs/assistance-cmds/stock.switch.md create mode 100644 cogs/assistance-cmds/transfersd.3ds.md create mode 100644 cogs/assistance-cmds/transfersd.switch.md create mode 100644 cogs/assistance-cmds/tutorial/ap.dsi.md create mode 100644 cogs/assistance-cmds/tutorial/cpcheats.3ds.md create mode 100644 cogs/assistance-cmds/tutorial/gbabiosdump.3ds.md create mode 100644 cogs/assistance-cmds/tutorial/gbadump.all.md create mode 100644 cogs/assistance-cmds/tutorial/layeredfs.3ds.md create mode 100644 cogs/assistance-cmds/tutorial/ninfs.3ds.md create mode 100644 cogs/assistance-cmds/tutorial/obscitra.3ds.md create mode 100644 cogs/assistance-cmds/tutorial/plugins.3ds.md create mode 100644 cogs/assistance-cmds/tutorial/transfersave.3ds.md create mode 100644 cogs/assistance-cmds/update.3ds.md create mode 100644 cogs/assistance-cmds/update.switch.md create mode 100644 cogs/assistance-cmds/updateb9s.3ds.md create mode 100644 cogs/assistance-cmds/vguides.all.md create mode 100644 cogs/assistance-cmds/vguides2.all.md create mode 100644 cogs/assistance-cmds/what.3ds.md create mode 100644 cogs/assistance-cmds/what.switch.md create mode 100644 example-embed.png create mode 100644 utils/mdcmd.py diff --git a/.gitignore b/.gitignore index e29c83f82..136beb240 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,8 @@ *.ini *.sh !dockerbuild.sh -*.log +*.log* *.json -*.png *.bin ._* .DS_Store diff --git a/WritingAssistanceCommands.md b/WritingAssistanceCommands.md new file mode 100644 index 000000000..76ef03ed5 --- /dev/null +++ b/WritingAssistanceCommands.md @@ -0,0 +1,59 @@ +## Markdown commands +Kurisu can create commands based on Markdown command files. Currently this supports the Assistance module and the tutorial group. + +These commands are placed in the `cogs/assistance-cmds` directory. Tutorial commands are placed in `cogs/assistance-cmds/tutorial`. + +### File format +A Markdown command file must follow this filename format: `commandname.console.md`. Valid console names are `3ds`, `wiiu`, `vwii`, `switch`, `wii`, `dsi`, and `all`. + +A command can have multiple responses for different consoles. This means `commandname.3ds.md` and `commandname.switch.md` can exist. The resulting command will attempt to figure out which one to display in the channel, or the console names can be provided as arguments. + +The file is split into two parts: a header and a body. The header ends once a double newline is found. The header should be wrapped with a `---` at the beginning and end. All header keys are optional. Valid header keys are: +* `title` +* `url` +* `author.name` (if setting author fields, this is required) +* `author.url` +* `author.icon-url` +* `help-desc` (shows in `.help`, last one to be loaded is the one actually used) +* `aliases` (comma-separated, merged from all files for the command) +* `color` (RGB hex code, defaults to console color) +* `thumbnail-url` + +A file can have no header keys if it starts with two newlines, but this is not recommended. + +Any Markdown syntax supported by Discord embeds is supported. Notably this includes Markdown links, which normal user messages cannot use. + +Headers define the start of a new embed field. There is only one header size, so any amount of `#` at the beginning will produce the same result. + +The body supports certain including certain variables in the format of `{variable_name}`. These will be replaced with the variable contents. Supported variables include: `nx_firmware`, `ams_ver`, `hekate_ver`, and `last_revision`. The contents are stored in `cogs/assistance.py`. + +### Example + +This file should be called `mycommand.3ds.md` and placed in `cogs/assistance-cmds`: + +```md +--- +title: Title +url: https://example.com +author.name: Author Name +author.url: https://your-site.com +author.icon-url: https://upload.wikimedia.org/wikipedia/commons/b/b8/Anagallis_arvensis_2.jpg +help-desc: Help Description +aliases: thing,stuff +color: 93A0D5 +thumbnail-url: https://upload.wikimedia.org/wikipedia/commons/e/e7/Starr_070302-5063_Merremia_tuberosa.jpg +--- + +Description goes here... + +# My header +This goes in one section! +[Example url](https://3ds.hacks.guide/faq) + +# My other header +This goes in another section! +``` + +When either `.mycommand` or the aliases `.thing` or `.stuff` are used: + + diff --git a/cogs/assistance-cmds/atmosphere.switch.md b/cogs/assistance-cmds/atmosphere.switch.md new file mode 100644 index 000000000..36acc5874 --- /dev/null +++ b/cogs/assistance-cmds/atmosphere.switch.md @@ -0,0 +1,12 @@ +--- +title: Atmosphère +url: https://github.com/Atmosphere-NX/Atmosphere/releases +thumbnail-url: https://avatars2.githubusercontent.com/u/37918415?s=200&v=4 +author.name: Atmosphère-NX Team +author.url: https://github.com/Atmosphere-NX +help-desc: Download link for the latest Atmosphère version +aliases: atmos,ams +color: 3498db +--- + +Link to Atmosphère latest release diff --git a/cogs/assistance-cmds/atob.3ds.md b/cogs/assistance-cmds/atob.3ds.md new file mode 100644 index 000000000..3ec274c34 --- /dev/null +++ b/cogs/assistance-cmds/atob.3ds.md @@ -0,0 +1,11 @@ +--- +title: Upgrading a9lh to b9s +url: https://3ds.hacks.guide/a9lh-to-b9s +thumbnail-url: https://nintendohomebrew.com/assets/img/nhplai.png +author.name: NH & Friends +author.url: https://3ds.hacks.guide/a9lh-to-b9s +aliases: a9lhtob9s,updatea9lh +help-desc: Links to the guide for updating from a9lh to b9s +--- + +A guide for upgrading your device from arm9loaderhax to boot9strap. diff --git a/cogs/assistance-cmds/baninfo.3ds.md b/cogs/assistance-cmds/baninfo.3ds.md new file mode 100644 index 000000000..b2fa8ff52 --- /dev/null +++ b/cogs/assistance-cmds/baninfo.3ds.md @@ -0,0 +1,10 @@ +--- +title: 3DS Bans +help-desc: Links to ban information pages +--- + +**Nintendo has shown a marked lack of care about bans on the 3DS lately.** +However, such things as piracy and cheating online/cheating in multiplayer games have been known causes for NNID/console bans in the past. +eShop fraud (eg credit card chargebacks) will also get you banned. + +You can enable online status and Spotpass/Streetpass as these do not seem to be high risk at this time. diff --git a/cogs/assistance-cmds/baninfo.switch.md b/cogs/assistance-cmds/baninfo.switch.md new file mode 100644 index 000000000..000568e79 --- /dev/null +++ b/cogs/assistance-cmds/baninfo.switch.md @@ -0,0 +1,8 @@ +--- +title: NX Bans +url: https://nx.eiphax.tech/ban +thumbnail-url: https://nintendohomebrew.com/assets/img/gunther.png +help-desc: Links to ban information pages +--- + +Bans on the Switch are complicated. Please click the embed header link and read the linked page to learn more. diff --git a/cogs/assistance-cmds/bigsd.3ds.md b/cogs/assistance-cmds/bigsd.3ds.md new file mode 100644 index 000000000..0553a59ce --- /dev/null +++ b/cogs/assistance-cmds/bigsd.3ds.md @@ -0,0 +1,14 @@ +--- +title: Big SD +help-desc: Embeds big sd information +--- + +Although Nintendo says the official SD size limit is 32GB, the 3DS can accept cards up to 2TB. +In order to use them, you will have to format them to FAT32 first. +You can do this using these tools: + +-GUIFormat for Windows: http://ridgecrop.co.uk/index.htm?guiformat.htm +-gparted for Linux: https://gparted.org/download.php +-Disk Utility for macOS: https://support.apple.com/guide/disk-utility/format-a-disk-for-windows-computers-dskutl1010 + +IMPORTANT: On macOS, always select "MS-DOS (Fat)". Formatting will erase all data on the card. Make a backup first. diff --git a/cogs/assistance-cmds/bigsd.switch.md b/cogs/assistance-cmds/bigsd.switch.md new file mode 100644 index 000000000..f87575fef --- /dev/null +++ b/cogs/assistance-cmds/bigsd.switch.md @@ -0,0 +1,14 @@ +--- +title: Big SD +help-desc: Embeds big sd information +--- + +Although Nintendo supports large SD cards in EXFAT format, it is recommended to use FAT32. +In order to change the card's format, you will need to use an external utility. +Here are some suggestions: + +-GUIFormat for Windows: http://ridgecrop.co.uk/index.htm?guiformat.htm +-gparted for Linux: https://gparted.org/download.php +-Disk Utility for macOS: https://support.apple.com/guide/disk-utility/format-a-disk-for-windows-computers-dskutl1010 + +IMPORTANT: On macOS, always select "MS-DOS (Fat)". Formatting will erase all data on the card. Make a backup first. diff --git a/cogs/assistance-cmds/cfwuses.3ds.md b/cogs/assistance-cmds/cfwuses.3ds.md new file mode 100644 index 000000000..e0e4f169e --- /dev/null +++ b/cogs/assistance-cmds/cfwuses.3ds.md @@ -0,0 +1,5 @@ +--- +help-desc: Uses for CFW on Wii U, Switch, and 3DS +--- + +Want to know what CFW can be used for? diff --git a/cogs/assistance-cmds/cfwuses.switch.md b/cogs/assistance-cmds/cfwuses.switch.md new file mode 100644 index 000000000..00b8fb615 --- /dev/null +++ b/cogs/assistance-cmds/cfwuses.switch.md @@ -0,0 +1,15 @@ +--- +title: What can I do with a hacked switch? +help-desc: Uses for CFW on Wii U, Switch, and 3DS +--- + +There is no complete list about what is possible and what not, but to give you an idea of what you can do, here is an overview: + +-Have custom themes, +-Run emulators (up to N64 works, with a bit of modification GCN/Wii work fine as well but it varies from game to game), +-Run custom homebrew apps, +-Backup, edit and restore game saves, +-Dump game cartridges (to look at the contents, for example) +-Mod games, +-Run Android or Linux on your Switch, +-Still have access to normal stock features (e.g. eShop, online services etc.) diff --git a/cogs/assistance-cmds/cfwuses.wiiu.md b/cogs/assistance-cmds/cfwuses.wiiu.md new file mode 100644 index 000000000..31066ede6 --- /dev/null +++ b/cogs/assistance-cmds/cfwuses.wiiu.md @@ -0,0 +1,11 @@ +--- +title: What can Wii U CFW be used for? +help-desc: Uses for CFW on Wii U, Switch, and 3DS +--- + +# Among other things, it allows you to do the following: +- Use “ROM hacks” for games that you own. +- Backup, edit and restore saves for many games. +- Play games for older systems with various emulators, using RetroArch or other standalone emulators. +- Play out-of-region games. +- Dump your Wii U game discs to a format that can be installed on your internal or external Wii U storage drive. diff --git a/cogs/assistance-cmds/ctr.3ds.md b/cogs/assistance-cmds/ctr.3ds.md new file mode 100644 index 000000000..f8d857334 --- /dev/null +++ b/cogs/assistance-cmds/ctr.3ds.md @@ -0,0 +1,11 @@ +--- +title: Guide - ctrtransfer +url: https://3ds.hacks.guide/ctrtransfer +thumbnail-url: https://nintendohomebrew.com/assets/img/nhplai.png +author.name: NH & Friends +author.url: https://3ds.hacks.guide/ +aliases: ctrtransfer,ctrnandtransfer +help-desc: Links to ctrtransfer guide +--- + +How to do the 11.5.0-38 ctrtransfer diff --git a/cogs/assistance-cmds/dsp.3ds.md b/cogs/assistance-cmds/dsp.3ds.md new file mode 100644 index 000000000..fe54ceb68 --- /dev/null +++ b/cogs/assistance-cmds/dsp.3ds.md @@ -0,0 +1,11 @@ +--- +title: Dsp1 +url: https://github.com/zoogie/DSP1/releases +thumbnail-url: https://raw.githubusercontent.com/Cruel/DspDump/master/icon.png +author.name: zoogie +author.url: https://github.com/zoogie +author.icon-url: https://gbatemp.net/data/avatars/l/357/357147.jpg?1426471484 +help-desc: Links to Dsp1. +--- + +Dump 3DS's DSP component to SD for homebrew audio. diff --git a/cogs/assistance-cmds/formatsd.3ds.md b/cogs/assistance-cmds/formatsd.3ds.md new file mode 100644 index 000000000..17d8412c1 --- /dev/null +++ b/cogs/assistance-cmds/formatsd.3ds.md @@ -0,0 +1,11 @@ +--- +title: SD Formatting Tools +help-desc: SD Format Tools +aliases: sdformat +--- + +Here are some links to common FAT32 formatting tools: +• [GUIFormat](http://ridgecrop.co.uk/index.htm?guiformat.htm) (Windows) +• [gparted](https://gparted.org/download.php) + [dosfstools](https://github.com/dosfstools/dosfstools) (Linux) +• [Disk Utility](https://support.apple.com/guide/disk-utility/format-a-disk-for-windows-computers-dskutl1010) (MacOS) +MacOS: Always select "MS-DOS (FAT)", even if the card is larger than 32GB. diff --git a/cogs/assistance-cmds/hekate.switch.md b/cogs/assistance-cmds/hekate.switch.md new file mode 100644 index 000000000..9a6038445 --- /dev/null +++ b/cogs/assistance-cmds/hekate.switch.md @@ -0,0 +1,11 @@ +--- +title: Hekate +url: https://github.com/CTCaer/hekate/releases/latest +thumbnail-url: https://imgur.com/kFEZyuC.png +author.name: CTCaer +author.url: https://github.com/CTCaer +help-desc: Download link for the latest Hekate version +color: e74c3c +--- + +Link to Hekate's latest release diff --git a/cogs/assistance-cmds/ip.3ds.md b/cogs/assistance-cmds/ip.3ds.md new file mode 100644 index 000000000..a1ab30374 --- /dev/null +++ b/cogs/assistance-cmds/ip.3ds.md @@ -0,0 +1,12 @@ +--- +title: Check your 3DSs IP (CFW) +help-desc: How to check your IP +--- + +1. FBI +2. Remote Install +3. Receive URLs over the network + +# Check your 3DSs IP (Homebrew) +1. Open Homebrew Launcher +2. Press Y diff --git a/cogs/assistance-cmds/lumabug.3ds.md b/cogs/assistance-cmds/lumabug.3ds.md new file mode 100644 index 000000000..72ccd4f62 --- /dev/null +++ b/cogs/assistance-cmds/lumabug.3ds.md @@ -0,0 +1,11 @@ +--- +title: Luma Black Screen Bug +help-desc: Luma Black Screen Bug +--- + +If you have Luma3DS and your console is stuck on a black screen after you power it on, follow these steps: +1. Power off the console. +2. Take out any game cartridge, but leave the SD card in. +3. Power on the console. +4. Leave the console open and powered on for 10-15 minutes. Do not touch the console during this time. +If the console boots successfully in that time, the bug is now fixed and is unlikely to happen again. If the console still fails to boot to home menu, come back and ask for more help. Mention that you have already tried the Luma black screen process. diff --git a/cogs/assistance-cmds/lumacheck.3ds.md b/cogs/assistance-cmds/lumacheck.3ds.md new file mode 100644 index 000000000..d8ec61f8c --- /dev/null +++ b/cogs/assistance-cmds/lumacheck.3ds.md @@ -0,0 +1,13 @@ +--- +title: Please check your Luma version. +help-desc: How to check Luma version +aliases: checkluma +--- + +In order to do this, you will need to load the Luma Configuration screen. + +# Steps to open Luma Configuration +1. Turn your console off. +2. Hold the SELECT button. +3. While still holding SELECT, turn the console on. +4. Provide a photo of your console's screens, or if you can see the version, tell us here. diff --git a/cogs/assistance-cmds/modmoon.3ds.md b/cogs/assistance-cmds/modmoon.3ds.md new file mode 100644 index 000000000..b4945d268 --- /dev/null +++ b/cogs/assistance-cmds/modmoon.3ds.md @@ -0,0 +1,7 @@ +--- +help-desc: Links to a tool for a mod manager +--- + +To install mods for Smash 3DS, and to manage other LayeredFS mods, [Mod-Moon](https://github.com/Swiftloke/ModMoon/releases) is recommended. + +Instructions for usage can be found [in this thread.](https://gbatemp.net/threads/modmoon-a-beautiful-simple-and-compact-mods-manager-for-the-nintendo-3ds.519080#) diff --git a/cogs/assistance-cmds/newver.3ds.md b/cogs/assistance-cmds/newver.3ds.md new file mode 100644 index 000000000..ae5b1a102 --- /dev/null +++ b/cogs/assistance-cmds/newver.3ds.md @@ -0,0 +1,14 @@ +--- +title: Is the new 3DS update safe? +help-desc: Quick advice for new versions +--- + +Currently, the latest 3DS system firmware is `11.15.0-47`. + +If you currently have CFW installed (boot9strap/Luma): +Is your Luma version up to date? If your Luma version is 10.2.1 or above, **updating is safe**. +If it is 10.2 or below, please type `.update` in <#261581918653513729> and follow the information there. + +If you DO NOT currently have CFW installed (stock console): +11.15.0-46 can be hacked with current methods. **Updating is safe**; however, if you are on 11.3 or below, it may be worthwhile to stay on that version as it is faster to hack your console there. +*Last edited: November 16th, 2020* diff --git a/cogs/assistance-cmds/newver.switch.md b/cogs/assistance-cmds/newver.switch.md new file mode 100644 index 000000000..c75e6078a --- /dev/null +++ b/cogs/assistance-cmds/newver.switch.md @@ -0,0 +1,14 @@ +--- +title: Is the new Switch update safe? +help-desc: Quick advice for new versions +--- + +Currently, the latest Switch system firmware is `{nx_firmware}`. + +If your Switch is **unpatched and can access RCM**: +Atmosphere and Hekate currently support {nx_firmware}, and unpatched units will always be hackable. +You should follow the precautions in our update guide, and always update Atmosphere and Hekate before updating the system firmware. + +If your Switch is **hardware patched and cannot access RCM**: +Stay on the lowest possible firmware version. Any Switch that is patched and above 7.0.1 is unlikely to be hackable. +*Last edited: {last_revision}* diff --git a/cogs/assistance-cmds/nospace.3ds.md b/cogs/assistance-cmds/nospace.3ds.md new file mode 100644 index 000000000..44df63712 --- /dev/null +++ b/cogs/assistance-cmds/nospace.3ds.md @@ -0,0 +1,12 @@ +--- +title: How to create a 3DS NAND backup without enough space on the SD card +help-desc: Low space NAND Backup +aliases: lowspace,lowbackup +--- + +# Steps to create the backup + +1. Copy the Nintendo 3DS folder from the root of your SD card to your computer then delete it from **the SD card.** +2. Boot GodMode9 by holding START on boot then preform a normal NAND backup. After that, power off the system. +3. Copy the files in gm9/out on your SD card to a safe spot on your computer. Then, delete the files from **the SD card.** +4. Copy the Nintendo 3DS folder to your SD card root then delete it **from your computer.** diff --git a/cogs/assistance-cmds/nsupdate.switch.md b/cogs/assistance-cmds/nsupdate.switch.md new file mode 100644 index 000000000..092c7f6e1 --- /dev/null +++ b/cogs/assistance-cmds/nsupdate.switch.md @@ -0,0 +1,23 @@ +--- +title: What do I need to do before updating my system firmware when running CFW? +help-desc: What you should do before updating a Nintendo Switch +aliases: updateprep,nxupdate +--- + +**Make sure your version of Atmosphere is up to date and that it supports the latest firmware** + +**Atmosphere {ams_ver} (latest release)** +Supports up to firmware {nx_firmware}. + +*To find Atmosphere's version information, while booted into CFW, go into System Settings -> System, and look at the text under the System Update button. If it says that a system update is ready instead of displaying the CFW version, type .pendingupdate in <#261581918653513729> to learn how to delete it.* + +**Make sure your version of Hekate is up to date and that it supports the latest firmware** + +**Hekate {hekate_ver} (latest release)** +Supports up to firmware {nx_firmware}. + +*To find Hekate's version information, once Hekate starts, look in the top left corner of the screen. If you use auto-boot, hold `volume -` to stop it.* + +**If you use a custom theme (Atmosphere 0.10.0 and above)** +Delete or rename `/atmosphere/contents/0100000000001000` on your SD card prior to updating, as custom themes must be reinstalled for most firmware updates. **Note: On Atmosphere 0.9.4 or below, `contents` is called `titles`.** + diff --git a/cogs/assistance-cmds/ntrstream.3ds.md b/cogs/assistance-cmds/ntrstream.3ds.md new file mode 100644 index 000000000..b57ca9351 --- /dev/null +++ b/cogs/assistance-cmds/ntrstream.3ds.md @@ -0,0 +1,20 @@ +--- +title: Snickerstream: NTR Streaming Client +url: https://gbatemp.net/threads/release-snickerstream-revived-a-proper-release-with-lots-of-improvements-and-new-features.488374/ +help-desc: Snickerstream/NTR streaming guide +aliases: snickerstream +--- + +How to use NTR CFW with Snickerstream to stream your 3DS' screen + +# Guide and Advice + +Easy [install guide](https://github.com/RattletraPM/Snickerstream/wiki/Streaming-with-NTR) for streaming with Snickerstream. +Snickerstream [app download](https://github.com/RattletraPM/Snickerstream/releases/latest) +Having issues? Check the following: +• Are you connected to the Internet? +• Is your antivirus program blocking the program? +• Make sure you typed the IP correctly. +• Make sure you are using the latest BootNTR Selector with NTR 3.6. +More detailed troubleshooting [available here](https://github.com/RattletraPM/Snickerstream/wiki/Troubleshooting) +Other information about Snickerstream on [Snickerstream's GitHub Wiki](https://github.com/RattletraPM/Snickerstream/wiki) diff --git a/cogs/assistance-cmds/readguide.all.md b/cogs/assistance-cmds/readguide.all.md new file mode 100644 index 000000000..b43f80953 --- /dev/null +++ b/cogs/assistance-cmds/readguide.all.md @@ -0,0 +1,6 @@ +--- +title: Please read the guide +help-desc: Read the guide please +--- + +Asking something that is on the guide will make everyone lose time, so please read and re-read the guide steps 2 or 3 times before coming here. diff --git a/cogs/assistance-cmds/seedminer.3ds.md b/cogs/assistance-cmds/seedminer.3ds.md new file mode 100644 index 000000000..8b9d0fe68 --- /dev/null +++ b/cogs/assistance-cmds/seedminer.3ds.md @@ -0,0 +1,10 @@ +--- +title: Seedminer +url: https://3ds.hacks.guide/seedminer +thumbnail-url: https://nintendohomebrew.com/assets/img/nhplai.png +author.name: NH & Friends +author.url: https://3ds.hacks.guide/seedminer +help-desc: Links the seedminer guide +--- + +A guide on how to do the seedminer process to get your 3ds' movable.sed file diff --git a/cogs/assistance-cmds/stock.3ds.md b/cogs/assistance-cmds/stock.3ds.md new file mode 100644 index 000000000..b4742a754 --- /dev/null +++ b/cogs/assistance-cmds/stock.3ds.md @@ -0,0 +1,7 @@ +--- +title: Running stock (unmodified) 3DS firmware? +help-desc: Advisory for various Nintendo systems on stock firmware +--- + +# Check Your Firmware +The latest update to common guide methods mean that the best method for you now depends on your firmware version. Please read the [guide](https://3ds.hacks.guide/get-started) to learn more. diff --git a/cogs/assistance-cmds/stock.switch.md b/cogs/assistance-cmds/stock.switch.md new file mode 100644 index 000000000..bd566de86 --- /dev/null +++ b/cogs/assistance-cmds/stock.switch.md @@ -0,0 +1,16 @@ +--- +title: Looking to hack your Switch? +help-desc: Advisory for various Nintendo systems on stock firmware +--- + +Use [our guide](https://nh-server.github.io/switch-guide/user_guide/getting_started/) to determine if your Switch is a first-gen unit. +**First generation consoles (RCM exploitable)** +All of these can run [Atmosphere](https://nh-server.github.io/switch-guide/). Make sure that Atmosphere is compatible with the latest firmware version before you update. + +**Second generation consoles ("patched" units, Switch Lite, Mariko, etc.)** + +**"Old" Patched Switch (HAC-001)**: Do NOT update past 7.0.1. Units on 7.0.1 and below will eventually get CFW. Units on 8.0.0 and higher are not expected to be hacked and can be updated. +**"New" Switch (HAC-001-01)**: Do NOT update past 8.0.1. Units on 8.0.1 and below will likely get homebrew. Units on 8.1.0 and higher are not expected to be hacked and can be updated. +**Switch Lite (HDH-001)**: Do NOT update past 8.0.1. Units on 8.0.1 and below will likely get homebrew. Units on 8.1.0 and higher are not expected to be hacked and can be updated. + +Downgrading is **impossible** on patched consoles, and isn't worth your time on unpatched ones. diff --git a/cogs/assistance-cmds/transfersd.3ds.md b/cogs/assistance-cmds/transfersd.3ds.md new file mode 100644 index 000000000..077cf04b8 --- /dev/null +++ b/cogs/assistance-cmds/transfersd.3ds.md @@ -0,0 +1,16 @@ +--- +title: Moving SD Cards +help-desc: Embeds sd transfer information +--- + +Moving SD cards on a 3DS is easy. +First, ensure the new SD card is in the FAT32 format. +If it is above 32GB, you will need to format it using one of these tools: + +-GUIFormat for Windows: http://ridgecrop.co.uk/index.htm?guiformat.htm +-gparted for Linux: https://gparted.org/download.php +-Disk Utility for macOS: https://support.apple.com/guide/disk-utility/format-a-disk-for-windows-computers-dskutl1010 + +Once the new card is in FAT32, move all your content from the old SD to the new SD. +IMPORTANT: On macOS, always select "MS-DOS (Fat)". Formatting will erase all data on the card. Make a backup first. +IMPORTANT: Do not put the new SD card in the console before moving all your data to it. diff --git a/cogs/assistance-cmds/transfersd.switch.md b/cogs/assistance-cmds/transfersd.switch.md new file mode 100644 index 000000000..f62bb09fb --- /dev/null +++ b/cogs/assistance-cmds/transfersd.switch.md @@ -0,0 +1,10 @@ +--- +title: Moving SD cards +url: https://switchgui.de/switch-guide/extras/transfer_sd/ +thumbnail-url: https://i.imgur.com/CVSu1zc.png +author.name: NH Discord Server +author.url: https://switchgui.de/switch-guide/ +help-desc: Embeds sd transfer information +--- + +A guide to moving SD cards with emuMMC diff --git a/cogs/assistance-cmds/tutorial/ap.dsi.md b/cogs/assistance-cmds/tutorial/ap.dsi.md new file mode 100644 index 000000000..991ead0d7 --- /dev/null +++ b/cogs/assistance-cmds/tutorial/ap.dsi.md @@ -0,0 +1,10 @@ +--- +title: AP Guide +url: https://glazedbelmont.github.io/appatching/ +thumbnail-url: https://i.imgur.com/TgdOPkG.png +aliases: appatch,dsscene +help-desc: Anti-piracy patching guide +author.name: Glazed_Belmont +--- + +An AP-Patching guide diff --git a/cogs/assistance-cmds/tutorial/cpcheats.3ds.md b/cogs/assistance-cmds/tutorial/cpcheats.3ds.md new file mode 100644 index 000000000..1c103c0be --- /dev/null +++ b/cogs/assistance-cmds/tutorial/cpcheats.3ds.md @@ -0,0 +1,11 @@ +--- +title: 3DS Cheats Guide +url: https://3ds.eiphax.tech/cpcheats.html +author.name: Krieg +thumbnail-url: https://nintendohomebrew.com/assets/img/krieg.png +help-desc: Checkpoint/Rosalina cheat guide +aliases: cheats,3dscheats +color: 9B59B6 +--- + +A guide to using cheats with Checkpoint and Rosalina diff --git a/cogs/assistance-cmds/tutorial/gbabiosdump.3ds.md b/cogs/assistance-cmds/tutorial/gbabiosdump.3ds.md new file mode 100644 index 000000000..3ad5ec2a7 --- /dev/null +++ b/cogs/assistance-cmds/tutorial/gbabiosdump.3ds.md @@ -0,0 +1,11 @@ +--- +title: GBA Bios Extraction Tutorial +url: https://glazedbelmont.github.io/gbabiosdump/ +help-desc: Links to GBA Bios Extraction Tutorial +author.name: Glazed_Belmont +thumbnail-url: https://i.imgur.com/TgdOPkG.png +aliases: gbabios +color: 551A8B +--- + +Basic tutorial to extract a GBA bios diff --git a/cogs/assistance-cmds/tutorial/gbadump.all.md b/cogs/assistance-cmds/tutorial/gbadump.all.md new file mode 100644 index 000000000..1009c64f8 --- /dev/null +++ b/cogs/assistance-cmds/tutorial/gbadump.all.md @@ -0,0 +1,9 @@ +--- +title: Dumping GBA games +url: https://wiki.no-intro.org/index.php?title=Game_Boy_Advance_Dumping_Guide +thumbnail-url: https://wiki.no-intro.org/resources/assets/wiki.png +help-desc: Links to GBA Dump guide +color: 9B59B6 +--- + +How to dump GBA cartridges diff --git a/cogs/assistance-cmds/tutorial/layeredfs.3ds.md b/cogs/assistance-cmds/tutorial/layeredfs.3ds.md new file mode 100644 index 000000000..b3fcc305e --- /dev/null +++ b/cogs/assistance-cmds/tutorial/layeredfs.3ds.md @@ -0,0 +1,11 @@ +--- +title: LayeredFs Guide +url: https://github.com/knight-ryu12/godmode9-layeredfs-usage/wiki/Using-Luma3DS'-layeredfs-(Only-version-8.0-and-higher) +author.name: Chroma Ryu +author.url: https://github.com/knight-ryu12/godmode9-layeredfs-usage/wiki/Using-Luma3DS'-layeredfs-(Only-version-8.0-and-higher) +thumbnail-url: https://i.imgur.com/U8NA9lx.png +help-desc: How to use Luma 8.0+ LayeredFs +color: 66FFFF +--- + +How to use Luma 8.0+ LayeredFs for ROM Hacking. diff --git a/cogs/assistance-cmds/tutorial/ninfs.3ds.md b/cogs/assistance-cmds/tutorial/ninfs.3ds.md new file mode 100644 index 000000000..3da4f106e --- /dev/null +++ b/cogs/assistance-cmds/tutorial/ninfs.3ds.md @@ -0,0 +1,7 @@ +--- +title: Extract and Decrypt games, NAND backups, and SD contents with ninfs +url: https://gbatemp.net/threads/499994/ +help-desc: Link to ninfs tutorial. +--- + +This is a tutorial that shows you how to use ninfs to extract the contents of games, NAND backups, and SD card contents. Windows, macOS, and Linux are supported. diff --git a/cogs/assistance-cmds/tutorial/obscitra.3ds.md b/cogs/assistance-cmds/tutorial/obscitra.3ds.md new file mode 100644 index 000000000..f9b46e648 --- /dev/null +++ b/cogs/assistance-cmds/tutorial/obscitra.3ds.md @@ -0,0 +1,11 @@ +--- +title: OBS and Citra Guide +url: https://kriegisrei.github.io/obscitra/ +thumbnail-url: https://nintendohomebrew.com/assets/img/krieg.png +author.name: Krieg +aliases: citraobs +help-desc: OBS and Citra guide +color: 9B59B6 +--- + +A guide to recording Citra with OBS diff --git a/cogs/assistance-cmds/tutorial/plugins.3ds.md b/cogs/assistance-cmds/tutorial/plugins.3ds.md new file mode 100644 index 000000000..2fe3f565c --- /dev/null +++ b/cogs/assistance-cmds/tutorial/plugins.3ds.md @@ -0,0 +1,11 @@ +--- +title: 3DS NTR Plugins Guide +url: https://3ds.eiphax.tech/ntrplugins +thumbnail-url: https://nintendohomebrew.com/assets/img/krieg.png +author.name: Krieg +aliases: ntrplugins +help-desc: NTR Plugins guide +color: 9B59B6 +--- + +A guide to using plugins with NTR diff --git a/cogs/assistance-cmds/tutorial/transfersave.3ds.md b/cogs/assistance-cmds/tutorial/transfersave.3ds.md new file mode 100644 index 000000000..8744030e9 --- /dev/null +++ b/cogs/assistance-cmds/tutorial/transfersave.3ds.md @@ -0,0 +1,9 @@ +--- +title: Cart to digital version save transfer tutorial +url: https://redkerry135.github.io/transfersave/ +help-desc: Links to cart to digital version save transfer tutorial +aliases: carttodigitalsave,ctdsave +color: 9B59B6 +--- + +A tutorial about how to transfer a save from the cart version of a game to a digital version of that game. diff --git a/cogs/assistance-cmds/update.3ds.md b/cogs/assistance-cmds/update.3ds.md new file mode 100644 index 000000000..efa83d4d1 --- /dev/null +++ b/cogs/assistance-cmds/update.3ds.md @@ -0,0 +1,19 @@ +--- +help-desc: Explains how to safely prepare for an update for a hacked console (1) +--- + +**Is it safe to update to current 3DS firmware?** + +**Luma3DS 10.2.1 and above** +You can update safely. + +**Luma3DS 8.0 - 10.2** +Please follow the directions on the 3DS Hacks Guide [Restoring / Updating CFW](https://3ds.hacks.guide/restoring-updating-cfw) page, then you can update safely. Being on these Luma3DS versions on 11.8+ will cause an error screen until you update. + +**Luma3DS 7.1** +Follow the [B9S upgrade guide](https://3ds.hacks.guide/updating-b9s) + +**Luma3DS 7.0.5 and below** +Follow the [a9lh-to-b9s guide](https://3ds.hacks.guide/a9lh-to-b9s) + +**To find out your Luma3DS version, hold select on bootup and look at the top left corner of the top screen** diff --git a/cogs/assistance-cmds/update.switch.md b/cogs/assistance-cmds/update.switch.md new file mode 100644 index 000000000..ca0511e24 --- /dev/null +++ b/cogs/assistance-cmds/update.switch.md @@ -0,0 +1,10 @@ +--- +title: Updating Guide +author.name: NH Discord Server +author.url: https://nh-server.github.io/switch-guide/ +thumbnail-url: https://i.imgur.com/CVSu1zc.png +url: https://nh-server.github.io/switch-guide/extras/updating +help-desc: Explains how to safely prepare for an update for a hacked console (2) +--- + +A guide and general recommendations for updating your switch with emuMMC. diff --git a/cogs/assistance-cmds/updateb9s.3ds.md b/cogs/assistance-cmds/updateb9s.3ds.md new file mode 100644 index 000000000..1ab47436c --- /dev/null +++ b/cogs/assistance-cmds/updateb9s.3ds.md @@ -0,0 +1,10 @@ +--- +title: Updating B9S Guide +url: https://3ds.hacks.guide/updating-b9s +thumbnail-url: https://nintendohomebrew.com/assets/img/nhplai.png +author.name: NH & Friends +author.url: https://3ds.hacks.guide/updating-b9s +help-desc: Links to the guide for updating b9s versions +--- + +A guide for updating to new B9S versions. diff --git a/cogs/assistance-cmds/vguides.all.md b/cogs/assistance-cmds/vguides.all.md new file mode 100644 index 000000000..80562a762 --- /dev/null +++ b/cogs/assistance-cmds/vguides.all.md @@ -0,0 +1,14 @@ +--- +title: Why you should not use video guides +help-desc: Information about video guides relating to custom firmware +color: A84300 +--- + +Reasons to not use video guides: +- Most uploaders do not edit their guides after uploading, even if there are mistakes +- When methods become outdated, the information is not updated +- Difficult to give assistance with +- Most videos also refer to a pre-packaged download, which are often outdated and poorly organised + +# Recommended Solution +Read a trusted written tutorial. Try `.guide` in <#261581918653513729> for a list. diff --git a/cogs/assistance-cmds/vguides2.all.md b/cogs/assistance-cmds/vguides2.all.md new file mode 100644 index 000000000..190c524cb --- /dev/null +++ b/cogs/assistance-cmds/vguides2.all.md @@ -0,0 +1,15 @@ +--- +title: More information about video guides +help-desc: Video Guides 2: Electric Boogaloo +color: A84300 +--- + +Other problems with video guides: +- Uploaders tend to care more about views than helping the community, so they don't remove old content +- This usually leads to confusion about which method is best, or most current +- Every uploader has a different route through each method, which often makes it very difficult to give assistance +- Pre-packaged downloads are often hosted on the uploader's server, which they use to generate clicks and revenue +- Pre-packaged downloads ("AIOs") are also very often outdated and not maintained by the creators + +# Recommended Solution +Read a trusted written tutorial. Try `.guide` in <#261581918653513729> for a list. diff --git a/cogs/assistance-cmds/what.3ds.md b/cogs/assistance-cmds/what.3ds.md new file mode 100644 index 000000000..79591ca2f --- /dev/null +++ b/cogs/assistance-cmds/what.3ds.md @@ -0,0 +1,8 @@ +--- +title: what? +url: https://3ds.eiphax.tech/what.html +thumbnail-url: https://nintendohomebrew.com/assets/img/eip2.png +help-desc: Links to 'what' style pages +--- + +Basic things about the 3DS and CFW diff --git a/cogs/assistance-cmds/what.switch.md b/cogs/assistance-cmds/what.switch.md new file mode 100644 index 000000000..fc5f69381 --- /dev/null +++ b/cogs/assistance-cmds/what.switch.md @@ -0,0 +1,8 @@ +--- +title: what? +url: https://nx.eiphax.tech/nutshell.html +thumbnail-url: https://nintendohomebrew.com/assets/img/eip2.png +help-desc: Links to 'what' style pages +--- + +Basic things about the Switch and CFW diff --git a/cogs/assistance.py b/cogs/assistance.py index fa9da2952..99ae7ee80 100644 --- a/cogs/assistance.py +++ b/cogs/assistance.py @@ -5,10 +5,11 @@ from discord.ext import commands, tasks from io import BytesIO from inspect import cleandoc +from os.path import dirname, join from Levenshtein import distance from utils.utils import ConsoleColor from utils.checks import check_if_user_can_sr - +from utils.mdcmd import add_md_files_as_commands, check_console, systems logger = logging.getLogger(__name__) @@ -18,14 +19,23 @@ class Assistance(commands.Cog, command_attrs=dict(cooldown=commands.Cooldown(1, Commands that will mostly be used in the help channels. """ - nx_firmware = "12.1.0" - ams_ver = "0.20.1" - hekate_ver = "5.6.0" - last_revision = "August 30th, 2021" + format_map = { + 'nx_firmware': '12.1.0', + 'ams_ver': '0.20.1', + 'hekate_ver': '5.6.0', + 'last_revision': 'August 30th, 2021', + } + + # compatibility until the use of these variables is removed + nx_firmware = format_map['nx_firmware'] + ams_ver = format_map['ams_ver'] + hekate_ver = format_map['hekate_ver'] + last_revision = format_map['last_revision'] + + data_dir = join(dirname(__file__), 'assistance-cmds') def __init__(self, bot): self.bot = bot - self.systems = ("3ds", "wiiu", "vwii", "switch", "nx", "ns", "wii", "dsi", "legacy") self.unidb = {} self.apps_update.start() @@ -58,14 +68,6 @@ async def simple_embed(self, ctx, text, *, title="", color=discord.Color.default embed.description = cleandoc(text) await ctx.send(embed=embed) - def check_console(self, message, channel, consoles): - message = message.lower() - if message in consoles: - return True - elif ("wii" not in consoles or channel.startswith("legacy")) and channel.startswith(consoles) and message not in self.systems: - return True - return False - @check_if_user_can_sr() @commands.guild_only() @commands.command(aliases=["sr"], cooldown=commands.Cooldown(0, 0, commands.BucketType.channel)) @@ -87,19 +89,19 @@ async def staffreq(self, ctx, *, msg_request: str = ""): async def guide(self, ctx, *, consoles=""): """Links to the recommended guides.""" consoles = consoles.casefold() - consoleslist = {x for x in consoles.split() if x in self.systems} + consoleslist = {x for x in consoles.split() if x in systems} channel_name = ctx.channel.name if not isinstance(ctx.channel, discord.DMChannel) else "" if not consoleslist: - if channel_name.startswith(self.systems): + if channel_name.startswith(systems): consoleslist = ['auto'] else: - await ctx.send(f"Please specify a console. Valid options are: {', '.join([x for x in self.systems])}.") + await ctx.send(f"Please specify a console. Valid options are: {', '.join([x for x in systems])}.") ctx.command.reset_cooldown(ctx) return for x in consoleslist: - if self.check_console(x, channel_name, '3ds'): + if check_console(x, channel_name, '3ds'): embed = discord.Embed(title="Guide", color=ConsoleColor.n3ds()) embed.set_author(name="NH & Friends", url="https://3ds.hacks.guide/") embed.set_thumbnail(url="https://nintendohomebrew.com/assets/img/nhplai.png") @@ -107,7 +109,7 @@ async def guide(self, ctx, *, consoles=""): embed.description = "A complete guide to 3DS custom firmware, from stock to boot9strap." await ctx.send(embed=embed) continue - if self.check_console(x, channel_name, ('wiiu',)): + if check_console(x, channel_name, ('wiiu',)): embed = discord.Embed(title="Guide", color=ConsoleColor.wiiu()) embed.set_author(name="NH Discord Server", url="https://wiiu.hacks.guide/") embed.set_thumbnail(url="https://i.imgur.com/CVSu1zc.png") @@ -115,7 +117,7 @@ async def guide(self, ctx, *, consoles=""): embed.description = "A complete Wii U custom firmware + coldboothax guide" await ctx.send(embed=embed) continue - if self.check_console(x, channel_name, ('vwii',)): + if check_console(x, channel_name, ('vwii',)): embed = discord.Embed(title="Guide", color=ConsoleColor.wiiu()) embed.set_author(name="NH Discord Server", url="https://wiiu.hacks.guide/#/vwii-modding") embed.set_thumbnail(url="https://i.imgur.com/FclGzNz.png") @@ -123,7 +125,7 @@ async def guide(self, ctx, *, consoles=""): embed.description = "A complete vWii modding guide" await ctx.send(embed=embed) continue - if self.check_console(x, channel_name, ('switch', 'nx', 'ns')): + if check_console(x, channel_name, ('switch', 'nx', 'ns')): embed = discord.Embed(title="Guide", color=ConsoleColor.switch()) embed.set_author(name="NH Discord Server", url="https://nh-server.github.io/switch-guide/") embed.set_thumbnail(url="https://i.imgur.com/CVSu1zc.png") @@ -131,14 +133,14 @@ async def guide(self, ctx, *, consoles=""): embed.description = "A Switch guide from stock to Atmosphere" await ctx.send(embed=embed) continue - if self.check_console(x, channel_name, ('legacy', 'wii')): + if check_console(x, channel_name, ('legacy', 'wii')): embed = discord.Embed(title="Guide", color=ConsoleColor.wii()) embed.set_author(name="RiiConnect24", url="https://wii.guide/") embed.set_thumbnail(url="https://i.imgur.com/KI6IXmm.png") embed.url = "https://wii.guide/" embed.description = "A complete original Wii softmod guide" await ctx.send(embed=embed) - if self.check_console(x, channel_name, ('legacy', 'dsi')): + if check_console(x, channel_name, ('legacy', 'dsi')): embed = discord.Embed(title="Guide", color=ConsoleColor.legacy()) embed.set_author(name="emiyl & DS⁽ⁱ⁾ Mode Hacking", url="https://dsi.cfw.guide/credits") embed.set_thumbnail(url="https://i.imgur.com/OGelKVt.png") @@ -163,26 +165,6 @@ async def soundhax(self, ctx): embed.description = "Free 3DS Primary Entrypoint <= 11.3" await ctx.send(embed=embed) - @commands.command() - async def dsp(self, ctx): - """Links to Dsp1.""" - embed = discord.Embed(title="Dsp1", color=discord.Color.green()) - embed.set_author(name="zoogie", url="https://github.com/zoogie", icon_url="https://gbatemp.net/data/avatars/l/357/357147.jpg?1426471484") - embed.description = "Dump 3DS's DSP component to SD for homebrew audio." - embed.set_thumbnail(url="https://raw.githubusercontent.com/Cruel/DspDump/master/icon.png") - embed.url = "https://github.com/zoogie/DSP1/releases" - await ctx.send(embed=embed) - - @commands.command() - async def seedminer(self, ctx): - """Links the seedminer guide""" - embed = discord.Embed(title="Seedminer", color=discord.Color(0xb4eb4d)) - embed.set_author(name="NH & Friends", url="https://3ds.hacks.guide/seedminer") - embed.set_thumbnail(url="https://nintendohomebrew.com/assets/img/nhplai.png") - embed.url = "https://3ds.hacks.guide/seedminer" - embed.description = "A guide on how to do the seedminer process to get your 3ds' movable.sed file" - await ctx.send(embed=embed) - @commands.command(aliases=['3dslanding']) async def getstarted(self, ctx): """Links the 3DS get-started page""" @@ -193,435 +175,6 @@ async def getstarted(self, ctx): embed.description = "How to hack your 3DS console on any firmware from 1.0.0 to 11.14" await ctx.send(embed=embed) - @commands.command(aliases=['snickerstream']) - async def ntrstream(self, ctx): - """Snickerstream/NTR streaming guide""" - embed = discord.Embed(title="Snickerstream: NTR Streaming Client", color=ConsoleColor.n3ds()) - embed.url = "https://gbatemp.net/threads/release-snickerstream-revived-a-proper-release-with-lots-of-improvements-and-new-features.488374/" - embed.description = "How to use NTR CFW with Snickerstream to stream your 3DS' screen" - embed.add_field(name="Guide and Advice", value=cleandoc(""" - Easy [install guide](https://github.com/RattletraPM/Snickerstream/wiki/Streaming-with-NTR) for streaming with Snickerstream. - Snickerstream [app download](https://github.com/RattletraPM/Snickerstream/releases/latest) - Having issues? Check the following: - • Are you connected to the Internet? - • Is your antivirus program blocking the program? - • Make sure you typed the IP correctly. - • Make sure you are using the latest BootNTR Selector with NTR 3.6. - More detailed troubleshooting [available here](https://github.com/RattletraPM/Snickerstream/wiki/Troubleshooting) - Other information about Snickerstream on [Snickerstream's GitHub Wiki](https://github.com/RattletraPM/Snickerstream/wiki) - """)) - await ctx.send(embed=embed) - - @commands.command() - async def update(self, ctx, *, consoles=""): - """Explains how to safely prepare for an update for a hacked console""" - - systems = ('3ds', 'nx', 'ns', 'switch') - wanted_consoles = list(set(x for x in consoles.split() if x in systems)) - channel_name = ctx.channel.name if not isinstance(ctx.channel, discord.DMChannel) else "" - - if not wanted_consoles: - if channel_name.startswith(systems): - wanted_consoles = ["auto"] - else: - await ctx.send(f"Please specify a console. Valid options are: {', '.join([x for x in systems])}") - - ctx.command.reset_cooldown(ctx) - return - - for console in wanted_consoles: - if self.check_console(console, channel_name, "3ds"): - await self.simple_embed(ctx, - "**Is it safe to update to current 3DS firmware?**\n\n" - - "**Luma3DS 10.2.1 and above**\n" - "You can update safely.\n\n" - - "**Luma3DS 8.0 - 10.2**\n" - "Please follow the directions on the 3DS Hacks Guide [Restoring / Updating CFW](https://3ds.hacks.guide/restoring-updating-cfw) page, then you can update safely. Being on these Luma3DS " - "versions on 11.8+ will cause an error screen until you update.\n\n" - - "**Luma3DS 7.1**\n" - "Follow the [B9S upgrade guide](https://3ds.hacks.guide/updating-b9s)\n\n" - - "**Luma3DS 7.0.5 and below**\n" - "Follow the [a9lh-to-b9s guide](https://3ds.hacks.guide/a9lh-to-b9s)\n\n" - - "**To find out your Luma3DS version, hold select on bootup and look at the top left corner of the top screen**\n", - color=ConsoleColor.n3ds()) - elif self.check_console(console, channel_name, ("switch", "nx", "ns")): - embed = discord.Embed(title="Updating Guide", color=ConsoleColor.switch()) - embed.set_author(name="NH Discord Server", url="https://nh-server.github.io/switch-guide/") - embed.set_thumbnail(url="https://i.imgur.com/CVSu1zc.png") - embed.url = "https://nh-server.github.io/switch-guide/extras/updating/" - embed.description = "A guide and general recommendations for updating your switch with emuMMC." - await ctx.send(embed=embed) - - @commands.command(aliases=["checkluma"]) - @commands.cooldown(rate=1, per=15.0, type=commands.BucketType.channel) - async def lumacheck(self, ctx): - """How to check Luma version""" - embed = discord.Embed(title="Please check your Luma version.", color=ConsoleColor.n3ds()) - embed.description = "In order to do this, you will need to load the Luma Configuration screen." - embed.add_field(name="Steps to open Luma Configuration", value=cleandoc(""" - 1. Turn your console off. - 2. Hold the SELECT button. - 3. While still holding SELECT, turn the console on. - 4. Provide a photo of your console's screens, or if you can see the version, tell us here. - """)) - await ctx.send(embed=embed) - - @commands.command(aliases=["lowspace", "lowbackup"]) - @commands.cooldown(rate=1, per=15.0, type=commands.BucketType.channel) - async def nospace(self, ctx): - """Low space NAND Backup""" - embed = discord.Embed(title="How to create a 3DS NAND backup without enough space on the SD card", color=ConsoleColor.n3ds()) - embed.add_field(name="Steps to create the backup", value=cleandoc(""" - 1. Copy the Nintendo 3DS folder from the root of your SD card to your computer then delete it from **the SD card.** - 2. Boot GodMode9 by holding START on boot then preform a normal NAND backup. After that, power off the system. - 3. Copy the files in gm9/out on your SD card to a safe spot on your computer. Then, delete the files from **the SD card.** - 4. Copy the Nintendo 3DS folder to your SD card root then delete it **from your computer.** - """)) - await ctx.send(embed=embed) - - @commands.command() - async def cfwuses(self, ctx, console=""): - """Uses for CFW on Wii U and 3DS""" - systems = ("3ds", "wiiu", "nx", "ns", "switch") - channel_name = ctx.channel.name if not isinstance(ctx.channel, discord.DMChannel) else "" - if console not in systems: - if channel_name.startswith(systems): - console = "auto" - else: - await ctx.send(f"Please specify a console. Valid options are: {', '.join([x for x in systems])}.") - - ctx.command.reset_cooldown(ctx) - return - if self.check_console(console, channel_name, '3ds'): - """Links to eiphax cfw uses page""" - await self.simple_embed(ctx, "Want to know what CFW can be used for? ") - elif self.check_console(console, channel_name, ('switch', 'nx', 'ns')): - embed = discord.Embed(title="What can I do with a hacked switch?", color=ConsoleColor.switch()) - embed.description = cleandoc(""" - There is no complete list about what is possible and what not, but to give you an idea of what you can do, here is an overview: - - -Have custom themes, - -Run emulators (up to N64 works, with a bit of modification GCN/Wii work fine as well but it varies from game to game), - -Run custom homebrew apps, - -Backup, edit and restore game saves, - -Dump game cartridges (to look at the contents, for example) - -Mod games, - -Run Android or Linux on your Switch, - -Still have access to normal stock features (e.g. eShop, online services etc.)""") - await ctx.send(embed=embed) - elif self.check_console(console, channel_name, ('wiiu',)): - embed = discord.Embed(title="What can Wii U CFW be used for?", color=ConsoleColor.wiiu()) - embed.add_field(name="Among other things, it allows you to do the following:", value=cleandoc(""" - - Use “ROM hacks” for games that you own. - - Backup, edit and restore saves for many games. - - Play games for older systems with various emulators, using RetroArch or other standalone emulators. - - Play out-of-region games. - - Dump your Wii U game discs to a format that can be installed on your internal or external Wii U storage drive. - """)) - await ctx.send(embed=embed) - - @commands.command() - async def updateb9s(self, ctx): - """Links to the guide for updating b9s versions""" - embed = discord.Embed(title="Updating B9S Guide", color=ConsoleColor.n3ds()) - embed.set_author(name="NH & Friends", url="https://3ds.hacks.guide/updating-b9s") - embed.set_thumbnail(url="https://nintendohomebrew.com/assets/img/nhplai.png") - embed.url = "https://3ds.hacks.guide/updating-b9s" - embed.description = "A guide for updating to new B9S versions." - await ctx.send(embed=embed) - - @commands.command(aliases=["a9lhtob9s", "updatea9lh"]) - async def atob(self, ctx): - """Links to the guide for updating from a9lh to b9s""" - embed = discord.Embed(title="Upgrading a9lh to b9s", color=ConsoleColor.n3ds()) - embed.set_author(name="NH & Friends", url="https://3ds.hacks.guide/a9lh-to-b9s") - embed.set_thumbnail(url="https://nintendohomebrew.com/assets/img/nhplai.png") - embed.url = "https://3ds.hacks.guide/a9lh-to-b9s" - embed.description = "A guide for upgrading your device from arm9loaderhax to boot9strap." - await ctx.send(embed=embed) - - @commands.command(aliases=["ctrtransfer", "ctrnandtransfer"]) - async def ctr(self, ctx): - """Links to ctrtransfer guide""" - embed = discord.Embed(title="Guide - ctrtransfer", color=ConsoleColor.n3ds()) - embed.set_author(name="NH & Friends", url="https://3ds.hacks.guide/") - embed.set_thumbnail(url="https://nintendohomebrew.com/assets/img/nhplai.png") - embed.url = "https://3ds.hacks.guide/ctrtransfer" - embed.description = "How to do the 11.5.0-38 ctrtransfer" - await ctx.send(embed=embed) - - @commands.command() - async def modmoon(self, ctx): - """Links to a tool for a mod manager""" - await self.simple_embed(ctx, cleandoc(""" - To install mods for Smash 3DS, and to manage other LayeredFS mods, \ -[Mod-Moon](https://github.com/Swiftloke/ModMoon/releases) is recommended. - - Instructions for usage can be found [in this thread.](https://gbatemp.net/threads/modmoon-a-beautiful-simple-and-compact-mods-manager-for-the-nintendo-3ds.519080#) - """), color=ConsoleColor.n3ds()) - - @commands.command() - async def vguides(self, ctx): - """Information about video guides relating to custom firmware""" - embed = discord.Embed(title="Why you should not use video guides", color=discord.Color.dark_orange()) - embed.description = cleandoc(""" - Reasons to not use video guides: - - Most uploaders do not edit their guides after uploading, even if there are mistakes - - When methods become outdated, the information is not updated - - Difficult to give assistance with - - Most videos also refer to a pre-packaged download, which are often outdated and poorly organised - """) - embed.add_field(name="Recommended Solution", value=f"Read a trusted written tutorial. Try `.guide` in {self.bot.channels['bot-cmds'].mention} for a list.") - await ctx.send(embed=embed) - - @commands.command() - async def vguides2(self, ctx): - """Video Guides 2: Electric Boogaloo""" - embed = discord.Embed(title="More information about video guides", color=discord.Color.dark_orange()) - embed.description = cleandoc(""" - Other problems with video guides: - - Uploaders tend to care more about views than helping the community, so they don't remove old content - - This usually leads to confusion about which method is best, or most current - - Every uploader has a different route through each method, which often makes it very difficult to give assistance - - Pre-packaged downloads are often hosted on the uploader's server, which they use to generate clicks and revenue - - Pre-packaged downloads ("AIOs") are also very often outdated and not maintained by the creators - """) - embed.add_field(name="Recommended Solution", value=f"Read a trusted written tutorial. Try `.guide` in {self.bot.channels['bot-cmds'].mention} for a list.") - await ctx.send(embed=embed) - - @commands.command() - async def ip(self, ctx): - """How to check your IP""" - embed = discord.Embed(title="Check your 3DSs IP (CFW)", color=ConsoleColor.n3ds()) - embed.description = "1. FBI\n2. Remote Install\n3. Receive URLs over the network" - embed.add_field(name="Check your 3DSs IP (Homebrew)", value="1. Open Homebrew Launcher\n2. Press Y") - await ctx.send(embed=embed) - - @commands.command() - async def stock(self, ctx, console=None): - """Advisory for various Nintendo systems on stock firmware""" - systems = ("3ds", "nx", "ns", "switch") - channel_name = ctx.channel.name if not isinstance(ctx.channel, discord.DMChannel) else "" - if console not in systems: - if channel_name.startswith(systems): - console = "auto" - else: - await ctx.send(f"Please specify a console. Valid options are: {', '.join([x for x in systems])}.") - - ctx.command.reset_cooldown(ctx) - return - if self.check_console(console, channel_name, '3ds'): - embed = discord.Embed(title="Running stock (unmodified) 3DS firmware?", color=ConsoleColor.n3ds()) - embed.add_field(name="Check Your Firmware", value="The latest update to common guide methods mean that the best method for you now depends on your firmware version. Please read the [guide](https://3ds.hacks.guide/get-started) to learn more.", inline=False) - await ctx.send(embed=embed) - elif self.check_console(console, channel_name, ('nx', 'switch', 'ns')): - await self.simple_embed(ctx, - """ - Use [our guide](https://nh-server.github.io/switch-guide/user_guide/getting_started/) to determine if your Switch is a first-gen unit. - **First generation consoles (RCM exploitable)** - All of these can run [Atmosphere](https://nh-server.github.io/switch-guide/). Make sure that Atmosphere is compatible with the latest firmware version before you update. - - **Second generation consoles ("patched" units, Switch Lite, Mariko, etc.)** - - **"Old" Patched Switch (HAC-001)**: Do NOT update past 7.0.1. Units on 7.0.1 and below will eventually get CFW. Units on 8.0.0 and higher are not expected to be hacked and can be updated. - **"New" Switch (HAC-001-01)**: Do NOT update past 8.0.1. Units on 8.0.1 and below will likely get homebrew. Units on 8.1.0 and higher are not expected to be hacked and can be updated. - **Switch Lite (HDH-001)**: Do NOT update past 8.0.1. Units on 8.0.1 and below will likely get homebrew. Units on 8.1.0 and higher are not expected to be hacked and can be updated. - - Downgrading is **impossible** on patched consoles, and isn't worth your time on unpatched ones. - """, title="Looking to hack your Switch?", color=ConsoleColor.switch()) - - @commands.command() - async def newver(self, ctx, console=None): - """Quick advice for new versions""" - systems = ("3ds", "nx", "ns", "switch") - channel_name = ctx.channel.name if not isinstance(ctx.channel, discord.DMChannel) else "" - if console not in systems: - if channel_name.startswith(systems): - console = "auto" - else: - await ctx.send(f"Please specify a console. Valid options are: {', '.join([x for x in systems])}.") - - ctx.command.reset_cooldown(ctx) - return - - if self.check_console(console, channel_name, '3ds'): - embed = discord.Embed(title="Is the new 3DS update safe?", color=ConsoleColor.n3ds()) - embed.description = cleandoc(""" - Currently, the latest 3DS system firmware is `11.15.0-47`. - - If you currently have CFW installed (boot9strap/Luma): - Is your Luma version up to date? If your Luma version is 10.2.1 or above, **updating is safe**. - If it is 10.2 or below, please type `.update` in <#261581918653513729> and follow the information there. - - If you DO NOT currently have CFW installed (stock console): - 11.15.0-46 can be hacked with current methods. **Updating is safe**; however, if you are on 11.3 or below, it may be worthwhile to stay on that version as it is faster to hack your console there. - *Last edited: November 16th, 2020* - """) - await ctx.send(embed=embed) - - elif self.check_console(console, channel_name, ('nx', 'switch', 'ns')): - embed = discord.Embed(title="Is the new Switch update safe?", color=ConsoleColor.switch()) - embed.description = cleandoc(f""" - Currently, the latest Switch system firmware is `{self.nx_firmware}`. - - If your Switch is **unpatched and can access RCM**: - Atmosphere and Hekate currently support {self.nx_firmware}, and unpatched units will always be hackable. - You should follow the precautions in our update guide, and always update Atmosphere and Hekate before updating the system firmware. - - If your Switch is **hardware patched and cannot access RCM**: - Stay on the lowest possible firmware version. Any Switch that is patched and above 7.0.1 is unlikely to be hackable. - *Last edited: {self.last_revision}* - """) - await ctx.send(embed=embed) - - @commands.command() - async def what(self, ctx, console=None): - """Links to 'what' style pages""" - systems = ("3ds", "nx", "ns", "switch") - channel_name = ctx.channel.name if not isinstance(ctx.channel, discord.DMChannel) else "" - if console not in systems: - if channel_name.startswith(systems): - console = "auto" - else: - await ctx.send(f"Please specify a console. Valid options are: {', '.join([x for x in systems])}.") - - ctx.command.reset_cooldown(ctx) - return - - if self.check_console(console, channel_name, '3ds'): - embed = discord.Embed(title="what?", color=ConsoleColor.n3ds()) - embed.set_thumbnail(url="https://nintendohomebrew.com/assets/img/eip2.png") - embed.url = "https://3ds.eiphax.tech/what.html" - embed.description = "Basic things about the 3DS and CFW" - await ctx.send(embed=embed) - - elif self.check_console(console, channel_name, ('nx', 'switch', 'ns')): - embed = discord.Embed(title="The NX Nutshell", color=ConsoleColor.switch()) - embed.set_thumbnail(url="https://nintendohomebrew.com/assets/img/eip2.png") - embed.url = "https://nx.eiphax.tech/nutshell.html" - embed.description = "Basic things about the Switch and CFW" - await ctx.send(embed=embed) - - @commands.command() - async def baninfo(self, ctx, console=None): - """Links to ban information pages""" - systems = ("3ds", "nx", "ns", "switch") - channel_name = ctx.channel.name if not isinstance(ctx.channel, discord.DMChannel) else "" - if console not in systems: - if channel_name.startswith(systems): - console = "auto" - else: - await ctx.send(f"Please specify a console. Valid options are: {', '.join([x for x in systems])}.") - - ctx.command.reset_cooldown(ctx) - return - - if self.check_console(console, channel_name, '3ds'): - embed = discord.Embed(title="3DS Bans", color=ConsoleColor.n3ds()) - embed.description = cleandoc(""" - **Nintendo has shown a marked lack of care about bans on the 3DS lately.** - However, such things as piracy and cheating online/cheating in multiplayer games have been known causes for NNID/console bans in the past. - eShop fraud (eg credit card chargebacks) will also get you banned. - - You can enable online status and Spotpass/Streetpass as these do not seem to be high risk at this time. - """) - await ctx.send(embed=embed) - - elif self.check_console(console, channel_name, ('nx', 'switch', 'ns')): - embed = discord.Embed(title="NX Bans", color=ConsoleColor.switch()) - embed.set_thumbnail(url="https://nintendohomebrew.com/assets/img/gunther.png") - embed.url = "https://nx.eiphax.tech/ban" - embed.description = "Bans on the Switch are complicated. Please click the embed header link and read the linked page to learn more." - await ctx.send(embed=embed) - - @commands.command() - async def bigsd(self, ctx, console=None): - """Embeds big sd information""" - systems = ("3ds", "nx", "ns", "switch") - channel_name = ctx.channel.name if not isinstance(ctx.channel, discord.DMChannel) else "" - if console not in systems: - if channel_name.startswith(systems): - console = "auto" - else: - await ctx.send(f"Please specify a console. Valid options are: {', '.join([x for x in systems])}.") - - ctx.command.reset_cooldown(ctx) - return - - if self.check_console(console, channel_name, '3ds'): - embed = discord.Embed(title="Big SD", color=ConsoleColor.n3ds()) - embed.description = cleandoc(""" - Although Nintendo says the official SD size limit is 32GB, the 3DS can accept cards up to 2TB. - In order to use them, you will have to format them to FAT32 first. - You can do this using these tools: - - -GUIFormat for Windows: http://ridgecrop.co.uk/index.htm?guiformat.htm - -gparted for Linux: https://gparted.org/download.php - -Disk Utility for macOS: https://support.apple.com/guide/disk-utility/format-a-disk-for-windows-computers-dskutl1010 - - IMPORTANT: On macOS, always select "MS-DOS (Fat)". Formatting will erase all data on the card. Make a backup first. - """) - await ctx.send(embed=embed) - - elif self.check_console(console, channel_name, ('nx', 'switch', 'ns')): - embed = discord.Embed(title="Big SD", color=ConsoleColor.switch()) - embed.description = cleandoc(""" - Although Nintendo supports large SD cards in EXFAT format, it is recommended to use FAT32. - In order to change the card's format, you will need to use an external utility. - Here are some suggestions: - - -GUIFormat for Windows: http://ridgecrop.co.uk/index.htm?guiformat.htm - -gparted for Linux: https://gparted.org/download.php - -Disk Utility for macOS: https://support.apple.com/guide/disk-utility/format-a-disk-for-windows-computers-dskutl1010 - - IMPORTANT: On macOS, always select "MS-DOS (Fat)". Formatting will erase all data on the card. Make a backup first. - """) - await ctx.send(embed=embed) - - @commands.command() - async def transfersd(self, ctx, console=None): - """Embeds sd transfer information""" - systems = ("3ds", "nx", "ns", "switch") - channel_name = ctx.channel.name if not isinstance(ctx.channel, discord.DMChannel) else "" - if console not in systems: - if channel_name.startswith(systems): - console = "auto" - else: - await ctx.send(f"Please specify a console. Valid options are: {', '.join([x for x in systems])}.") - - ctx.command.reset_cooldown(ctx) - return - - if self.check_console(console, channel_name, '3ds'): - embed = discord.Embed(title="Moving SD Cards", color=ConsoleColor.n3ds()) - embed.description = cleandoc(""" - Moving SD cards on a 3DS is easy. - First, ensure the new SD card is in the FAT32 format. - If it is above 32GB, you will need to format it using one of these tools: - - -GUIFormat for Windows: http://ridgecrop.co.uk/index.htm?guiformat.htm - -gparted for Linux: https://gparted.org/download.php - -Disk Utility for macOS: https://support.apple.com/guide/disk-utility/format-a-disk-for-windows-computers-dskutl1010 - - Once the new card is in FAT32, move all your content from the old SD to the new SD. - IMPORTANT: On macOS, always select "MS-DOS (Fat)". Formatting will erase all data on the card. Make a backup first. - IMPORTANT: Do not put the new SD card in the console before moving all your data to it. - """) - await ctx.send(embed=embed) - - elif self.check_console(console, channel_name, ('nx', 'switch', 'ns')): - embed = discord.Embed(title="Moving SD cards", color=ConsoleColor.switch()) - embed.set_author(name="NH Discord Server", url="https://switchgui.de/switch-guide/") - embed.set_thumbnail(url="https://i.imgur.com/CVSu1zc.png") - embed.url = "https://switchgui.de/switch-guide/extras/transfer_sd/" - embed.description = "A guide to moving SD cards with emuMMC" - await ctx.send(embed=embed) - @commands.command() async def catalyst(self, ctx, console=None): """Link to problem solvers""" @@ -635,14 +188,14 @@ async def catalyst(self, ctx, console=None): ctx.command.reset_cooldown(ctx) return - if self.check_console(console, channel_name, '3ds'): + if check_console(console, channel_name, '3ds'): embed = discord.Embed(title="eip's problem solver packs", color=ConsoleColor.n3ds()) embed.description = cleandoc(""" Please visit the following page and read the information provided. https://3ds.eiphax.tech/catalyst.html """) await ctx.send(embed=embed) - elif self.check_console(console, channel_name, ('nx', 'switch', 'ns')): + elif check_console(console, channel_name, ('nx', 'switch', 'ns')): embed = discord.Embed(title="eip's problem solver pack", color=ConsoleColor.switch()) embed.description = cleandoc(""" Please visit the following page and read the information provided. @@ -650,34 +203,6 @@ async def catalyst(self, ctx, console=None): """) await ctx.send(embed=embed) - @commands.command() - async def readguide(self, ctx): - """Read the guide please""" - await self.simple_embed(ctx, """ - Asking something that is on the guide will make everyone lose time, so please read and \ -re-read the guide steps 2 or 3 times before coming here. - """, title="Please read the guide") - - @commands.command(aliases=["atmos", "ams"]) - async def atmosphere(self, ctx): - """Download link for the latest Atmosphère version""" - embed = discord.Embed(title="Atmosphère", color=discord.Color.blue()) - embed.set_author(name="Atmosphère-NX Team", url="https://github.com/Atmosphere-NX") - embed.set_thumbnail(url="https://avatars2.githubusercontent.com/u/37918415?s=200&v=4") - embed.url = "https://github.com/Atmosphere-NX/Atmosphere/releases" - embed.description = "Link to Atmosphère latest release" - await ctx.send(embed=embed) - - @commands.command() - async def hekate(self, ctx): - """Download link for the latest Hekate version""" - embed = discord.Embed(title="Hekate", color=discord.Color.red()) - embed.set_author(name="CTCaer", url="https://github.com/CTCaer") - embed.set_thumbnail(url="https://imgur.com/kFEZyuC.png") - embed.url = "https://github.com/CTCaer/hekate/releases/latest" - embed.description = "Link to Hekate's latest release" - await ctx.send(embed=embed) - @commands.command() async def nxcfw(self, ctx, cfw=""): """Information on why we don't support or recommend various other Switch CFWs""" @@ -724,34 +249,6 @@ async def sdguide(self, ctx): Need to do something with your SD card? Find advice in [this guide](https://3ds.eiphax.tech/sd.html) """, title="SD Troubleshooter") - SDFORMAT_TEXT = """ - Here are some links to common FAT32 formatting tools: - • [GUIFormat](http://ridgecrop.co.uk/index.htm?guiformat.htm) (Windows) - • [gparted](https://gparted.org/download.php) + [dosfstools](https://github.com/dosfstools/dosfstools) (Linux) - • [Disk Utility](https://support.apple.com/guide/disk-utility/format-a-disk-for-windows-computers-dskutl1010) (MacOS) - MacOS: Always select "MS-DOS (FAT)", even if the card is larger than 32GB.""" - - @commands.command(aliases=["sdformat"]) - async def formatsd(self, ctx): - """SD Format Tools""" - await self.simple_embed(ctx, self.SDFORMAT_TEXT, title="SD Formatting Tools") - - @commands.command() - async def lumabug(self, ctx): - """Luma Black Screen Bug""" - await self.simple_embed(ctx, """ - If you have Luma3DS and your console is stuck on a black screen after you power it on, \ -follow these steps: - 1. Power off the console. - 2. Take out any game cartridge, but leave the SD card in. - 3. Power on the console. - 4. Leave the console open and powered on for 10-15 minutes. Do not touch the console \ -during this time. - If the console boots successfully in that time, the bug is now fixed and is unlikely to \ -happen again. If the console still fails to boot to home menu, come back and ask for more help. Mention that you have \ -already tried the Luma black screen process. - """, title="Luma Black Screen Bug") - @commands.command() async def notbricked(self, ctx): """Missing boot.firm""" @@ -785,7 +282,7 @@ async def emureco(self, ctx, console=None): ctx.command.reset_cooldown(ctx) return - if self.check_console(console, channel_name, '3ds'): + if check_console(console, channel_name, '3ds'): embed = discord.Embed(title="EmuNAND for 3DS", color=ConsoleColor.n3ds()) embed.description = cleandoc(""" With the recent advances in hacking methods and safety, it is no longer recommended to use an emuNAND on a 3DS/2DS system. @@ -793,7 +290,7 @@ async def emureco(self, ctx, console=None): If you do not know what an emuNAND is, or is used for, you do not need one. """) await ctx.send(embed=embed) - elif self.check_console(console, channel_name, ('nx', 'switch', 'ns')): + elif check_console(console, channel_name, ('nx', 'switch', 'ns')): embed = discord.Embed(title="EmuMMC/EmuNAND for Switch", color=ConsoleColor.switch()) embed.description = cleandoc(f""" On the Switch system, it is recommended to use an emuMMC/emuNAND. @@ -885,7 +382,7 @@ async def deltheme(self, ctx, console=None): ctx.command.reset_cooldown(ctx) return - if self.check_console(console, channel_name, '3ds'): + if check_console(console, channel_name, '3ds'): await self.simple_embed(ctx, """ 1. Navigate to the following folder on your SD card: \ `/Nintendo 3DS/(32 Character ID)/(32 Character ID)/extdata/00000000/` @@ -894,7 +391,7 @@ async def deltheme(self, ctx, console=None): EUR: `000002ce` JPN: `000002cc` """, title="How to delete Home Menu Theme Data", color=ConsoleColor.n3ds()) - elif self.check_console(console, channel_name, ('nx', 'switch', 'ns')): + elif check_console(console, channel_name, ('nx', 'switch', 'ns')): await self.simple_embed(ctx, """ 1. Navigate to the following folder on your SD card: `/atmosphere/contents` 2. Delete the folder with the name `0100000000001000` @@ -936,7 +433,7 @@ async def vc(self, ctx, *, consoles=""): ctx.command.reset_cooldown(ctx) return for x in consoleslist: - if self.check_console(x, channel_name, ('3ds',)): + if check_console(x, channel_name, ('3ds',)): embed = discord.Embed(title="Virtual Console Injects for 3DS", color=ConsoleColor.n3ds()) embed.set_author(name="Asdolo", url="https://gbatemp.net/members/asdolo.389539/") embed.set_thumbnail(url="https://i.imgur.com/rHa76XM.png") @@ -946,7 +443,7 @@ async def vc(self, ctx, *, consoles=""): await ctx.send(embed=embed) continue - if self.check_console(x, channel_name, ('wiiu', 'wii u')): + if check_console(x, channel_name, ('wiiu', 'wii u')): embed = discord.Embed(title="Virtual Console Injects for Wii U", color=ConsoleColor.wiiu()) embed.set_author(name="NicoAICP", url="https://gbatemp.net/members/nicoaicp.404553/") embed.set_thumbnail(url="https://gbatemp.net/data/avatars/l/404/404553.jpg") @@ -968,14 +465,14 @@ async def dump(self, ctx, console=None): ctx.command.reset_cooldown(ctx) return - if self.check_console(console, channel_name, '3ds'): + if check_console(console, channel_name, '3ds'): embed = discord.Embed(title="GodMode9 dump/build Guide", color=ConsoleColor.n3ds()) embed.set_author(name="NH & Friends", url="https://3ds.hacks.guide/dumping-titles-and-game-cartridges") embed.set_thumbnail(url="https://nintendohomebrew.com/assets/img/nhplai.png") embed.url = "https://3ds.hacks.guide/dumping-titles-and-game-cartridges" embed.description = "How to dump/build CIAs and Files using GodMode9" await ctx.send(embed=embed) - elif self.check_console(console, channel_name, ('switch', 'nx', 'ns')): + elif check_console(console, channel_name, ('switch', 'nx', 'ns')): embed = discord.Embed(title="Switch dump/build Guide", color=ConsoleColor.switch()) embed.set_author(name="SuchMeme", url="https://suchmememanyskill.github.io/guides/switchdumpguide/") embed.set_thumbnail(url="https://i.imgur.com/FkKB0er.png") @@ -983,21 +480,21 @@ async def dump(self, ctx, console=None): embed.description = ("How to dump/build NSPs using NXDumpTool\n" "BAN Warning: only for use using offline emummc") await ctx.send(embed=embed) - elif self.check_console(console, channel_name, ('wiiu',)): + elif check_console(console, channel_name, ('wiiu',)): embed = discord.Embed(title="Wii U dump/install Guide", color=ConsoleColor.wiiu()) embed.set_author(name="NH Discord Server", url="https://wiiu.hacks.guide/#/dump-games") embed.set_thumbnail(url="https://i.imgur.com/CVSu1zc.png") embed.url = "https://wiiu.hacks.guide/#/dump-games" embed.description = "How to dump/install Wii U game discs using disc2app and WUP Installer GX2" await ctx.send(embed=embed) - elif self.check_console(console, channel_name, 'vwii'): + elif check_console(console, channel_name, 'vwii'): embed = discord.Embed(title="vWii dump Guide", color=ConsoleColor.wii()) embed.set_author(name="NH Discord Server", url="https://wiiu.hacks.guide/#/dump-wii-games") embed.set_thumbnail(url="https://i.imgur.com/CVSu1zc.png") embed.url = "https://wiiu.hacks.guide/#/dump-wii-games" embed.description = "How to dump Wii game discs on vWii using CleanRip" await ctx.send(embed=embed) - elif self.check_console(console, channel_name, 'dsi'): + elif check_console(console, channel_name, 'dsi'): embed = discord.Embed(title="GodMode9i dump Guide", color=ConsoleColor.legacy()) embed.set_author(name="NightScript", url="https://dsi.cfw.guide/dumping-game-cards") embed.url = "https://dsi.cfw.guide/dumping-game-cards" @@ -1041,31 +538,6 @@ async def wiiuhdd(self, ctx): One way to fix this is by using an y-cable to connect the HDD to two USB ports. """) - @commands.command(aliases=["updateprep", "nxupdate"]) - async def nsupdate(self, ctx): - """What you should do before updating a Nintendo Switch""" - await self.simple_embed(ctx, cleandoc(f""" - **Make sure your version of Atmosphere is up to date and that it supports the latest firmware** - - **Atmosphere {self.ams_ver} (latest release)** - Supports up to firmware {self.nx_firmware}. - - *To find Atmosphere's version information, while booted into CFW, go into System Settings -> System, and look at \ -the text under the System Update button. If it says that a system update is ready instead of displaying the CFW version, type .pendingupdate in <#261581918653513729> to learn \ -how to delete it.* - - **Make sure your version of Hekate is up to date and that it supports the latest firmware** - - **Hekate {self.hekate_ver} (latest release)** - Supports up to firmware {self.nx_firmware}. - - *To find Hekate's version information, once Hekate starts, look in the top left corner of the screen. If you use auto-boot, hold `volume -` to stop it.* - - **If you use a custom theme (Atmosphere 0.10.0 and above)** - Delete or rename `/atmosphere/contents/0100000000001000` on your SD card prior to updating, \ -as custom themes must be reinstalled for most firmware updates. **Note: On Atmosphere 0.9.4 or below, `contents` is called `titles`.** - """), title="What do I need to do before updating my system firmware when running CFW?", color=ConsoleColor.switch()) - @commands.command(aliases=["pendingupdate"]) async def delupdate(self, ctx): """Erase pending updates on Nintendo Switch""" @@ -1138,16 +610,6 @@ async def randomize(self, ctx): embed.description = "Basic tutorial for randomizing with LayeredFS" await ctx.send(embed=embed) - @tutorial.command(aliases=["romhack", "romhacks"], cooldown=commands.Cooldown(0, 0, commands.BucketType.channel)) - async def layeredfs(self, ctx): - """How to use Luma 8.0+ LayeredFs""" - embed = discord.Embed(title="LayeredFs Guide", color=discord.Color(0x66FFFF)) - embed.set_author(name="Chroma Ryu", url="https://github.com/knight-ryu12/godmode9-layeredfs-usage/wiki/Using-Luma3DS'-layeredfs-(Only-version-8.0-and-higher)") - embed.set_thumbnail(url="https://i.imgur.com/U8NA9lx.png") - embed.url = "https://github.com/knight-ryu12/godmode9-layeredfs-usage/wiki/Using-Luma3DS'-layeredfs-(Only-version-8.0-and-higher)" - embed.description = "How to use Luma 8.0+ LayeredFs for ROM Hacking." - await ctx.send(embed=embed) - @tutorial.command(aliases=["Animal_crossing"], cooldown=commands.Cooldown(0, 0, commands.BucketType.channel)) async def acnl(self, ctx): """Links to AC:NL editing tutorial""" @@ -1189,12 +651,12 @@ async def vcextract(self, ctx, console=""): return embed = discord.Embed() - if self.check_console(console, channel_name, "3ds"): + if check_console(console, channel_name, "3ds"): embed.title = "3DS VC Extraction Tutorial" embed.set_author(name="GlaZed_Belmont") embed.set_thumbnail(url="https://i.imgur.com/TgdOPkG.png") embed.url = "https://glazedbelmont.github.io/vcextract/" - elif self.check_console(console, channel_name, ('wiiu',)): + elif check_console(console, channel_name, ('wiiu',)): embed.title = "Wii U VC Extraction Tutorial" embed.set_author(name="lendun, Lazr") embed.set_thumbnail(url="https://i.imgur.com/qXc4TY5.png") @@ -1202,47 +664,6 @@ async def vcextract(self, ctx, console=""): embed.description = "Tutorial to extract a ROM out of your VC titles" await ctx.send(embed=embed) - @tutorial.command(aliases=["gbabios"], cooldown=commands.Cooldown(0, 0, commands.BucketType.channel)) - async def gbabiosdump(self, ctx): - """Links to GBA Bios Extraction Tutorial""" - embed = discord.Embed(title="GBA Bios Extraction Tutorial", color=discord.Color(0x551A8B)) - embed.set_author(name="Glazed_Belmont") - embed.set_thumbnail(url="https://i.imgur.com/TgdOPkG.png") - embed.url = "https://glazedbelmont.github.io/gbabiosdump/" - embed.description = "Basic tutorial to extract a GBA bios" - await ctx.send(embed=embed) - - @tutorial.command(aliases=["fuse-3ds", "fuse", "fuse3ds"], cooldown=commands.Cooldown(0, 0, commands.BucketType.channel)) - async def ninfs(self, ctx): - """Link to ninfs tutorial.""" - embed = discord.Embed(title="Extract and Decrypt games, NAND backups, and SD contents with ninfs", color=ConsoleColor.n3ds()) - embed.description = cleandoc(""" - This is a tutorial that shows you how to use ninfs to extract the contents of games, \ -NAND backups, and SD card contents. Windows, macOS, and Linux are supported. - """) - embed.url = "https://gbatemp.net/threads/499994/" - await ctx.send(embed=embed) - - @tutorial.command(aliases=["appatch", "dsscene"], cooldown=commands.Cooldown(0, 0, commands.BucketType.channel)) - async def ap(self, ctx): - """Anti-piracy patching guide""" - embed = discord.Embed(title="AP Guide", color=ConsoleColor.legacy()) - embed.set_author(name="Glazed_Belmont") - embed.set_thumbnail(url="https://i.imgur.com/TgdOPkG.png") - embed.url = "https://glazedbelmont.github.io/appatching/" - embed.description = "An AP-Patching guide" - await ctx.send(embed=embed) - - @tutorial.command(aliases=["cheats", "3dscheats"], cooldown=commands.Cooldown(0, 0, commands.BucketType.channel)) - async def cpcheats(self, ctx): - """Checkpoint/Rosalina cheat guide""" - embed = discord.Embed(title="3DS Cheats Guide", color=discord.Color.purple()) - embed.set_author(name="Krieg") - embed.set_thumbnail(url="https://nintendohomebrew.com/assets/img/krieg.png") - embed.url = "https://3ds.eiphax.tech/cpcheats.html" - embed.description = "A guide to using cheats with Checkpoint and Rosalina" - await ctx.send(embed=embed) - @tutorial.command(aliases=["ftpd"], cooldown=commands.Cooldown(0, 0, commands.BucketType.channel)) async def ftp(self, ctx, console=None): """FTPD/WinSCP ftp guide""" @@ -1257,10 +678,10 @@ async def ftp(self, ctx, console=None): return embed = discord.Embed() - if self.check_console(console, channel_name, "3ds"): + if check_console(console, channel_name, "3ds"): embed.title = "3DS FTP Guide" embed.url = "https://3ds.eiphax.tech/ftp.html" - elif self.check_console(console, channel_name, ("nx", "ns", "switch")): + elif check_console(console, channel_name, ("nx", "ns", "switch")): embed.title = "Switch FTP Guide" embed.url = "https://nx.eiphax.tech/ftp.html" embed.colour = discord.Color.purple() @@ -1269,43 +690,6 @@ async def ftp(self, ctx, console=None): embed.description = "A guide to using ftp with FTPD and WinSCP" await ctx.send(embed=embed) - @tutorial.command(aliases=["ntrplugins"], cooldown=commands.Cooldown(0, 0, commands.BucketType.channel)) - async def plugins(self, ctx): - """NTR Plugins guide""" - embed = discord.Embed(title="3DS NTR Plugins Guide", color=discord.Color.purple()) - embed.set_author(name="Krieg") - embed.set_thumbnail(url="https://nintendohomebrew.com/assets/img/krieg.png") - embed.url = "https://3ds.eiphax.tech/ntrplugins" - embed.description = "A guide to using plugins with NTR" - await ctx.send(embed=embed) - - @tutorial.command(aliases=["citraobs"], cooldown=commands.Cooldown(0, 0, commands.BucketType.channel)) - async def obscitra(self, ctx): - """OBS and Citra guide""" - embed = discord.Embed(title="OBS and Citra Guide", color=discord.Color.purple()) - embed.set_author(name="Krieg") - embed.set_thumbnail(url="https://nintendohomebrew.com/assets/img/krieg.png") - embed.url = "https://kriegisrei.github.io/obscitra/" - embed.description = "A guide to recording Citra with OBS" - await ctx.send(embed=embed) - - @tutorial.command(cooldown=commands.Cooldown(0, 0, commands.BucketType.channel)) - async def gbadump(self, ctx): - """Links to GBA Dump guide""" - embed = discord.Embed(title="Dumping GBA games", color=discord.Color.purple()) - embed.set_thumbnail(url="https://wiki.no-intro.org/resources/assets/wiki.png") - embed.url = "https://wiki.no-intro.org/index.php?title=Game_Boy_Advance_Dumping_Guide" - embed.description = "How to dump GBA cartridges" - await ctx.send(embed=embed) - - @tutorial.command(aliases=["carttodigitalsave", "ctdsave"]) - async def transfersave(self, ctx): - """Links to cart to digital version save transfer tutorial""" - embed = discord.Embed(title="Cart to digital version save transfer tutorial", color=discord.Color.purple()) - embed.url = "https://redkerry135.github.io/transfersave/" - embed.description = "A tutorial about how to transfer a save from the cart version of a game to a digital version of that game." - await ctx.send(embed=embed) - @tutorial.command(aliases=["theme"]) async def themes(self, ctx, console=None): """Links to tutorials for installing themes""" @@ -1319,12 +703,12 @@ async def themes(self, ctx, console=None): ctx.command.reset_cooldown(ctx) return - if self.check_console(console, channel_name, '3ds'): + if check_console(console, channel_name, '3ds'): embed = discord.Embed(title="3DS Themes Tutorial", color=discord.Color.dark_orange()) embed.url = "https://itspizzatime1501.github.io/guides/themes/" embed.description = "Tutorial for installing themes on the 3DS" await ctx.send(embed=embed) - elif self.check_console(console, channel_name, ('nx', 'switch', 'ns')): + elif check_console(console, channel_name, ('nx', 'switch', 'ns')): embed = discord.Embed(title="Switch Themes Tutorial", color=discord.Color.dark_orange()) embed.url = "https://nh-server.github.io/switch-guide/extras/theming/" embed.description = "Tutorial for installing themes on the Switch" @@ -1534,12 +918,12 @@ async def db(self, ctx, console=None): await ctx.send(f"Please specify a console. Valid options are: {', '.join([x for x in systems])}.") ctx.command.reset_cooldown(ctx) return - if self.check_console(console, channel_name, '3ds'): + if check_console(console, channel_name, '3ds'): embed = discord.Embed(title="3DS Database", color=ConsoleColor.n3ds()) embed.url = "http://3dsdb.com/" embed.description = "3DS database for game releases." await ctx.send(embed=embed) - elif self.check_console(console, channel_name, ('nx', 'switch', 'ns')): + elif check_console(console, channel_name, ('nx', 'switch', 'ns')): embed = discord.Embed(title="Nintendo Switch Database", color=ConsoleColor.switch()) embed.url = "http://nswdb.com/" embed.description = "Nintendo Switch database for game releases." @@ -1615,7 +999,7 @@ async def downgrade(self, ctx, console=None): await ctx.send(f"Please specify a console. Valid options are: {', '.join([x for x in systems])}.") ctx.command.reset_cooldown(ctx) return - if self.check_console(console, channel_name, ('nx', 'switch', 'ns')): + if check_console(console, channel_name, ('nx', 'switch', 'ns')): embed = discord.Embed(title="Downgrading on the Switch: Why you shouldn't do it", color=ConsoleColor.switch()) embed.description = "Downgrading your firmware on the Switch is not recommended. This will generally lead to a lot of issues and won't solve anything." embed.add_field(name="Possible side effects from downgrading:", value=cleandoc(""" @@ -1625,7 +1009,7 @@ async def downgrade(self, ctx, console=None): * Save data compatibility issues. * Games not launching. """)) - elif self.check_console(console, channel_name, '3ds'): + elif check_console(console, channel_name, '3ds'): embed = discord.Embed(title="Downgrading on the 3DS: Why you shouldn't do it", color=ConsoleColor.n3ds()) embed.description = "Downgrading your firmware on the 3DS is not recommended. Although you *can*, you won't get any benefits from it." embed.add_field(name="Possible side effects from downgrading:", value=cleandoc(""" @@ -1668,5 +1052,9 @@ async def unidb(self, ctx, *, query: str): await ctx.send(file=f, embed=embed) +add_md_files_as_commands(Assistance) +add_md_files_as_commands(Assistance, join(Assistance.data_dir, 'tutorial'), namespace=Assistance.tutorial) + + def setup(bot): bot.add_cog(Assistance(bot)) diff --git a/example-embed.png b/example-embed.png new file mode 100644 index 0000000000000000000000000000000000000000..d23ebd0da5a554596e91892e7b622e9462ba49f2 GIT binary patch literal 81411 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

DEqpY|@4!=L=T%Pf@ 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 z zE^a{m)CBBXW~tOrYMrYFgek!K^`@H-er*YVnzVBD0L$n<#6sMne* z6PeS7iesNr!>^~L>Pqpjwz0#KODabEfGYV>y4rG<6Cie~77vPSihaG13~zXL$y zH1{z&F1BxWvN%>zGUAr5uG=jG@-dDLNn)2;Wi|-X<#x5?;_9T52Jrr757BcVX(J*XzUkSezKtDv^gZd(+)=i;+oj6a~M4E z3Dp@6u_$C1d_?X}T(9X68HjwZ!x{uX{c!nx-q<7~5<&>ph~Mcx@5kYCUXZln2FjTs zulL=}B}`q9&$;`5T(*RWoA>lsyV>>{6F*%JSMvD1C&TpVG<(H5{v5-+2{l%)tIE?o+q`rLH8C#jq|YgnGQ=8x_FLz*bWESxBOcuM&BLE z%1o`MjEaraw$ga65>Wi>ZC~C--#LxXxs5bB*tG)Hf*rek^6U^a_iK}(#5mhFm7a=M zDa>hP9J=dm+U9IBVM^>C@>#d<5@i=4Qxef1MnC$iL^i=DQz>L}cRdGqd>PFM8bsaK z*p`YAFOG3H(!@5v>REAlXxG|c9Q4tt$?fmgU40)AOB@?XFV=!~@+&xEZK2l=t>13q z1fK?PJ3DOjYx*ICq%zm>mHsCc=hci{;d1WP#UpYi#X<3deIG_lu zM2}AxlC^8!w=D|v{eC_?@8ay511;mk;Pm6x;_PbvG_p5me+Cerr`sdDURnR2Qr-e1 zeWGS-pQps#XUlTV<9kOjtNnk-X|dVnwOKb|Gxo&{C&x!M1YALoRyh0K5;MD>+xM9o zy$Tg4_j?!Ig&5Y|66RcV(@`ur5GU*$fZL;}Lj9h5LfPK;tnJR`jqEFdt{QwXvQGpH zB9>d@`i(2HDda_MV`{bEm%(9UnY0pu{BNn*^3fIzqycOB_QUw@2PZu?H|c0XwrEWU zWiBm4cG4AhTt~vxQ@?|qKFxAYg{KeL=(SnhG1ikK%xv(6%^}6n^?N0X3M7$1(7F9$|9nvo)qLx#3B1N=UTW zJ#$g$ss7ALlD%e5$P7lnGq^gzb~UTr*K;Dc$?qD@e0dm4W8d@ zpw8)HtJi~Sxy3C^|v8rHX|N@It@*DY!O*!6P#lzibZ&%V~r% z`ioo`H4EazWwTd{wZeevQl~1K3V8kN$(D2(W0(xm`Oj(`3;ZzyAjyaM!+wk|r} zc_pT7QDPJL zpy0w=HMdG>2S_^s-hAa{vh2nGTr3<$saOgCsDnoLF-Lv$ilMUQzk9~Y!hL_GT!;|Cg)&{RE42P0j<>i>k&iS@MR)d3 z(cn+rpn$?Y_+YvKlqrq<-;pCJVd)FcDK*W1R~bwPssc5qgsSrYhO+1TSf$qNI@RRA z%@(eqBrZ%XZ;?ns|9g64HZTt=sce!c{#6aifr10&(W&C@zh_iI6##)1A7;2@=3FXz Q1^h{g$%|Hq7<~P|03v*BX8-^I literal 0 HcmV?d00001 diff --git a/utils/mdcmd.py b/utils/mdcmd.py new file mode 100644 index 000000000..14a3761af --- /dev/null +++ b/utils/mdcmd.py @@ -0,0 +1,218 @@ +from collections import defaultdict +from glob import iglob +from os.path import basename, join +from typing import TYPE_CHECKING + +import discord +from discord.ext import commands +from utils.utils import ConsoleColor + +if TYPE_CHECKING: + from typing import Dict, List, Optional, Tuple, Type +empty = discord.Embed.Empty + +systems_no_aliases = ('3ds', 'wiiu', 'vwii', 'switch', 'wii', 'dsi') +aliases = { + 'nx': 'switch', + 'ns': 'switch' +} +name_to_aliases = defaultdict(set) +for k, v in aliases.items(): + name_to_aliases[v].add(k) + +# compatibility +systems = systems_no_aliases + tuple(aliases) + ('legacy',) + + +def parse_header(header_raw: str): + header: Dict[str, str] = { + 'title': empty, + 'url': empty, + 'author.name': None, + 'author.url': empty, + 'author.icon-url': empty, + 'help-desc': None, + 'aliases': '', + 'color': None, + 'thumbnail-url': empty, + } + + for line in header_raw.splitlines(): + if line.startswith('---'): + continue + key, value = line.split(':', maxsplit=1) + key = key.strip() + value = value.strip() + header[key] = value + + return header + + +def parse_body(body_raw: str): + body_raw = body_raw.strip() + # first one should be the descripton and will have no header + parts = [] + current_header = '' + current_body = [] + + def doadd(): + parts.append((current_header, '\n'.join(current_body))) + + for line in body_raw.splitlines(): + if line.startswith('#'): + # This does not work entirely how markdown should work. + # It seems a header requires a space between the # and the text. + # Example: "#test" should not work but "# test" does. + # This isn't really worth trying to check for however. + doadd() + current_header = line.lstrip('#').lstrip(' ') + current_body = [] + else: + current_body.append(line) + + if current_header or current_body: + doadd() + + return parts + + +def create_embed(header: 'Dict[str, str]', body: 'List[Tuple[str, str]]', embed_color: discord.Color): + description = body[0][1] + embed = discord.Embed( + title=header['title'], + description=description, + url=header['url'], + color=embed_color, + thumbnail=None, + ) + if header['author.name']: # this field is required + embed.set_author(name=header['author.name'], url=header['author.url'], icon_url=header['author.icon-url']) + embed.set_thumbnail(url=header['thumbnail-url']) + + # first one is used as the embed description + for field in body[1:]: + embed.add_field(name=field[0], value=field[1], inline=False) + + return embed + + +def parse_md_command(md_text: str, format_map: dict, embed_color: discord.Color): + header_raw, body_raw = md_text.split('\n\n', maxsplit=1) + + body_raw = body_raw.format_map(format_map) + + header = parse_header(header_raw) + body = parse_body(body_raw) + + if header['color']: + # override with the custom color + embed_color = discord.Color(int(header['color'], 16)) + + return header, create_embed(header, body, embed_color) + + +def md_file_to_embed(md_path: str, format_map: dict): + colors = { + '3ds': ConsoleColor.n3ds(), + 'wiiu': ConsoleColor.wiiu(), + 'vwii': ConsoleColor.wiiu(), + 'wii': ConsoleColor.wii(), + 'switch': ConsoleColor.switch(), + 'legacy': ConsoleColor.legacy(), + 'dsi': ConsoleColor.legacy(), + 'all': empty # default embed color + } + + with open(md_path, 'r', encoding='utf-8') as f: + fn = basename(md_path) + name, console, _ = fn.rsplit('.', maxsplit=2) + return (name, console, *parse_md_command(f.read(), format_map, colors[console])) + + +def check_console(message, channel, consoles): + message = message.lower() + if isinstance(consoles, str): + consoles = (consoles,) + if message in consoles: + return True + elif ("wii" not in consoles or channel.startswith("legacy")) and channel.startswith(consoles) and message not in systems: + return True + return False + + +def get_console_name(console): + return aliases.get(console, console) + + +def add_md_files_as_commands(cog_class: 'Type[commands.Cog]', md_dir: str = None, *, namespace=commands, format_map=None): + + def make_cmd(name: str, help_desc: 'Optional[str]', embeds: 'Dict[str, discord.Embed]', aliases: list): + if len(embeds) > 1: + # multi-console commands require a check + async def cmd(self, ctx, *, consoles=''): + supported_consoles = tuple(embeds) + # replace aliases with the expected console + requested_consoles = {get_console_name(c) for c in consoles.split()} + # and then check if any of the consoles are supported here + requested_consoles = {c for c in requested_consoles if c in supported_consoles} + channel_name = ctx.channel.name if not isinstance(ctx.channel, discord.DMChannel) else '' + + if not requested_consoles: + if channel_name.startswith(supported_consoles): + requested_consoles = {'auto'} + else: + valid = set(supported_consoles) + for v in supported_consoles: + valid |= name_to_aliases[v] + await ctx.send(f'Please specify a console. Valid options are: {", ".join(sorted(valid))}') + + ctx.command.reset_cooldown(ctx) + return + + for console in requested_consoles: + for embed_console, embed in embeds.items(): + await ctx.send(f'chosen console: {console!r} {embed_console!r}') + if check_console(console, channel_name, embed_console): + await ctx.send('works') + await ctx.send(embed=embed) + else: + # single-console commands can simply print the one embed + async def cmd(self, ctx): + # this is kinda ugly, but basically it gets the first (and only) value of the dict + await ctx.send(embed=next(iter(embeds.values()))) + + cmd.__name__ = name + # i figured this was easier than dealing with the multiple attributes for command help + cmd.__doc__ = help_desc + + # this feels _wrong_ but is probably the best way to do this + return namespace.command(name=name, aliases=aliases)(cmd) + + new_commands = defaultdict(dict) + aliases = defaultdict(list) + helpdescs = defaultdict(lambda: None) + + if md_dir is None: + md_dir = cog_class.data_dir + + if format_map is None: + try: + format_map = cog_class.format_map + except AttributeError: + format_map = None + + for md in iglob(join(md_dir, '*.md')): + command, console, header, embed = md_file_to_embed(md, format_map) + new_commands[command][console] = embed + if header['aliases']: + aliases[command].extend(header['aliases'].split(',')) + if header['help-desc']: + # in case some don't have a help-desc, don't delete a previous one + helpdescs[command] = header['help-desc'] + + for command, embed_dict in new_commands.items(): + new_aliases = list(set(aliases[command])) + command_obj = make_cmd(command, helpdescs[command], embed_dict, new_aliases) + setattr(cog_class, command, command_obj) + # there has to be a better way to do this... + cog_class.__cog_commands__.append(command_obj)