/Rosetta-Public

Python 3 Granblue Fantasy-themed Discord Bot

Primary LanguagePythonMIT LicenseMIT

Rosetta

image

Caution

If you're looking for an invite for my instance, it's not open to public.
Use this repo if you have the how-to to host your own instance.
If you're somehow close to me in some way however, feel free to ask and I'll see if it's possible.

Requirements

  • System: The bot itself requires a few hundred MB of RAM, 100 MB of disk space and a stable and fast internet connection, and a CPU with good single treaded performance. Expect the RAM and CPU usage to ramp up as you invite the bot in more servers.
  • Software: Python 3.11. See requirements.txt for the list of third-party modules used in this project.

Important

The bot is designed to run on Linux but it has been tested to run on Windows.
It's untested on other systems but it should work on anything supported by the required Python version.

Tip

jemalloc should be installed if you plan to run it for long periods of time. It's to avoid the high Memory Usage problem encountered on its predecessor, MizaBOT, which was caused by memory fragmentation (although, Rosetta evolved a lot since, I don't know if it's still victim of this issue).
Refer to the jemalloc repository, my Dockerfile for how I set it up, and the Python documentation for details.

General Informations

  • Rosetta is a Discord Bot themed around the Browser Game Granblue Fantasy, providing utility commands and advanced features to the users, along with moderation and more generalistic commands.
  • This project is a fork of MizaBOT latest version featuring massive improvements, bug fixes, security fixes and so on.
  • Saving is done on Google Drive. If you want to use something else, you'll have to rewrite the drive.py component.

The assets folder contains various images and other files used by the bot:

  • The various icon.png files are automatically set at the beginning of the month.
  • The content of the emojis folder is used during the setup (see below).
  • The content of the hosted folder is merely a backup of some files I host on a github pages. In the advent I disappear, you must rehost them somewhere, and update their links in the code.
  • font.ttf is used by one command specific to my crew. You can remove it if you remove the corresponding cog.

The cogs folder contains the bot Cogs, which are pretty much Command groups:

  • Never delete this folder or init.py inside.
  • You can however delete the others or add your own cogs.
  • In particular, youcrew.py contains commands specific to my crew. You can remove it if you want.
  • Most cogs are standalone and should work even if others are missing. However, I suggest to never remove admin.py, moderation.py, gw.py and granblue.py.

The components folder contains the bot components, which are piece of codes needed for it to work.
The views folder contains the bot interactions to make interfaces and such for some commands.

The tools folder contains a few standalone pieces of code which might help you:

  • generate_help.py generates the help page html.

Warning

If you wrote your own Cogs, I recommend following how I format my syntax or this tool might not be able to detect your commands properly.

Warning

The tool attempts to write the HTML file to my github page repo before doing so in the current directory.
If you want to change this behavior, look around line 529.

  • save_gzip.py and save_lzma.py can be used to decompress/compress a save file. You can drag and drop a file on them but I suggest using them in a terminal/command prompt. Current save data are saved to the drive in the LZMA format. save_gzip.py is technically not used anymore.

Caution

It's a good practice to always make a copy of your save data before attempting any manipulation on it.

Usage

Method 1:

Important

This is the intended way to use the bot.

Simply build and run the Dockerfile. Refer to Docker Documentation for details.

Method 2:

Important

This method is mostly used for testing, although the end result should be analog to method 1.

Open a command prompt (Windows) or terminal (Linux) and run pip install -r requirements.txt in the bot folder, to install the third-party modules. You only need to do it once or if it gets updated.
Then, you can run the bot with python -O bot.py -run to start the bot (-O is optional).

There are three ways to start the bot:
-run: Start the bot.
-test: Start the bot but stop before connecting to Discord. Used to test if bot can start properly.
-remove: Start the bot without loading any commands. Mainly used to desync and remove a test bot commands from a server.
-emoji: To initialize the emojis. See in the guide below.

You can also add the following to change its behavior:
-debug: Start the bot in debug mode. config_test.json will be loaded and will partially overwrite config.json in memory, so you can use a different Discord token. The bot won't write on the Google Drive in this state, nor load all the tasks.

Stop Rosetta

A simple CTRL+C or sending a SIGTERM or SIGINT signal will make the bot saves and exits gracefully.
The bot returns the following codes upong exiting:

  • 0: Exited properly.
  • 1: Failed to load config.json on boot. Check if the file is missing or corrupted.
  • 2: Failed to load save.json on boot. Check if the file is corrupted.
  • 3: Failed to load the save data from Google Drive. Maybe Google is having troubles or the save file is missing from the folder.
  • 4: Failed to connect to Google Drive with your Service account.
  • 5: Failed to initialize a bot component.
  • 6: Bot instance initialization failure.

Note

The /owner bot reboot command doesn't actually reboot the bot, it's a simple shutdown with the exit code 0.
My instance is setup with Watchtower and automatically reboot once stopped, hence why it's called reboot.

Setup

Creating config.json and your Discord Application

Important

If you haven't yet, go to your Discord account settings, Advanced and turn on Developer Mode.
Now you can right click on anything to copy their IDs.

The bot is configured with a file called config.json. Create one in its folder and paste the following:

{
    "tokens" : {
        "discord" : "DISCORD_TOKEN",
        "drive" : "SAVE_FOLDER_ID",
        "upload" : "",
        "files" : "FILE_FOLDER_ID"
    },
    "ids" : {
        "debug_channel" : BOT_DEBUG_CHANNEL_ID,
        "image_upload" : BOT_IMAGE_UPLOAD_CHANNEL_ID,
        "debug_server" : BOT_SERVER_ID,
        "owner" : YOUR_USER_ID_ID,
        "you_server" : YOUR_CREW_OR_THE_BOT_SERVER_ID,
        "you_announcement" : YOUR_CREW_ANNOUNCEMENT_CHANNEl_ID,
        "you_meat" : YOUR_CREW_MEAT_CHANNEl_ID,
        "you_member" : YOUR_CREW_MEMBER_ROLE_ID,
        "atkace" : YOUR_CREW_ATK_ACE_ROLE_ID,
        "deface" : YOUR_CREW_DEF_ACE_ROLE_ID,
        "fo" : YOUR_CREW_FIRST_OFFICER_ROLE_ID,
        "gl" : YOUR_CREW_LEADER_ROLE_ID,
        "gbfg" : 339155308767215618,
        "gbfg_new" : 402973729942142988
    },
    "games" : [
        "Granblue Fantasy",
        "Granblue Fantasy: Versus Rising",
        "Granblue Fantasy: Relink"
    ],
    "emotes" : {
        "fire" : 614733962736042005,
        "water" : 614733963399004170,
        "earth" : 614733962937630752,
        "wind" : 614733962991894529,
        "dark" : 614733962937499648,
        "light" : 614733963054809098,
        "R" : 614733962807607316,
        "SR" : 614733962543366155,
        "SSR" : 614733962832773120,
        "sword" : 614733962744561675,
        "dagger" : 614733962899750912,
        "spear" : 614733963105140736,
        "axe" : 614733963076042762,
        "staff" : 614733962979442688,
        "gun" : 614733963105271809,
        "melee" : 614733963008933888,
        "bow" : 614733962924916751,
        "harp" : 614733962669064205,
        "katana" : 614733962790567938,
        "skill1" : 614733961717088257,
        "skill2" : 614733962681647124,
        "atk" : 614733962719395850,
        "hp" : 614733962966728714,
        "summon" : 614733962555686984,
        "kmr" : 614735088391028737,
        "gw" : 614733962996350977,
        "st" : 614733962937630764,
        "time" : 614733963222843392,
        "1" : 614733962178330665,
        "2" : 614733962643767322,
        "3" : 614733962698555392,
        "4" : 614733962711138314,
        "5" : 614733962476257292,
        "6" : 614733962601824257,
        "red" : 614733963017060363,
        "gold" : 614733962983768094,
        "wood" : 614733963092688896,
        "loot" : 614733962622926849,
        "mark" : 614733962698555415,
        "mark_a" : 614736075272880139,
        "clock" : 614733962652155906,
        "question" : 614733963004477445,
        "cog" : 614736426516480009,
        "ensign" : 700731360012271616,
        "captain" : 700732615879032842,
        "atkace" : 614733963071586344,
        "deface" : 614733963029643264,
        "foace" : 614733962870259713,
        "crystal" : 614733963067392015,
        "crown" : 614733963046551561,
        "misc" : 653259257998999584,
        "singledraw" : 734795587207299094,
        "tendraw" : 734795587630661752,
        "crystal0" : 756821583636725780,
        "crystal0+" : 756821618139070575,
        "crystal1" : 756822019491758160,
        "crystal2": 756821671230439424,
        "crew": 766297948467494924,
        "lyria" : 920696070764376084,
        "shrimp": 959392828847448124,
        "star0": 1050429257995792424,
        "star1": 1050429259593830400,
        "star2": 1050429261435117598,
        "star3": 1050429263217692714,
        "star4": 1056646303527997450,
        "arcarum": 1052232990727622706
    },
    "granblue": {
        "gbfgcrew" : {"aion no me":"645927", "aion":"645927", "bellabarca":"977866", "bullies":"745085", "bullied":"1317803", "chococat":"940560", "chococats":"940560", "cogfriends":"1049216", "cog":"1049216", "cowfag":"841064", "cowfags":"841064", "\u4e2d\u51fa\u3057\u306e\u5e78\u305b":"1036007", "cumshot":"1036007", "cumshot happiness":"1036007", "dem bois":"705648", "ded bois":"705648", "dembois":"705648", "demboi":"705648", "dedbois":"705648", "fleet":"599992", "haruna":"472465", "cug":"1161924", "lilypals":"1161924", "lilypal":"1161924", "\u5c0f\u3055\u306a\u5973\u306e\u5b50":"432330", "little girls":"432330", "lg":"432330", "little girl":"432330", "sixters":"1380234", "sixter":"1380234", "soy":"1601132", "onion":"1601132", "onions":"1601132", "oppaisuki":"678459", "oppai":"678459", "pcs":"632242", "quatrebois":"1141898", "quatreboi":"1141898", "the bathtub":"1580990", "thebathtub":"1580990", "bathtub":"1580990", "(you)":"581111", "you":"581111", "(you) too":"1010961", "(you)too":"1010961", "u2":"1010961", "youtoo":"1010961", "you too":"1010961", "a-team":"1744673", "ateam":"1744673", "a":"1744673", "grape":"1807204", "grapes":"1807204", "toot":"844716", "nier":"1837508", "nier2":"1880420"},
        "othercrew" : {"ssf":"677159", "hsp":"147448", "kingdom":"1377462", "atelier":"418206", "lum1":"388489", "lum2":"401211", "beafriends":"940700", "waifuanon":"588156", "lolibabas":"1593480", "lolibaba":"1593480", "hagchads":"1593480", "hagchad":"1593480", "mesugaki":"1593480", "lazytown":"1586134", "mavericks":"1629318", "maverick":"1629318"}
    }
}

Go to the Discord Developer Portal > Applications > New Application and create your application.
Go to Bot on the left and click Reset Token. Your Bot Token will appear, copy it and replace DISCORD_TOKEN with it in config.json (don't forget to put it between quotes ", it's a string).
Next, turn on the Message Content Intent, below.

Note

If you ever change the Intents used by your bot instance, you'll also need to update them in bot.py at the end of the _init_() function, or it won't take effect. Refer to the documentation for details.

Configuring your Google Service Account

The Google API website is a pain in the butt to use, so I'll only give general steps.

  • Find a way to create an application.
  • Go to Credentials on the left, create a new Service account.
  • Copy the email associated with this service account (it should be something like XXX@YYYY.iam.gserviceaccount.com ).
  • You might need to give it some permissions (Permissions tab, Grant Access, use the application email.).
  • Add a new key (Key tab), pick the JSON format and accept. Rename this file to service-secrets.json and put it in the bot folder, alongside config.json.

Go to your Google Drive and create two folders, one for the bot save data and one for GW related files.
Open the save data one and copy the url. It should be something like https://drive.google.com/drive/folders/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA. Copy the AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA part and replace SAVE_FOLDER_ID in config.json (don't forget to put it between quotes ", it's a string).
Do the same thing for the other folder and FILE_FOLDER_ID.
The third folder ID is unused but you are free to create one and set the key in config.json too.

Then, for each of those folders, right click on them and select Share. Add your service account (again, using the email you copied) to each of them.

Setup a Server for the Bot and invite it

Create a Discord Server for your bot.
The bot requires three text channels to be created:

  • One to output debug infos.
  • One to upload images.
  • One to post updates.

Create them.

Right click on the debug channel, copy the ID and replace BOT_DEBUG_CHANNEL_ID in config.json.
Same for the second and BOT_IMAGE_UPLOAD_CHANNEL_ID.
Same for the third and BOT_SERVER_UPDATE_CHANNEL_ID.

Right click on the Server itself (in your server list), same thing and replace BOT_SERVER_ID.
Next, post a message, right click on your avatar and copy your own ID to replace YOUR_USER_ID_ID.

Do the same thing to fill the remaining IDs.
The four last IDs are related to the /gbfg/ server and are already set.

If you did everything properly, you're set. On the Discord Developer Portal, copy the Application ID (under *General Information).
In the following: https://discord.com/api/oauth2/authorize?client_id=XXXXXXXXXXXXXXXX&permissions=1644905889015&scope=bot%20applications.commands, replace XXXXXXXXXXXXXXXX by this ID. This will be your bot invite link.
Use it now and invite your bot to the server (no need to start the bot). If you did it right, it should show up in the member list.

First Boot

Use whatever method you chose at the start to boot the Bot.
If Rosetta is starting properly, you should get a Rosetta is Ready message in the channel that you set as your debug one.
The logs should also look similar to this:

INFO:Rosetta:2024-02-18 17:43:39 | [BOOT] Loading the config file...
INFO:Rosetta:2024-02-18 17:43:39 | [BOOT] Downloading the save file...
INFO:Rosetta:2024-02-18 17:43:40 | [BOOT] Reading the save file...
WARNING:disnake.client:PyNaCl is not installed, voice will NOT be supported
INFO:Rosetta:2024-02-18 17:43:40 | [BOOT] Initialization complete
INFO:Rosetta:2024-02-18 17:43:40 | [MAIN] Loading cogs...
INFO:Rosetta:2024-02-18 17:43:41 | [MAIN] All cogs loaded
INFO:Rosetta:2024-02-18 17:43:41 | [MAIN] v11.1.5 starting up...
INFO:Rosetta:2024-02-18 17:43:47 | [MAIN] Rosetta is ready

Caution

If it doesn't start, be sure to check for error messages and so on, before reporting to me.

Important

If it starts, congratulations.
All the remaining setup is done via the /owner commands.
Refer to Additional Informations below if you need more help.

Important

The emotes used by the bot will likely not be working.
See one of the section below for moe informations.

You can stop the bot with a CTRL+C or by sending a SIGTERM or SIGINT signal.

Tip

On Windows, the Task Manager sends a SIGKILL, so CTRL+C is preferred.
On Linux, you can do something like kill -INT <bot_pid> in a terminal.

Other config.json keys

games: The list of game which will appear in the bot status. Feel free to modify it. At least one game is required in the list.

granblue: The bot has been historically used by the /gbfg/ Discord Server and its crews are set here for certain commands (notably the /gbfg ones).
You are free to remove or change the crews if you want, but don't remove it entirely.

Debug Mode

Tip

You can skip this section if you don't plan to use it.

Using the -debug argument requires to set up config_test.json.
Same principle, but shorter:

{
    "tokens" : {
        "discord" : "DISCORD_TOKEN",
        "drive" : "SAVE_FOLDER_ID",
        "upload" : "",
        "files" : "FILE_FOLDER_ID"
    },
    "debug" : null
}

For DISCORD_TOKEN, simply create a second application and bot and put its token here.
For the folders, either reuse the existing ones or make new ones if you want to use separate data.
Do note the bot is unable to write to your Google Drive in this mode.

Set the Emojis

The bot uses various emojis to pretty up its messages.
Discord Bots have innate Nitro, kind of. We use this fact by uploading the emojis to a server which the bot has a permanent access to (such as Server you made during setup).

There are two ways to do it:

Method 1:

On your local machine, in a command prompt or terminal, run python bot.py -emoji in the bot folder.
The bot will boot, do various checks and, if your Server (the one set in config.json, under debug_server) has enough emoji spaces, it will upload all emojis.
Once done, it will ask you to confirm and then save the changes to config.json.
You can then use this new config.json to run your bot as normal.

Warning

It can take time if you're hit by a Discord Rate Limit. Simply wait if it happens, the log will tell you how much time.

Important

If you're hit by an error, read the logs carefully.
If the errors is not of your fault, please report it to me (ideally with the complete log).
In the meantime, use Method 2 below.

Here's what the log of a successful run should look like (on version 11.8.0):

2024-07-14 15:02:41 | [INIT EMOJI] 66 file(s) in the 'assets/emojis' folder
2024-07-14 15:02:41 | [INIT EMOJI] 66 emoji(s) expected in 'config.json'
2024-07-14 15:02:41 | [INIT EMOJI] Checking correspondances...
2024-07-14 15:02:41 | [INIT EMOJI] Check finished
2024-07-14 15:02:41 | [INIT EMOJI] 50 normal emoji(s) and 16 animated emoji(s)
2024-07-14 15:02:41 | [INIT EMOJI] Connecting to Discord...
2024-07-14 15:02:44 | [INIT_EMOJI] Checking for free emoji slots...
2024-07-14 15:02:44 | [INIT_EMOJI] 50 free normal emoji slots for 50 required
2024-07-14 15:02:44 | [INIT_EMOJI] 50 free animated emoji slots for 16 required
2024-07-14 15:02:44 | [INIT_EMOJI] Not enough emoji slots, please make space and try again
2024-07-14 15:02:44 | [INIT_EMOJI] Uploading Emojis...
2024-07-14 15:17:44 | [INIT_EMOJI] Uploading Complete...
2024-07-14 15:17:44 | [INIT EMOJI] Disconnected from Discord
2024-07-14 15:17:44 | [INIT EMOJI] Initialization is over
2024-07-14 15:17:44 | [INIT EMOJI] Type 'confirm' to confirm the changes to config.json
2024-07-14 15:17:44 | [INIT EMOJI] Type 'cancel' to cancel
2024-07-14 15:17:44 | [INIT EMOJI] Loading config.json...
2024-07-14 15:17:45 | [INIT EMOJI] Creating backup...
2024-07-14 15:17:45 | [INIT EMOJI] Loading JSON...
2024-07-14 15:17:45 | [INIT EMOJI] Applying changes...
2024-07-14 15:17:45 | [INIT EMOJI] Saving config.json...
2024-07-14 15:17:45 | [INIT EMOJI] Emoji Initialization complete

Warning

A backup of config.json called config.bak.json is created upon confirmation.
If, for some reason, an error happens after its creation, I recommend making a manual copy of this backup if you plan to rerun the command, so it doesn't get overwritten

Important

If new emojis are added in future updates, don't rerun the command.
Use the Method 2 below instead..
python bot.py -emoji is intended to be used only once, on the first install.

Method 2:

The original and more tedious way.
Simply add the various emotes in assets/emojis to your Server.
Once done, retrieve their IDs one by one to set them in config.json.
A way is to add a slash before the emote in chat. Example, type \:whatever_your_emote_is: and it will show <:whatever_your_emote_is:0000000000000> where 0000000000000 is the emote ID.

An alternative way is to use the Chrome dev tools (F12 or CTRL+SHIFT+J) and the network tab then load your Emoji list. The ID of each emoji can be retrieved from their URLs.

Updating

First and foremost, if you want to run some sort of automation on this repo to always have the latest version, DON'T.
While I test the bot before pushing changes to this repository, I'm alone on this project and can't guarantee some mistakes haven't slipped in.
So here's my recommendations:

  1. Run and update your own copy. It can be a fork, a manual download, etc... Whatever you prefer.
  2. Read the commit list. If I just pushed a new version a few hours ago, maybe wait tomorrow. If it's a bugfix, it might be fine. The project is fairly mature, there shouldn't be a need to hurry to a new version.
  3. If the save file format is updated, your save file will automatically be converted to the new one. However, it won't be usable on old versions if, for some reason, you plan to downgrade. So, while it shouldn't be necessary, feel free to make a backup beforehand.
  4. If config.json is updated, so should be the example at the beginning of this readme. Make a backup, compare the differences, add what got added and remove what's not needed anymore.
  5. Check changes to cogs/admin.py for new Owner commands you might need ro run, to setup new features.
  6. If requirements.txt got updated and you aren't running the bot via a Dockerfile, you should run pip install -r requirements.txt again.
  7. If you developped your own Custom Cogs, you'll have to read the code and make sure no breaking changes got introduced. Major enough changes are usually when the first or second version number is increased (for example, 1.0.0 to 2.0.0 or 1.0.0 to 1.1.0).

Tip

If you're new to Github, you can see, on the page, when each files got modified for the last time on the right.

Additional Informations

Further setup might be required to use some features.
The /owner command group from the admin.py cog should contain what you need to set/edit some of the bot data.
Here's a quick overview of the sub command groups:

  • /owner utility: A bunch of small utility commands to execute code on the fly, answer a bug report, leave the server, etc... You should rarely need those.
  • /owner ban: Commands to ban/unban an user from using certain features from the bot.
  • /owner bot: Commands to stop the bot, get its invite link, list its guilds or change it's avatar with one in the assets folder.
  • /owner data: Commands related to the save data. You can trigger a save manually, retrieve a file, clean some data and so on. While you can technically load a save with the /owner data load command, a reboot is usually preferred.
  • /owner maintenance: Commands to manually set or clear a GBF maintenance date.
  • /owner stream: Commands to manually set a GBF upcoming stream date and infos.
  • /owner schedule: Commands to manually edit the GBF schedule or to force an update.
  • /owner account: Commands to set a GBF account on the bot. I won't provide any help with this.
  • /owner db: Commands to manually set a Dread Barrage event.
  • /owner gw: Commands to manually set and edit an Unite and Fight event.
  • /owner buff: Commands to debug the GW buff data used by my crew. I haven't used it in a long while.
  • /owner gacha: Commands to clear the gacha banner and change the /roll roulette command settings.

Warning

I rarely use most of those commands, there is a small chance it might be hiding some bugs.

If you want a GW.sql file for the ranking commands, you can go grab the most recent one here, rename it to GW.sql and put it in the "files" drive folder.

The cogs/youcrew.py Command Cog contains commands for my own crew. If you don't need them, you can simply delete the file.