diff --git a/.docs/Docker.md b/.docs/Docker.md index b07f0712..fa4870cf 100644 --- a/.docs/Docker.md +++ b/.docs/Docker.md @@ -10,14 +10,14 @@ Docker distribution of DiscordChatExporter provides a way to run the app in a vi This will download the [Docker image from the registry](https://hub.docker.com/r/tyrrrz/discordchatexporter) to your computer. You can run this command again to update when a new version is released. ```console -docker pull tyrrrz/discordchatexporter:stable +$ docker pull tyrrrz/discordchatexporter:stable ``` Note the `:stable` tag. DiscordChatExporter images are tagged according to the following patterns: - `stable` — latest stable version release. This tag is updated with each release of a new project version. Recommended for personal use. - `x.y.z` (e.g. `2.30.1`) — specific stable version release. This tag is pushed when the corresponding version is released and never updated thereafter. Recommended for use in automation scenarios. -- `latest` — latest (potentially unstable) build. This tag is updated with each new commit to the `master` branch. Not recommended, unless you want to test a new feature that has not been released in a stable version yet. +- `latest` — latest (potentially unstable) build. This tag is updated with each new commit to the `prime` branch. Not recommended, unless you want to test a new feature that has not been released in a stable version yet. You can see all available tags [here](https://hub.docker.com/r/tyrrrz/discordchatexporter/tags?ordering=name). @@ -26,19 +26,19 @@ You can see all available tags [here](https://hub.docker.com/r/tyrrrz/discordcha To run the CLI in Docker and render help text: ```console -docker run --rm tyrrrz/discordchatexporter:stable +$ docker run --rm tyrrrz/discordchatexporter:stable ``` To export a channel: ```console -docker run --rm -v /path/on/machine:/out tyrrrz/discordchatexporter:stable export -t TOKEN -c CHANNELID +$ docker run --rm -v /path/on/machine:/out tyrrrz/discordchatexporter:stable export -t TOKEN -c CHANNELID ``` If you want colored output and real-time progress reporting, pass the `-it` (interactive + pseudo-terminal) option: ```console -docker run --rm -it -v /path/on/machine:/out tyrrrz/discordchatexporter:stable export -t TOKEN -c CHANNELID +$ docker run --rm -it -v /path/on/machine:/out tyrrrz/discordchatexporter:stable export -t TOKEN -c CHANNELID ``` The `-v /path/on/machine:/out` option instructs Docker to bind the `/out` directory inside the container to a path on your host machine. Replace `/path/on/machine` with the directory you want the files to be saved at. @@ -47,7 +47,7 @@ The `-v /path/on/machine:/out` option instructs Docker to bind the `/out` direct > If you are running SELinux, you will need to add the `:z` option after `/out`, e.g.: > > ```console -> docker run --rm -v /path/on/machine:/out:z tyrrrz/discordchatexporter:stable export -t TOKEN -c CHANNELID +> $ docker run --rm -v /path/on/machine:/out:z tyrrrz/discordchatexporter:stable export -t TOKEN -c CHANNELID > ``` > > For more information, refer to the [Docker docs SELinux labels for bind mounts page](https://docs.docker.com/storage/bind-mounts/#configure-the-selinux-label). @@ -57,10 +57,21 @@ You can also use the current working directory as the output directory by specif - `-v $PWD:/out` in Bash - `-v $pwd.Path:/out` in PowerShell -For more information, please refer to the [Dockerfile](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/DiscordChatExporter.Cli.dockerfile) and [Docker documentation](https://docs.docker.com/engine/reference/run). +For more information, please refer to the [Dockerfile](https://github.com/Tyrrrz/DiscordChatExporter/blob/prime/DiscordChatExporter.Cli.dockerfile) and [Docker documentation](https://docs.docker.com/engine/reference/run). To get your Token and Channel IDs, please refer to [this page](Token-and-IDs.md). +## Unix permissions issues + +This image was designed with a user running as uid:gid of 1000:1000. + +If your current user has different IDs, and you want to generate files directly editable for your user, you might want to run the container like this: + +```console +$ mkdir data # or chown -R $(id -u):$(id -g) data +$ docker run -it --rm -v $PWD/data:/out --user $(id -u):$(id -g) tyrrrz/discordchatexporter:stable export -t TOKEN -g CHANNELID +``` + ## Environment variables DiscordChatExpoter CLI accepts the `DISCORD_TOKEN` environment variable as a fallback for the `--token` option. You can set this variable either with the `--env` Docker option or with a combination of the `--env-file` Docker option and a `.env` file. diff --git a/.docs/Dotnet.md b/.docs/Dotnet.md deleted file mode 100644 index aebf28b1..00000000 --- a/.docs/Dotnet.md +++ /dev/null @@ -1,55 +0,0 @@ -# Install .NET runtime - -**.NET 8.0 Runtime** is required by **DiscordChatExporter**. - -> **Note**: -> Installing the **.NET Runtime** is not required if you running **DiscordChatExporter** using [Docker](Docker.md). - -## Windows - -Both the GUI and the CLI flavors of **DiscordChatExporter** for Windows come with a bootstrapper that should automatically install all the required prerequisites. Simply run the application and follow the presented instructions. - -If, for some reason, you need to install the runtime manually, use one of the download links below: - -- [.NET 8.0 Runtime for **Windows x64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-windows-x86-installer) -- [.NET 8.0 Runtime for **Windows x86**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-windows-x64-installer) -- [.NET 8.0 Runtime for **Windows arm64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-windows-arm64-installer) - -> **Note**: -> [How can I tell if my computer is running a 32-bit (x86) or a 64-bit (x64) version of Windows?](https://support.microsoft.com/help/15056/windows-32-64-bit-faq) - -## macOS - -Use one of the download links below: - -- [.NET 8.0 Runtime for **macOS x64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-macos-x64-installer) -- [.NET 8.0 Runtime for **macOS arm64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-macos-arm64-installer) - -> **Note**: -> [Is my Mac using an Intel processor (x64) or Apple Silicon (arm64)?](https://support.apple.com/HT211814) - -## Linux - -Installing the .NET Runtime works differently depending on which Linux distribution you use. Check out the instructions for your distro: - -- [Ubuntu](https://docs.microsoft.com/dotnet/core/install/linux-ubuntu) -- [Debian](https://docs.microsoft.com/dotnet/core/install/linux-debian) -- [Fedora](https://docs.microsoft.com/dotnet/core/install/linux-fedora) - -For other distros, please check the _'Install on Linux'_ menu on the left of [this page](https://docs.microsoft.com/dotnet/core/install/linux). - -## Verify installation (optional) - -You can check which version of **.NET Runtime** is installed by running the following command in a terminal: - -```console -dotnet --info -``` - -If the **.NET Runtime** is correctly installed, the command will output something similar to the following: - -```console -.NET runtimes installed: - Microsoft.NETCore.App 7.0.x [C:\path\to\dotnet\shared\Microsoft.NETCore.App] - Microsoft.WindowsDesktop.App 7.0.x [C:\path\to\dotnet\shared\Microsoft.WindowsDesktop.App] -``` diff --git a/.docs/Getting-started.md b/.docs/Getting-started.md index a107adad..c822e417 100644 --- a/.docs/Getting-started.md +++ b/.docs/Getting-started.md @@ -15,12 +15,9 @@ The information presented on this page is valid for **all** platforms. **DCE** has two different versions: - **Graphical User Interface** (**GUI**) - it's the preferred version for newcomers as it is easy to use. - You can get it by [downloading](https://github.com/Tyrrrz/DiscordChatExporter/releases/latest) the `DiscordChatExporter.zip` file. + You can get it by [downloading](https://github.com/Tyrrrz/DiscordChatExporter/releases/latest) the `DiscordChatExporter.*.zip` file. - **Command-line Interface** (**CLI**) - offers greater flexibility and more features for advanced users, such as export scheduling, ID lists, and more specific date ranges. - You can get it by [downloading](https://github.com/Tyrrrz/DiscordChatExporter/releases/latest) the `DiscordChatExporter.CLI.zip` file. - -If you're not comfortable with **Windows'** Command-line (cmd), please choose the GUI. -[**macOS**](MacOS.md), [**Linux**](Linux.md) and [**Docker**](Docker.md) users can only use the CLI version. + You can get it by [downloading](https://github.com/Tyrrrz/DiscordChatExporter/releases/latest) the `DiscordChatExporter.Cli.*.zip` file. There are dedicated guides for each version: @@ -36,15 +33,15 @@ The HTML format replicates Discord's interface, making it the most user-friendly It's the best format for attachment preview and sharing. You can open `.html` files with a web browser, such as Google Chrome. -> **Warning**: +> [!WARNING] > If a picture is deleted, or if a user changes its avatar, the respective images will no longer be displayed. -> Export using the "Download referenced assets" (`--media`) option to avoid this. +> Export using the "Download assets" (`--media`) option to avoid this. ### Plain Text -The Plain Text format is the best option for archiving due to its small size. +The Plain Text format formats messages as plain text, and has the smallest size. You can open `.txt` files with a text editor, such as Notepad. ### JSON diff --git a/.docs/Linux.md b/.docs/Linux.md deleted file mode 100644 index 7a37ff72..00000000 --- a/.docs/Linux.md +++ /dev/null @@ -1,26 +0,0 @@ -# Linux usage instructions - -## Installing .NET Runtime - -Please follow the [instructions provided here](Dotnet.md). - -## Downloading and using DiscordChatExporter.Cli - -1. Download [DiscordChatExporter.CLI.zip](https://github.com/Tyrrrz/DiscordChatExporter/releases/latest) and extract it to a folder. -2. Open Terminal. -3. `cd` into the extracted folder. You can do this in Terminal by typing `cd`, then press the SPACE key, drag and drop the extracted folder into the Terminal window, and press the ENTER key. -4. Replace `TOKEN` and `CHANNEL`, then execute this command to export: - -```console -dotnet DiscordChatExporter.Cli.dll export -t TOKEN -c CHANNEL -``` - -If the above command throws a "Permission denied" error, execute this command to fix the permissions: - -```console -chmod 644 *.dll DiscordChatExporter.* -``` - -> [How to get Token and Channel IDs](Token-and-IDs.md). - -There's much more you can do with DCE.CLI! Read the [CLI explained](Using-the-CLI.md) page to get started. diff --git a/.docs/MacOS.md b/.docs/MacOS.md deleted file mode 100644 index 8b949e4c..00000000 --- a/.docs/MacOS.md +++ /dev/null @@ -1,22 +0,0 @@ -# macOS usage instructions - -![Might look different depending on your macOS version](https://i.imgur.com/gA8jFjJ.png) - -## Installing .NET Runtime - -Please follow the [instructions provided here](Dotnet.md). - -## Downloading and using DiscordChatExporter.Cli - -1. Download [DiscordChatExporter.CLI.zip](https://github.com/Tyrrrz/DiscordChatExporter/releases/latest) and extract it to a folder. -2. Search for `Terminal.app` in Spotlight (⌘+SPACE), then open it. -3. In the Terminal window, type `cd` , press the SPACE key, then drag and drop the extracted folder into the window, then press the RETURN key. -4. Execute the following command to export, replacing `TOKEN` and `CHANNEL` with your own values: - -```console -dotnet DiscordChatExporter.Cli.dll export -t TOKEN -c CHANNEL -``` - -> [How to get Token and Channel IDs](Token-and-IDs.md). - -There's much more you can do with DCE.CLI! Read the [CLI explained](Using-the-CLI.md) page to get started. diff --git a/.docs/Message-filters.md b/.docs/Message-filters.md index 4d917d3d..c228aa70 100644 --- a/.docs/Message-filters.md +++ b/.docs/Message-filters.md @@ -89,17 +89,17 @@ from:96\-LB In most cases, you will need to enclose your filter in quotes (`"`) to escape characters that may have special meaning in your shell: ```console -DiscordChatExporter.Cli export [...] --filter "from:Tyrrrz has:image" +$ ./DiscordChatExporter.Cli export [...] --filter "from:Tyrrrz has:image" ``` If you need to include quotes inside the filter itself as well, use single quotes (`'`) for those instead: ```console -DiscordChatExporter.Cli export [...] --filter "from:Tyrrrz 'hello world'" +$ ./DiscordChatExporter.Cli export [...] --filter "from:Tyrrrz 'hello world'" ``` Additionally, negated filters (those that start with `-`) may cause parsing issues even when enclosed in quotes. To avoid this, use the tilde (`~`) character instead of the dash (`-`): ```console -DiscordChatExporter.Cli export [...] --filter ~from:Tyrrrz +$ ./DiscordChatExporter.Cli export [...] --filter ~from:Tyrrrz ``` diff --git a/.docs/Readme.md b/.docs/Readme.md index b050817c..01f86e51 100644 --- a/.docs/Readme.md +++ b/.docs/Readme.md @@ -2,8 +2,6 @@ ## Installation & Usage -- [Get .NET Core Runtime](Dotnet.md) (Required for CLI; Installed automatically for GUI; Not required in Docker) -- [Windows](Getting-started.md#gui-or-cli) | [macOS](MacOS.md) | [Linux](Linux.md) | [Docker](Docker.md) - Getting started: - [Using the GUI](Using-the-GUI.md) - [Using the CLI](Using-the-CLI.md) @@ -29,6 +27,5 @@ - [General questions](Troubleshooting.md#general) - [First steps help](Troubleshooting.md#first-steps) - [It's crashing/failing](Troubleshooting.md#DCE-is-crashingfailing) -- [.NET Core Runtime is required](Troubleshooting.md#net-core-runtime-is-required) - [Errors](Troubleshooting.md#errors) - [**More help**](Troubleshooting.md) diff --git a/.docs/Scheduling-Linux.md b/.docs/Scheduling-Linux.md index 63a5bfc1..7534d2e6 100644 --- a/.docs/Scheduling-Linux.md +++ b/.docs/Scheduling-Linux.md @@ -1,7 +1,5 @@ # Scheduling exports with Cron -Make sure you already have **DiscordChatExporter.CLI** and **.NET Core** properly installed ([instructions here](Linux.md)). - ## Creating the script 1. Open Terminal and create a new text file with `nano /path/to/DiscordChatExporter/cron.sh` @@ -13,7 +11,7 @@ Make sure you already have **DiscordChatExporter.CLI** and **.NET Core** properl ```bash #!/bin/bash -# Info: https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs +# Info: https://github.com/Tyrrrz/DiscordChatExporter/blob/prime/.docs TOKEN=tokenhere CHANNELID=channelhere @@ -45,7 +43,7 @@ fi cd $DLLFOLDER || exit 1 # This will export your chat -dotnet DiscordChatExporter.Cli.dll export -t $TOKEN -c $CHANNELID -f $EXPORTFORMAT -o $FILENAME.tmp +./DiscordChatExporter.Cli export -t $TOKEN -c $CHANNELID -f $EXPORTFORMAT -o $FILENAME.tmp # This sets the current time to a variable CURRENTTIME=$(date +"%Y-%m-%d-%H-%M-%S") diff --git a/.docs/Scheduling-MacOS.md b/.docs/Scheduling-MacOS.md index f2a9a360..78e362a9 100644 --- a/.docs/Scheduling-MacOS.md +++ b/.docs/Scheduling-MacOS.md @@ -1,9 +1,5 @@ # Scheduling exports on macOS -Scheduling on macOS is a bit tricky, but it should work if you follow the instructions accordingly. - -Make sure you already have **DiscordChatExporter.CLI** and **.NET Core** properly installed ([instructions here](MacOS.md)). - ## Creating the script 1. Open TextEdit.app and create a new file @@ -16,7 +12,7 @@ Make sure you already have **DiscordChatExporter.CLI** and **.NET Core** properl ```bash #!/bin/bash -# Info: https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs +# Info: https://github.com/Tyrrrz/DiscordChatExporter/blob/prime/.docs TOKEN=tokenhere CHANNELID=channelhere @@ -51,7 +47,7 @@ fi cd $DLLFOLDER || exit 1 # This will export your chat -dotnet DiscordChatExporter.Cli.dll export -t $TOKEN -c $CHANNELID -f $EXPORTFORMAT -o $FILENAME.tmp +./DiscordChatExporter.Cli export -t $TOKEN -c $CHANNELID -f $EXPORTFORMAT -o $FILENAME.tmp # This sets the current time to a variable CURRENTTIME=$(date +"%Y-%m-%d-%H-%M-%S") diff --git a/.docs/Scheduling-Windows.md b/.docs/Scheduling-Windows.md index 9cfed26a..f8729c3d 100644 --- a/.docs/Scheduling-Windows.md +++ b/.docs/Scheduling-Windows.md @@ -1,13 +1,11 @@ # Scheduling exports on Windows -We'll be using [DiscordChatExporter CLI](https://github.com/Tyrrrz/DiscordChatExporter/releases/latest), PowerShell, and Task Scheduler. - ## Creating the script 1. Open a text editor such as Notepad and paste: ```console -# Info: https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs +# Info: https://github.com/Tyrrrz/DiscordChatExporter/blob/prime/.docs $TOKEN = "tokenhere" $CHANNEL = "channelhere" @@ -19,7 +17,7 @@ $EXPORTFORMAT = "formathere" cd $EXEPATH -.\DiscordChatExporter.Cli.exe export -t $TOKEN -c $CHANNEL -f $EXPORTFORMAT -o "$FILENAME.tmp" +./DiscordChatExporter.Cli export -t $TOKEN -c $CHANNEL -f $EXPORTFORMAT -o "$FILENAME.tmp" $Date = Get-Date -Format "yyyy-MM-dd-HH-mm" diff --git a/.docs/Token-and-IDs.md b/.docs/Token-and-IDs.md index 7dbdf15d..eae162ce 100644 --- a/.docs/Token-and-IDs.md +++ b/.docs/Token-and-IDs.md @@ -1,10 +1,11 @@ # Obtaining Token and Channel IDs -> **Warning**: -> **Do not share your token!** -> A token gives full access to an account. To reset a user token, change your account password. To reset a bot token, click on [Regenerate](#how-to-get-a-bot-token) in the bot settings. +> [!WARNING] +> **Do not share your token!** A token gives full access to an account. +> To reset a user token, change your account password. +> To reset a bot token, click on [Reset Token](#how-to-export-with-a-bot-token) in the bot settings. -## How to get a User Token +## How to get a user token **Caution:** [Automating user accounts violates Discord's terms of service](https://support.discord.com/hc/en-us/articles/115002192352-Automated-user-accounts-self-bots-) and may result in account termination. Use at your own risk. @@ -25,8 +26,8 @@ Prerequisite step: Navigate to [discord.com](https://discord.com) and login. 3. Type - ```console - (webpackChunkdiscord_app.push([[''],{},e=>{m=[];for(let c in e.c)m.push(e.c[c])}]),m).find(m=>m?.exports?.default?.getToken!==void 0).exports.default.getToken() + ```js + let m;webpackChunkdiscord_app.push([[Math.random()],{},e=>{for(let i in e.c){let x=e.c[i];if(x?.exports?.getToken){m=x;break}}}]);m&&console.log("Token:",m.exports.getToken()); ``` into the console and press Enter. The console will display your user token. @@ -118,8 +119,8 @@ Prerequisite step: Navigate to [discord.com](https://discord.com) and login. 1. Type - ```console - (webpackChunkdiscord_app.push([[''],{},e=>{m=[];for(let c in e.c)m.push(e.c[c])}]),m).find(m=>m?.exports?.default?.getToken!==void 0).exports.default.getToken() + ```js + let m;webpackChunkdiscord_app.push([[Math.random()],{},e=>{for(let i in e.c){let x=e.c[i];if(x?.exports?.getToken){m=x;break}}}]);m&&console.log("Token:",m.exports.getToken()); ``` into the console and press Enter. The console will display your user token. @@ -254,17 +255,61 @@ Prerequisite step: Navigate to [discord.com](https://discord.com) and login. 4. Press Esc. The settings page will close. 5. To find your user token, continue [here](#in-chrome). -## How to get a Bot Token +## How to export with a bot token + +### Step 1 - Create an application + +You can create a new application or use an existing one. If you want to create a new one: + +1. Go to [Discord developer portal](https://discord.com/developers/applications) +2. Click on **New Application** in the top right corner +3. Enter a name for your application and click **Create** + +### Step 2 - Invite the bot to your server + +The bot needs to be invited to the server you'd like to export from. + +1. Go to [Discord developer portal](https://discord.com/developers/applications) +2. Navigate to **General Information** on the left +3. Copy the **Application ID** +4. Open the following URL in your browser, replacing `YOUR_APP_ID` with the copied Client ID: + + + +``` +https://discord.com/oauth2/authorize?scope=bot&permissions=66560&client_id=YOUR_APP_ID +``` + +### Step 3 - Ensure message content intent is enabled + +If this option is not enabled, the exported files will be empty. 1. Go to [Discord developer portal](https://discord.com/developers/applications) 2. Open your Application's settings 3. Navigate to the **Bot** section on the left -4. Under **Token** click **Copy** +4. Scroll down to the **Privileged Gateway Intents** section +5. Enable **Message Content Intent** by toggling the switch -> **Warning**: -> Your bot needs to have **Message Content Intent** enabled for it to be able to read messages! + -![https://discord.com/developers/applications/](https://i.imgur.com/BdrrxlY.png) +### Step 4 - Copy the bot token + +If you don't have a bot token yet or if you've lost it, follow these steps to reset it: + +1. Go to [Discord developer portal](https://discord.com/developers/applications) +2. Open your Application's settings +3. Navigate to the **Bot** section on the left +4. Under **Token** click **Reset Token** +5. Click **Yes, do it!** and authenticate to confirm + +> **Tip**: +> As the token is only shown once, make sure to store it in a safe place. If you lose the token, you will have to reset it again. + +> [!WARNING] +> Resetting the token will invalidate the old one. Any integrations relying on the old token will cease to function until they are updated. + +![https://discord.com/developers/applications/](https://i.imgur.com/soiB8Qc.png) --- diff --git a/.docs/Troubleshooting.md b/.docs/Troubleshooting.md index 7ff700b2..024a8dbb 100644 --- a/.docs/Troubleshooting.md +++ b/.docs/Troubleshooting.md @@ -24,7 +24,7 @@ Follow the [instructions here](Token-and-IDs.md). ### Will I get banned if I use this? -Automating user accounts is technically against [TOS](https://discord.com/terms), use at your discretion. [Bot accounts](https://discord.com/developers/docs/topics/oauth2#bots) don't have this restriction. +Automating user accounts is technically against [TOS](https://discord.com/terms), use at your discretion. [Bot accounts](https://discord.com/developers/docs/topics/oauth2#bot-users) don't have this restriction. ### Will the messages disappear from the exported file if I delete a message, delete my account or block a person? @@ -70,14 +70,7 @@ Check the following page: [Obtaining token](Token-and-IDs.md) ### When I open DCE a black window pops up quickly or nothing shows up -If you have [.NET Core Runtime correctly installed](Dotnet.md), you might have downloaded the CLi flavor, try [downloading the GUI](Getting-started.md#gui-or-cli) instead. - -### How do I run DCE on macOS or Linux? - -Check the following pages: - -- [macOS usage instructions](MacOS.md) -- [Linux usage instructions](Linux.md) +You might have downloaded the CLI flavor of the app, which is meant to be run in a terminal. Try [downloading the GUI](Getting-started.md#gui-or-cli) instead if that's what you want. ### How can I set DCE to export automatically at certain times? @@ -91,16 +84,6 @@ Check the following pages to learn how to schedule **DiscordChatExporter.CLI** r Try opening it with a different program, try partitioning or use a different file format, like `PlainText`. -### DCE is crashing/failing - -Check the following page: [Installing .NET Core Runtime](Dotnet.md) - -If you already have .NET Core installed, please check if your problem is a [known issue](https://github.com/Tyrrrz/DiscordChatExporter/issues?q=is%3Aissue) before [opening a bug report](https://github.com/Tyrrrz/DiscordChatExporter/issues/new). - -### .NET Core Runtime is required - -Check the following page: [Installing .NET Core Runtime](Dotnet.md) - ### I see messages in the export, but they have no content Your bot is missing the 'Message Content Intent'. Go to the [Discord Developer Portal](https://discord.com/developers/applications), navigate to the 'Bot' section and enable it. @@ -133,34 +116,26 @@ Make sure you're [copying the DM Channel ID](Token-and-IDs.md#how-to-get-a-direc ## Errors -```console +```yml DiscordChatExporter.Domain.Exceptions.DiscordChatExporterException: Authentication token is invalid. -... ``` ↳ Make sure the provided token is correct. -```console +```yml DiscordChatExporter.Domain.Exceptions.DiscordChatExporterException: Requested resource does not exist. ``` ↳ Check your channel ID, it might be invalid. [Read this if you need help](Token-and-IDs.md). -```console +```yml DiscordChatExporter.Domain.Exceptions.DiscordChatExporterException: Access is forbidden. ``` ↳ This means you don't have access to the channel. -```console -The application to execute does not exist: -``` - -↳ The `DiscordChatExporter.Cli.dll` file is missing. Keep the `.exe` and all the `.dll` files together. If you didn't move the files, try unzipping again. - -```console +```yml System.Net.WebException: Error: TrustFailure ... Invalid certificate received from server. -... ``` ↳ Try running cert-sync. @@ -171,6 +146,12 @@ Red Hat: `cert-sync --user /etc/pki/tls/certs/ca-bundle.crt` If it still doesn't work, try mozroots: `mozroots --import --ask-remove` +## macOS-specific + +### DiscordChatExporter is damaged and can’t be opened. You should move it to the Trash. + +Check the [Using the GUI page](Using-the-GUI.md#step-1) for instructions on how to run the app. + --- > ❓ If you still have unanswered questions, feel free to [create a new discussion](https://github.com/Tyrrrz/DiscordChatExporter/discussions/new). diff --git a/.docs/Using-the-CLI.md b/.docs/Using-the-CLI.md index b52d2449..36de6c95 100644 --- a/.docs/Using-the-CLI.md +++ b/.docs/Using-the-CLI.md @@ -1,66 +1,61 @@ # Using the CLI -## Guide - -> **Note**: -> Make sure you have [.NET Core installed](Dotnet.md) before attempting to run the commands below. -> **Docker** users, please refer to the [Docker usage instructions](Docker.md). - ## Step 1 -After extracting the `.zip`, open Command Prompt, aka `cmd` (`Terminal` on **macOS** and **Linux**). +After extracting the `.zip` archive, open your preferred terminal. ## Step 2 -Change the current directory to DCE's folder with `cd C:\path\to\directory`, then press ENTER to run the command. +Change the current directory to DCE's folder with `cd C:\path\to\DiscordChatExporter` (`cd /path/to/DiscordChatExporter` on **MacOS** and **Linux**), then press ENTER to run the command. -**Windows** users can quickly get the directory's path by clicking the address bar while inside the folder. +**Windows** users can quickly get the folder's path by clicking the address bar while inside the folder. ![Copy path from Explorer](https://i.imgur.com/XncnhC2.gif) -**macOS** users can select the `.exe`, hit Command+I (⌘I), and copy what's after `Where:` to get the directory -![Mac info panel](https://camo.githubusercontent.com/3c51a904b0099c9f68a4797461d4a7914902fc04/68747470733a2f2f692e696d6775722e636f6d2f323975364e79782e706e67) +**macOS** users can press Command+Option+C (⌘⌥C) while inside the folder (or selecting it) to copy its path to the clipboard. You can also drag and drop the folder on **every platform**. ![Drag and drop folder](https://i.imgur.com/sOpZQAb.gif) ## Step 3 -Now we're ready to run the commands. The examples on this page follow the Windows file path format, change the file -paths according to your system. +Now we're ready to run the commands. -Type the following in Command Prompt (Terminal), then press ENTER to run it. This will list DCE's options. +Type the following command in your terminal of choice, then press ENTER to run it. This will list all available subcommands and options. ```console -dotnet DiscordChatExporter.Cli.dll +./DiscordChatExporter.Cli ``` +> **Note**: +> On Windows, if you're using the default Command Prompt (`cmd`), omit the leading `./` at the start of the command. + > **Docker** users, please refer to the [Docker usage instructions](Docker.md). ## CLI commands -| Command | Description | -|-------------------------|------------------------------------------------------| -| export | Exports a channel | -| exportdm | Exports all direct message channels | -| exportguild | Exports all channels within the specified server | -| exportall | Exports all accessible channels | -| channels | Outputs the list of channels in the given server | -| dm | Outputs the list of direct message channels | -| guilds | Outputs the list of accessible servers | -| guide | Explains how to obtain token, server, and channel ID | +| Command | Description | +| ----------- | ---------------------------------------------------- | +| export | Exports a channel | +| exportdm | Exports all direct message channels | +| exportguild | Exports all channels within the specified server | +| exportall | Exports all accessible channels | +| channels | Outputs the list of channels in the given server | +| dm | Outputs the list of direct message channels | +| guilds | Outputs the list of accessible servers | +| guide | Explains how to obtain token, server, and channel ID | -To use the commands, you'll need a token. For the instructions on how to get a token, please refer to [this page](Token-and-IDs.md), or run `dotnet DiscordChatExporter.Cli.dll guide`. +To use the commands, you'll need a token. For the instructions on how to get a token, please refer to [this page](Token-and-IDs.md), or run `./DiscordChatExporter.Cli guide`. To get help with a specific command, run: ```console -dotnet DiscordChatExporter.Cli.dll command --help +./DiscordChatExporter.Cli command --help ``` For example, to figure out how to use the `export` command, run: ```console -dotnet DiscordChatExporter.Cli.dll export --help +./DiscordChatExporter.Cli export --help ``` ## Export a specific channel @@ -68,7 +63,7 @@ dotnet DiscordChatExporter.Cli.dll export --help You can quickly export with DCE's default settings by using just `-t token` and `-c channelid`. ```console -dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 +./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 ``` #### Changing the format @@ -77,7 +72,7 @@ You can change the export format to `HtmlDark`, `HtmlLight`, `PlainText` `Json` format is `HtmlDark`. ```console -dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 -f Json +./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 -f Json ``` #### Changing the output filename @@ -85,7 +80,7 @@ dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 -f Json You can change the filename by using `-o name.ext`. e.g. for the `HTML` format: ```console -dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 -o myserver.html +./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 -o myserver.html ``` #### Changing the output directory @@ -95,7 +90,7 @@ extension. If any of the folders in the path have a space in its name, escape them with quotes ("). ```console -dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 -o "C:\Discord Exports" +./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 -o "C:\Discord Exports" ``` #### Changing the filename and output directory @@ -105,7 +100,7 @@ Note that the filename must have an extension, otherwise it will be considered a If any of the folders in the path have a space in its name, escape them with quotes ("). ```console -dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 -o "C:\Discord Exports\myserver.html" +./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 -o "C:\Discord Exports\myserver.html" ``` #### Generating the filename and output directory dynamically @@ -113,7 +108,7 @@ dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 -o "C:\Discord You can use template tokens to generate the output file path based on the server and channel metadata. ```console -dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 -o "C:\Discord Exports\%G\%T\%C.html" +./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 -o "C:\Discord Exports\%G\%T\%C.html" ``` Assuming you are exporting a channel named `"my-channel"` in the `"Text channels"` category from a server @@ -132,6 +127,7 @@ Here is the full list of supported template tokens: - `%P` - category position - `%a` - the "after" date - `%b` - the "before" date +- `%d` - the current date - `%%` - escapes `%` #### Partitioning @@ -140,13 +136,13 @@ You can use partitioning to split files after a given number of messages or file For example, a channel with 36 messages set to be partitioned every 10 messages will output 4 files. ```console -dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 -p 10 +./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 -p 10 ``` A 45 MB channel set to be partitioned every 20 MB will output 3 files. ```console -dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 -p 20mb +./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 -p 20mb ``` #### Downloading assets @@ -157,7 +153,7 @@ downloaded when using the plain text (TXT) export format. A folder containing the assets will be created along with the exported chat. They must be kept together. ```console -dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 --media +./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 --media ``` #### Reusing assets @@ -166,7 +162,7 @@ Previously downloaded assets can be reused to skip redundant downloads as long a same folder. Using this option can speed up future exports. This option requires the `--media` option. ```console -dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 --media --reuse-media +./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 --media --reuse-media ``` #### Changing the media directory @@ -175,7 +171,7 @@ By default, the media directory is created alongside the exported chat. You can providing a path that ends with a slash. All of the exported media will be stored in this directory. ```console -dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 --media --media-dir "C:\Discord Media" +./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 --media --media-dir "C:\Discord Media" ``` #### Changing the date format @@ -184,7 +180,7 @@ You can customize how dates are formatted in the exported files by using `--loca locales. The default locale is `en-US`. ```console -dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 --locale "de-DE" +./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 --locale "de-DE" ``` #### Date ranges @@ -193,14 +189,14 @@ dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 --locale "de-DE Use `--before` to export messages sent before the provided date. E.g. messages sent before September 18th, 2019: ```console -dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 --before 2019-09-18 +./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 --before 2019-09-18 ``` **Messages sent after a date** Use `--after` to export messages sent after the provided date. E.g. messages sent after September 17th, 2019 11:34 PM: ```console -dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 --after "2019-09-17 23:34" +./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 --after "2019-09-17 23:34" ``` **Messages sent in a date range** @@ -208,7 +204,7 @@ Use `--before` and `--after` to export messages sent during the provided date ra September 17th, 2019 11:34 PM and September 18th: ```console -dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 --after "2019-09-17 23:34" --before "2019-09-18" +./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 --after "2019-09-17 23:34" --before "2019-09-18" ``` You can try different formats like `17-SEP-2019 11:34 PM` or even refine your ranges down to @@ -217,12 +213,22 @@ Don't forget to quote (") the date if it has spaces! More info about .NET date formats [here](https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings). +#### Filtering messages + +Use `--filter` to filter what messages are included in the export. + +```console +./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 --filter "from:Tyrrrz has:image" +``` + +Documentation on message filter syntax can be found [here](https://github.com/Tyrrrz/DiscordChatExporter/blob/prime/.docs/Message-filters.md). + ### Export channels from a specific server To export all channels in a specific server, use the `exportguild` command and provide the server ID through the `-g|--guild` option: ```console -dotnet DiscordChatExporter.Cli.dll exportguild -t "mfa.Ifrn" -g 21814 +./DiscordChatExporter.Cli exportguild -t "mfa.Ifrn" -g 21814 ``` #### Including threads @@ -232,7 +238,7 @@ specifying which threads should be included. It has possible values of `none`, ` threads should be included. To include both active and archived threads, use `--include-threads all`. ```console -dotnet DiscordChatExporter.Cli.dll exportguild -t "mfa.Ifrn" -g 21814 --include-threads all +./DiscordChatExporter.Cli exportguild -t "mfa.Ifrn" -g 21814 --include-threads all ``` #### Including voice channels @@ -242,7 +248,7 @@ specifying whether to include voice channels in the export. It has possible valu voice channels, use `--include-vc false`. ```console -dotnet DiscordChatExporter.Cli.dll exportguild -t "mfa.Ifrn" -g 21814 --include-vc false +./DiscordChatExporter.Cli exportguild -t "mfa.Ifrn" -g 21814 --include-vc false ``` ### Export all channels @@ -250,7 +256,7 @@ dotnet DiscordChatExporter.Cli.dll exportguild -t "mfa.Ifrn" -g 21814 --include- To export all accessible channels, use the `exportall` command: ```console -dotnet DiscordChatExporter.Cli.dll exportall -t "mfa.Ifrn" +./DiscordChatExporter.Cli exportall -t "mfa.Ifrn" ``` #### Excluding DMs @@ -258,7 +264,7 @@ dotnet DiscordChatExporter.Cli.dll exportall -t "mfa.Ifrn" To exclude DMs, add the `--include-dm false` option. ```console -dotnet DiscordChatExporter.Cli.dll exportall -t "mfa.Ifrn" --include-dm false +./DiscordChatExporter.Cli exportall -t "mfa.Ifrn" --include-dm false ``` ### List channels in a server @@ -266,7 +272,7 @@ dotnet DiscordChatExporter.Cli.dll exportall -t "mfa.Ifrn" --include-dm false To list the channels available in a specific server, use the `channels` command and provide the server ID through the `-g|--guild` option: ```console -dotnet DiscordChatExporter.Cli.dll channels -t "mfa.Ifrn" -g 21814 +./DiscordChatExporter.Cli channels -t "mfa.Ifrn" -g 21814 ``` ### List direct message channels @@ -274,7 +280,7 @@ dotnet DiscordChatExporter.Cli.dll channels -t "mfa.Ifrn" -g 21814 To list all DM channels accessible to the current account, use the `dm` command: ```console -dotnet DiscordChatExporter.Cli.dll dm -t "mfa.Ifrn" +./DiscordChatExporter.Cli dm -t "mfa.Ifrn" ``` ### List servers @@ -282,5 +288,5 @@ dotnet DiscordChatExporter.Cli.dll dm -t "mfa.Ifrn" To list all servers accessible by the current account, use the `guilds` command: ```console -dotnet DiscordChatExporter.Cli.dll guilds -t "mfa.Ifrn" > C:\path\to\output.txt -``` \ No newline at end of file +./DiscordChatExporter.Cli guilds -t "mfa.Ifrn" > C:\path\to\output.txt +``` diff --git a/.docs/Using-the-GUI.md b/.docs/Using-the-GUI.md index 07e073f6..a206b2fe 100644 --- a/.docs/Using-the-GUI.md +++ b/.docs/Using-the-GUI.md @@ -10,13 +10,28 @@ ### Step 1 -After extracting the `.zip`, open `DiscordChatExporter.exe` +After extracting the `.zip`, run `DiscordChatExporter.exe` **(Windows)**, or `DiscordChatExporter` **(Linux)**. + +If you're using **macOS**, you'll need to manually grant permission for the app to run. +If you skip these steps, the "DiscordChatExporter is damaged and can’t be opened" error will be shown. + +1. Open Terminal.app. You can search for it in Spotlight (press + Space and type "Terminal"). +2. Paste the following into the terminal window: + ```bash + xattr -rd com.apple.quarantine + ``` +3. Hit Space once to add a space after the command +4. Drag and drop DiscordChatExporter.app into the terminal window +5. Press Return to run the command +6. Open DiscordChatExporter.app normally + +> Apple requires apps to be notarized and signed in order to run on macOS without warnings, which in turn requires an Apple Developer membership ($99/year). This open-source project is distributed for free and without commercial intent. ### Step 2 Please refer to the on-screen instructions to get your token, then paste your token in the upper text box and hit ENTER or click the arrow (→). -> **Warning**: +> [!WARNING] > **Never share your token!** > A token gives full access to an account, treat it like a password. @@ -61,37 +76,30 @@ In this screen you can customize the following: ## Settings -- **Auto-update** -Perform automatic updates on every launch. +- **Auto-update** - Perform automatic updates on every launch. Default: Enabled > **Note**: > Keep this option enabled to receive the latest features and bug fixes! -- **Dark mode** -Use darker colors in the UI (User Interface). +- **Dark mode** - Use darker colors in the UI (User Interface). Default: Disabled -- **Persist token** -Persist last used token between sessions. +- **Persist token** - Persist last used token between sessions. Default: Enabled -- **Show threads** -Controls whether threads are shown in the channel list. +- **Show threads** - Controls whether threads are shown in the channel list. Default: none -- **Locale** -Customize how dates are formatted in the exported files. +- **Locale** - Customize how dates are formatted in the exported files. -- **Date format** -Customize how dates are formatted in the exported files in the settings menu (). +- **Date format** - Customize how dates are formatted in the exported files in the settings menu (). -- **Parallel limit** -The number of channels that will be exported at the same time. +- **Parallel limit** - The number of channels that will be exported at the same time. Default: 1 -- **Normalize to UTC** -Convert all dates to UTC before exporting. - > **Note**: > Try to keep this number low so that your account doesn't get flagged. + +- **Normalize to UTC** - Convert all dates to UTC before exporting. + diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 1ffcc397..fbc70a4a 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -100,6 +100,8 @@ body: required: true - label: I have sponsored this project required: false + - label: I have not read any of the above and just checked all the boxes to submit the issue + required: false - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 1c401ac5..0228eac6 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,10 +1,10 @@ blank_issues_enabled: false contact_links: - name: ⚠ Feature request - url: https://github.com/Tyrrrz/.github/blob/master/docs/project-status.md - about: Sorry, but this project is in maintenance mode and no longer accepts new feature requests. + url: https://github.com/Tyrrrz/.github/blob/prime/docs/project-status.md + about: Sorry, but this project is in maintenance mode and currently doesn't accept new feature requests. - name: 📖 Documentation - url: https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs + url: https://github.com/Tyrrrz/DiscordChatExporter/blob/prime/.docs about: Find usage guides and frequently asked questions. - name: 🗨 Discussions url: https://github.com/Tyrrrz/DiscordChatExporter/discussions/new diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..cdbf26b9 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,32 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: monthly + labels: + - enhancement + groups: + actions: + patterns: + - "*" + - package-ecosystem: docker + directory: "/" + schedule: + interval: monthly + labels: + - enhancement + groups: + docker: + patterns: + - "*" + - package-ecosystem: nuget + directory: "/" + schedule: + interval: monthly + labels: + - enhancement + groups: + nuget: + patterns: + - "*" diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 00000000..1df7efb4 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,18 @@ +changelog: + exclude: + authors: + - dependabot + - dependabot[bot] + + categories: + - title: Enhancements + labels: + - enhancement + + - title: Bugs + labels: + - bug + + - title: Other + labels: + - "*" diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c43b586a..ec56bde2 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -4,16 +4,15 @@ on: workflow_dispatch: push: branches: - - master + - prime tags: - "*" pull_request: branches: - - master + - prime jobs: - # Outputs from this job aren't really used, but it's here to verify that - # the Dockerfile builds correctly on pull requests. + # Outputs from this job aren't really used, but it's here to verify that the Dockerfile builds correctly pack: runs-on: ubuntu-latest timeout-minutes: 10 @@ -24,10 +23,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # 4.1.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Docker Buildx - uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # 3.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Build image run: > @@ -37,16 +36,16 @@ jobs: --build-arg VERSION=${{ github.ref_type == 'tag' && github.ref_name || format('999.9.9-ci-{0}', github.sha) }} --output type=tar,dest=DiscordChatExporter.Cli.Docker.tar - - name: Upload artifacts - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # 3.1.3 + - name: Upload image + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: DiscordChatExporter.Cli.Docker path: DiscordChatExporter.Cli.Docker.tar if-no-files-found: error deploy: - # Deploy to DockerHub only on tag push or master branch push - if: ${{ github.ref_type == 'tag' || github.ref_type == 'branch' && github.ref_name == 'master' }} + # Deploy to DockerHub only on tag push or prime branch push + if: ${{ github.ref_type == 'tag' || github.ref_type == 'branch' && github.ref_name == 'prime' }} runs-on: ubuntu-latest timeout-minutes: 10 @@ -56,10 +55,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # 4.1.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Docker Buildx - uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # 3.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Login to DockerHub run: > diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2d2df6bd..7e63cf6a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,12 +4,12 @@ on: workflow_dispatch: push: branches: - - master + - prime tags: - "*" pull_request: branches: - - master + - prime env: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true @@ -18,7 +18,7 @@ env: jobs: format: - runs-on: windows-latest + runs-on: ubuntu-latest timeout-minutes: 10 permissions: @@ -26,24 +26,35 @@ jobs: steps: - name: Checkout - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # 4.1.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install .NET - uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # 3.2.0 - with: - dotnet-version: 8.0.x + uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 + + # Build the project separately to discern between build and format errors + - name: Build + run: > + dotnet build + -p:CSharpier_Bypass=true + --configuration Release - name: Verify formatting + id: verify run: > dotnet build -t:CSharpierFormat --configuration Release + --no-restore + + - name: Report issues + if: ${{ failure() && steps.verify.outcome == 'failure' }} + run: echo "::error title=Bad formatting::Formatting issues detected. Please build the solution locally to fix them." test: # Tests need access to secrets, so we can't run them against PRs because of limited trust if: ${{ github.event_name != 'pull_request' }} - runs-on: windows-latest + runs-on: ubuntu-latest timeout-minutes: 10 permissions: @@ -51,12 +62,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # 4.1.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install .NET - uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # 3.2.0 - with: - dotnet-version: 8.0.x + uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 - name: Run tests env: @@ -72,7 +81,7 @@ jobs: DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover - name: Upload coverage - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # 3.1.4 + uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 with: token: ${{ secrets.CODECOV_TOKEN }} @@ -82,8 +91,24 @@ jobs: app: - DiscordChatExporter.Cli - DiscordChatExporter.Gui + rid: + - win-arm64 + - win-x86 + - win-x64 + - linux-arm + - linux-arm64 + - linux-musl-x64 + - linux-x64 + - osx-arm64 + - osx-x64 + include: + - app: DiscordChatExporter.Cli + asset: DiscordChatExporter.Cli + - app: DiscordChatExporter.Gui + # GUI assets aren't suffixed, unlike the CLI assets + asset: DiscordChatExporter - runs-on: windows-latest + runs-on: ${{ startsWith(matrix.rid, 'win-') && 'windows-latest' || startsWith(matrix.rid, 'osx-') && 'macos-latest' || 'ubuntu-latest' }} timeout-minutes: 10 permissions: @@ -92,25 +117,27 @@ jobs: steps: - name: Checkout - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # 4.1.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install .NET - uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # 3.2.0 - with: - dotnet-version: 8.0.x + uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 - name: Publish app run: > dotnet publish ${{ matrix.app }} -p:Version=${{ github.ref_type == 'tag' && github.ref_name || format('999.9.9-ci-{0}', github.sha) }} -p:CSharpier_Bypass=true + -p:EncryptionSalt=${{ secrets.ENCRYPTION_SALT || 'HimalayanPinkSalt' }} + -p:PublishMacOSBundle=${{ startsWith(matrix.rid, 'osx-') }} --output ${{ matrix.app }}/bin/publish/ --configuration Release + --runtime ${{ matrix.rid }} + --self-contained - - name: Upload artifacts - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # 3.1.3 + - name: Upload app binaries + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: ${{ matrix.app }} + name: ${{ matrix.asset }}.${{ matrix.rid }} path: ${{ matrix.app }}/bin/publish/ if-no-files-found: error @@ -147,11 +174,21 @@ jobs: app: - DiscordChatExporter.Cli - DiscordChatExporter.Gui + rid: + - win-arm64 + - win-x86 + - win-x64 + - linux-arm + - linux-arm64 + - linux-musl-x64 + - linux-x64 + - osx-arm64 + - osx-x64 include: - app: DiscordChatExporter.Cli asset: DiscordChatExporter.Cli - app: DiscordChatExporter.Gui - # GUI asset isn't suffixed, unlike the CLI asset + # GUI assets aren't suffixed, unlike the CLI assets asset: DiscordChatExporter runs-on: ubuntu-latest @@ -162,23 +199,31 @@ jobs: contents: write steps: - - name: Download artifacts - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # 3.0.2 + - name: Download app binaries + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: ${{ matrix.app }} + name: ${{ matrix.asset }}.${{ matrix.rid }} path: ${{ matrix.app }}/ + - name: Set permissions + if: ${{ !startsWith(matrix.rid, 'win-') }} + run: | + [ -f ${{ matrix.app }}/${{ matrix.asset }} ] && chmod +x ${{ matrix.app }}/${{ matrix.asset }} || true + + # macOS bundle + [ -f ${{ matrix.app }}/${{ matrix.asset }}.app/Contents/MacOS/${{ matrix.asset }} ] && chmod +x ${{ matrix.app }}/${{ matrix.asset }}.app/Contents/MacOS/${{ matrix.asset }} || true + - name: Create package # Change into the artifacts directory to avoid including the directory itself in the zip archive working-directory: ${{ matrix.app }}/ - run: zip -r ../${{ matrix.asset }}.zip . + run: zip -r ../${{ matrix.asset }}.${{ matrix.rid }}.zip . - name: Upload release asset env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: > gh release upload ${{ github.ref_name }} - ${{ matrix.asset }}.zip + ${{ matrix.asset }}.${{ matrix.rid }}.zip --repo ${{ github.event.repository.full_name }} notify: @@ -191,7 +236,7 @@ jobs: steps: - name: Notify Discord - uses: tyrrrz/action-http-request@64c70c67f5ebc54d4c7ea09cbe3553322778afd5 # 1.1.2 + uses: tyrrrz/action-http-request@110f1a0f0f7e91c3b2de349539249f4573c243fa # 1.1.5 with: url: ${{ secrets.DISCORD_WEBHOOK }} method: POST diff --git a/Directory.Build.props b/Directory.Build.props index ec60d3ca..188b0263 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,13 +1,12 @@ - - net8.0 + net10.0 999.9.9-dev Tyrrrz Copyright (c) Oleksii Holub - preview enable + true true + false - - \ No newline at end of file + diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 00000000..5e2024f5 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,46 @@ + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DiscordChatExporter.Cli.Tests/DiscordChatExporter.Cli.Tests.csproj b/DiscordChatExporter.Cli.Tests/DiscordChatExporter.Cli.Tests.csproj index dff62425..826921f4 100644 --- a/DiscordChatExporter.Cli.Tests/DiscordChatExporter.Cli.Tests.csproj +++ b/DiscordChatExporter.Cli.Tests/DiscordChatExporter.Cli.Tests.csproj @@ -1,5 +1,4 @@ - - + false true @@ -11,23 +10,22 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + - + - - \ No newline at end of file + diff --git a/DiscordChatExporter.Cli.Tests/Infra/ChannelIds.cs b/DiscordChatExporter.Cli.Tests/Infra/ChannelIds.cs index f48f9b00..9941c36e 100644 --- a/DiscordChatExporter.Cli.Tests/Infra/ChannelIds.cs +++ b/DiscordChatExporter.Cli.Tests/Infra/ChannelIds.cs @@ -1,4 +1,4 @@ -using DiscordChatExporter.Core.Discord; +using DiscordChatExporter.Core.Discord; namespace DiscordChatExporter.Cli.Tests.Infra; @@ -10,10 +10,14 @@ public static class ChannelIds public static Snowflake EmbedTestCases { get; } = Snowflake.Parse("866472452459462687"); + public static Snowflake EmojiTestCases { get; } = Snowflake.Parse("866768438290415636"); + public static Snowflake GroupingTestCases { get; } = Snowflake.Parse("992092091545034842"); public static Snowflake FilterTestCases { get; } = Snowflake.Parse("866744075033641020"); + public static Snowflake ForwardTestCases { get; } = Snowflake.Parse("1455202357204877477"); + public static Snowflake MarkdownTestCases { get; } = Snowflake.Parse("866459526819348521"); public static Snowflake MentionTestCases { get; } = Snowflake.Parse("866458801389174794"); diff --git a/DiscordChatExporter.Cli.Tests/Infra/ExportWrapper.cs b/DiscordChatExporter.Cli.Tests/Infra/ExportWrapper.cs index d0a389f7..dbcdc52f 100644 --- a/DiscordChatExporter.Cli.Tests/Infra/ExportWrapper.cs +++ b/DiscordChatExporter.Cli.Tests/Infra/ExportWrapper.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -19,12 +19,7 @@ namespace DiscordChatExporter.Cli.Tests.Infra; public static class ExportWrapper { - private static readonly AsyncKeyedLocker Locker = - new(o => - { - o.PoolSize = 20; - o.PoolInitialFill = 1; - }); + private static readonly AsyncKeyedLocker Locker = new(); private static readonly string DirPath = Path.Combine( Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) @@ -61,7 +56,7 @@ public static class ExportWrapper ExportFormat = format, OutputPath = filePath, Locale = "en-US", - IsUtcNormalizationEnabled = true + IsUtcNormalizationEnabled = true, }.ExecuteAsync(console); } @@ -93,13 +88,12 @@ public static class ExportWrapper Snowflake messageId ) { - var message = (await GetMessagesAsHtmlAsync(channelId)).SingleOrDefault( - e => - string.Equals( - e.GetAttribute("data-message-id"), - messageId.ToString(), - StringComparison.OrdinalIgnoreCase - ) + var message = (await GetMessagesAsHtmlAsync(channelId)).SingleOrDefault(e => + string.Equals( + e.GetAttribute("data-message-id"), + messageId.ToString(), + StringComparison.OrdinalIgnoreCase + ) ); if (message is null) @@ -117,13 +111,12 @@ public static class ExportWrapper Snowflake messageId ) { - var message = (await GetMessagesAsJsonAsync(channelId)).SingleOrDefault( - j => - string.Equals( - j.GetProperty("id").GetString(), - messageId.ToString(), - StringComparison.OrdinalIgnoreCase - ) + var message = (await GetMessagesAsJsonAsync(channelId)).SingleOrDefault(j => + string.Equals( + j.GetProperty("id").GetString(), + messageId.ToString(), + StringComparison.OrdinalIgnoreCase + ) ); if (message.ValueKind == JsonValueKind.Undefined) diff --git a/DiscordChatExporter.Cli.Tests/Infra/Secrets.cs b/DiscordChatExporter.Cli.Tests/Infra/Secrets.cs index 67141575..d907b34a 100644 --- a/DiscordChatExporter.Cli.Tests/Infra/Secrets.cs +++ b/DiscordChatExporter.Cli.Tests/Infra/Secrets.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Reflection; using Microsoft.Extensions.Configuration; diff --git a/DiscordChatExporter.Cli.Tests/Readme.md b/DiscordChatExporter.Cli.Tests/Readme.md index 359d2990..86bce440 100644 --- a/DiscordChatExporter.Cli.Tests/Readme.md +++ b/DiscordChatExporter.Cli.Tests/Readme.md @@ -1,4 +1,4 @@ -# DiscordChatExporter Tests +# DiscordChatExporter Tests This test suite runs against a real Discord server, specifically created to exercise different behaviors required by the test scenarios. In order to run these tests locally, you need to join the test server and configure your authentication token. @@ -8,5 +8,5 @@ In order to run these tests locally, you need to join the test server and config 3. Add your token to user secrets: `dotnet user-secrets set DISCORD_TOKEN ` 4. Run the tests: `dotnet test` -> **Note**: +> [!NOTE] > If you want to add a new test case, please let me know and I will give you the required permissions on the server. \ No newline at end of file diff --git a/DiscordChatExporter.Cli.Tests/Specs/CsvContentSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/CsvContentSpecs.cs index a4630d8a..abb7a959 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/CsvContentSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/CsvContentSpecs.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using DiscordChatExporter.Cli.Tests.Infra; using FluentAssertions; using Xunit; diff --git a/DiscordChatExporter.Cli.Tests/Specs/DateRangeSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/DateRangeSpecs.cs index 72577112..5d5bcac4 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/DateRangeSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/DateRangeSpecs.cs @@ -1,15 +1,15 @@ -using System; +using System; using System.IO; using System.Linq; using System.Threading.Tasks; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands; using DiscordChatExporter.Cli.Tests.Infra; -using DiscordChatExporter.Cli.Tests.Utils; using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Exporting; using FluentAssertions; using JsonExtensions; +using PowerKit; using Xunit; namespace DiscordChatExporter.Cli.Tests.Specs; @@ -30,7 +30,7 @@ public class DateRangeSpecs ChannelIds = [ChannelIds.DateRangeTestCases], ExportFormat = ExportFormat.Json, OutputPath = file.Path, - After = Snowflake.FromDate(after) + After = Snowflake.FromDate(after), }.ExecuteAsync(new FakeConsole()); // Assert @@ -50,13 +50,11 @@ public class DateRangeSpecs new DateTimeOffset(2021, 07, 24, 14, 52, 38, TimeSpan.Zero), new DateTimeOffset(2021, 07, 24, 14, 52, 39, TimeSpan.Zero), new DateTimeOffset(2021, 07, 24, 14, 52, 40, TimeSpan.Zero), - new DateTimeOffset(2021, 09, 08, 14, 26, 35, TimeSpan.Zero) + new DateTimeOffset(2021, 09, 08, 14, 26, 35, TimeSpan.Zero), ], o => - o.Using( - ctx => - ctx.Subject.Should() - .BeCloseTo(ctx.Expectation, TimeSpan.FromSeconds(1)) + o.Using(ctx => + ctx.Subject.Should().BeCloseTo(ctx.Expectation, TimeSpan.FromSeconds(1)) ) .WhenTypeIs() ); @@ -76,7 +74,7 @@ public class DateRangeSpecs ChannelIds = [ChannelIds.DateRangeTestCases], ExportFormat = ExportFormat.Json, OutputPath = file.Path, - Before = Snowflake.FromDate(before) + Before = Snowflake.FromDate(before), }.ExecuteAsync(new FakeConsole()); // Assert @@ -94,13 +92,11 @@ public class DateRangeSpecs [ new DateTimeOffset(2021, 07, 19, 13, 34, 18, TimeSpan.Zero), new DateTimeOffset(2021, 07, 19, 15, 58, 48, TimeSpan.Zero), - new DateTimeOffset(2021, 07, 19, 17, 23, 58, TimeSpan.Zero) + new DateTimeOffset(2021, 07, 19, 17, 23, 58, TimeSpan.Zero), ], o => - o.Using( - ctx => - ctx.Subject.Should() - .BeCloseTo(ctx.Expectation, TimeSpan.FromSeconds(1)) + o.Using(ctx => + ctx.Subject.Should().BeCloseTo(ctx.Expectation, TimeSpan.FromSeconds(1)) ) .WhenTypeIs() ); @@ -122,7 +118,7 @@ public class DateRangeSpecs ExportFormat = ExportFormat.Json, OutputPath = file.Path, Before = Snowflake.FromDate(before), - After = Snowflake.FromDate(after) + After = Snowflake.FromDate(after), }.ExecuteAsync(new FakeConsole()); // Assert @@ -141,15 +137,40 @@ public class DateRangeSpecs new DateTimeOffset(2021, 07, 24, 13, 49, 13, TimeSpan.Zero), new DateTimeOffset(2021, 07, 24, 14, 52, 38, TimeSpan.Zero), new DateTimeOffset(2021, 07, 24, 14, 52, 39, TimeSpan.Zero), - new DateTimeOffset(2021, 07, 24, 14, 52, 40, TimeSpan.Zero) + new DateTimeOffset(2021, 07, 24, 14, 52, 40, TimeSpan.Zero), ], o => - o.Using( - ctx => - ctx.Subject.Should() - .BeCloseTo(ctx.Expectation, TimeSpan.FromSeconds(1)) + o.Using(ctx => + ctx.Subject.Should().BeCloseTo(ctx.Expectation, TimeSpan.FromSeconds(1)) ) .WhenTypeIs() ); } + + [Fact] + public async Task I_can_filter_the_export_to_not_include_any_messages() + { + // Arrange + var before = new DateTimeOffset(2020, 08, 01, 0, 0, 0, TimeSpan.Zero); + using var file = TempFile.Create(); + + // Act + await new ExportChannelsCommand + { + Token = Secrets.DiscordToken, + ChannelIds = [ChannelIds.DateRangeTestCases], + ExportFormat = ExportFormat.Json, + OutputPath = file.Path, + Before = Snowflake.FromDate(before), + }.ExecuteAsync(new FakeConsole()); + + // Assert + var timestamps = Json.Parse(await File.ReadAllTextAsync(file.Path)) + .GetProperty("messages") + .EnumerateArray() + .Select(j => j.GetProperty("timestamp").GetDateTimeOffset()) + .ToArray(); + + timestamps.Should().BeEmpty(); + } } diff --git a/DiscordChatExporter.Cli.Tests/Specs/FilterSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/FilterSpecs.cs index 06e08705..50ab7297 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/FilterSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/FilterSpecs.cs @@ -1,15 +1,15 @@ -using System; +using System; using System.IO; using System.Linq; using System.Threading.Tasks; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands; using DiscordChatExporter.Cli.Tests.Infra; -using DiscordChatExporter.Cli.Tests.Utils; using DiscordChatExporter.Core.Exporting; using DiscordChatExporter.Core.Exporting.Filtering; using FluentAssertions; using JsonExtensions; +using PowerKit; using Xunit; namespace DiscordChatExporter.Cli.Tests.Specs; @@ -29,7 +29,7 @@ public class FilterSpecs ChannelIds = [ChannelIds.FilterTestCases], ExportFormat = ExportFormat.Json, OutputPath = file.Path, - MessageFilter = MessageFilter.Parse("some text") + MessageFilter = MessageFilter.Parse("some text"), }.ExecuteAsync(new FakeConsole()); // Assert @@ -54,7 +54,7 @@ public class FilterSpecs ChannelIds = [ChannelIds.FilterTestCases], ExportFormat = ExportFormat.Json, OutputPath = file.Path, - MessageFilter = MessageFilter.Parse("from:Tyrrrz") + MessageFilter = MessageFilter.Parse("from:Tyrrrz"), }.ExecuteAsync(new FakeConsole()); // Assert @@ -79,7 +79,7 @@ public class FilterSpecs ChannelIds = [ChannelIds.FilterTestCases], ExportFormat = ExportFormat.Json, OutputPath = file.Path, - MessageFilter = MessageFilter.Parse("has:image") + MessageFilter = MessageFilter.Parse("has:image"), }.ExecuteAsync(new FakeConsole()); // Assert @@ -104,7 +104,7 @@ public class FilterSpecs ChannelIds = [ChannelIds.FilterTestCases], ExportFormat = ExportFormat.Json, OutputPath = file.Path, - MessageFilter = MessageFilter.Parse("has:pin") + MessageFilter = MessageFilter.Parse("has:pin"), }.ExecuteAsync(new FakeConsole()); // Assert @@ -129,7 +129,7 @@ public class FilterSpecs ChannelIds = [ChannelIds.FilterTestCases], ExportFormat = ExportFormat.Json, OutputPath = file.Path, - MessageFilter = MessageFilter.Parse("has:invite") + MessageFilter = MessageFilter.Parse("has:invite"), }.ExecuteAsync(new FakeConsole()); // Assert @@ -154,7 +154,7 @@ public class FilterSpecs ChannelIds = [ChannelIds.FilterTestCases], ExportFormat = ExportFormat.Json, OutputPath = file.Path, - MessageFilter = MessageFilter.Parse("mentions:Tyrrrz") + MessageFilter = MessageFilter.Parse("mentions:Tyrrrz"), }.ExecuteAsync(new FakeConsole()); // Assert diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlAttachmentSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlAttachmentSpecs.cs index 6ac83f11..cfc64614 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlAttachmentSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlAttachmentSpecs.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading.Tasks; using AngleSharp.Dom; diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlContentSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlContentSpecs.cs index fcb1356a..e2ea424c 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlContentSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlContentSpecs.cs @@ -1,8 +1,14 @@ -using System.Linq; +using System.IO; +using System.Linq; using System.Threading.Tasks; using AngleSharp.Dom; +using CliFx.Infrastructure; +using DiscordChatExporter.Cli.Commands; using DiscordChatExporter.Cli.Tests.Infra; +using DiscordChatExporter.Cli.Tests.Utils; +using DiscordChatExporter.Core.Exporting; using FluentAssertions; +using PowerKit; using Xunit; namespace DiscordChatExporter.Cli.Tests.Specs; @@ -44,4 +50,41 @@ public class HtmlContentSpecs "Yeet" ); } + + [Fact] + public async Task I_can_export_a_channel_in_the_HTML_format_in_the_reverse_order() + { + // Arrange + using var file = TempFile.Create(); + + // Act + await new ExportChannelsCommand + { + Token = Secrets.DiscordToken, + ChannelIds = [ChannelIds.DateRangeTestCases], + ExportFormat = ExportFormat.HtmlDark, + OutputPath = file.Path, + Locale = "en-US", + IsUtcNormalizationEnabled = true, + IsReverseMessageOrder = true, + }.ExecuteAsync(new FakeConsole()); + + var document = Html.Parse(await File.ReadAllTextAsync(file.Path)); + var messages = document.QuerySelectorAll("[data-message-id]").ToArray(); + + // Assert + messages + .Select(e => e.GetAttribute("data-message-id")) + .Should() + .Equal( + "885169254029213696", + "868505973294268457", + "868505969821364245", + "868505966528835604", + "868490009366396958", + "866732113319428096", + "866710679758045195", + "866674314627121232" + ); + } } diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlEmbedSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlEmbedSpecs.cs index 8ed0fc56..8cc4e153 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlEmbedSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlEmbedSpecs.cs @@ -1,11 +1,11 @@ -using System; +using System; using System.Linq; using System.Threading.Tasks; using AngleSharp.Dom; using DiscordChatExporter.Cli.Tests.Infra; using DiscordChatExporter.Core.Discord; -using DiscordChatExporter.Core.Utils.Extensions; using FluentAssertions; +using PowerKit.Extensions; using Xunit; namespace DiscordChatExporter.Cli.Tests.Specs; @@ -90,12 +90,11 @@ public class HtmlEmbedSpecs .QuerySelectorAll("source") .Select(e => e.GetAttribute("src")) .WhereNotNull() - .Where( - s => - s.Contains( - "i_am_currently_feeling_slight_displeasure_of_what_you_have_just_sent_lqrem.mp4", - StringComparison.Ordinal - ) + .Where(s => + s.Contains( + "i_am_currently_feeling_slight_displeasure_of_what_you_have_just_sent_lqrem.mp4", + StringComparison.Ordinal + ) ) .Should() .ContainSingle(); @@ -150,6 +149,26 @@ public class HtmlEmbedSpecs iframeUrl.Should().StartWith("https://open.spotify.com/embed/track/1LHZMWefF9502NPfArRfvP"); } + [Fact(Skip = "Twitch does not allow embeds from inside local HTML files")] + public async Task I_can_export_a_channel_that_contains_a_message_with_a_Twitch_clip_embed() + { + // https://github.com/Tyrrrz/DiscordChatExporter/issues/1196 + + // Act + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.EmbedTestCases, + Snowflake.Parse("1207002986128216074") + ); + + // Assert + var iframeUrl = message.QuerySelector("iframe")?.GetAttribute("src"); + iframeUrl + .Should() + .StartWith( + "https://clips.twitch.tv/embed?clip=SpicyMildCiderThisIsSparta--PQhbllrvej_Ee7v" + ); + } + [Fact] public async Task I_can_export_a_channel_that_contains_a_message_with_a_YouTube_video_embed() { @@ -162,8 +181,14 @@ public class HtmlEmbedSpecs ); // Assert - var iframeUrl = message.QuerySelector("iframe")?.GetAttribute("src"); - iframeUrl.Should().StartWith("https://www.youtube.com/embed/qOWW4OlgbvE"); + // Check that the YouTube video thumbnail image exists with the correct video ID + var youtubeThumbnailSrc = message + .QuerySelectorAll("img") + .Select(e => e.GetAttribute("src")) + .WhereNotNull() + .FirstOrDefault(s => s.Contains("qOWW4OlgbvE", StringComparison.Ordinal)); + + youtubeThumbnailSrc.Should().NotBeNull(); } [Fact] @@ -185,42 +210,38 @@ public class HtmlEmbedSpecs imageUrls .Should() - .Contain( - u => - u.EndsWith( - "https/pbs.twimg.com/media/FVYIzYPWAAAMBqZ.png", - StringComparison.Ordinal - ) + .Contain(u => + u.EndsWith( + "https/pbs.twimg.com/media/FVYIzYPWAAAMBqZ.png", + StringComparison.Ordinal + ) ); imageUrls .Should() - .Contain( - u => - u.EndsWith( - "https/pbs.twimg.com/media/FVYJBWJWAAMNAx2.png", - StringComparison.Ordinal - ) + .Contain(u => + u.EndsWith( + "https/pbs.twimg.com/media/FVYJBWJWAAMNAx2.png", + StringComparison.Ordinal + ) ); imageUrls .Should() - .Contain( - u => - u.EndsWith( - "https/pbs.twimg.com/media/FVYJHiRX0AANZcz.png", - StringComparison.Ordinal - ) + .Contain(u => + u.EndsWith( + "https/pbs.twimg.com/media/FVYJHiRX0AANZcz.png", + StringComparison.Ordinal + ) ); imageUrls .Should() - .Contain( - u => - u.EndsWith( - "https/pbs.twimg.com/media/FVYJNZNXwAAPnVG.png", - StringComparison.Ordinal - ) + .Contain(u => + u.EndsWith( + "https/pbs.twimg.com/media/FVYJNZNXwAAPnVG.png", + StringComparison.Ordinal + ) ); message.QuerySelectorAll(".chatlog__embed").Should().ContainSingle(); diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlForwardSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlForwardSpecs.cs new file mode 100644 index 00000000..bc4b24c0 --- /dev/null +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlForwardSpecs.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using AngleSharp.Dom; +using DiscordChatExporter.Cli.Tests.Infra; +using DiscordChatExporter.Core.Discord; +using FluentAssertions; +using PowerKit.Extensions; +using Xunit; + +namespace DiscordChatExporter.Cli.Tests.Specs; + +public class HtmlForwardSpecs +{ + [Fact] + public async Task I_can_export_a_channel_that_contains_a_forwarded_message() + { + // Act + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.ForwardTestCases, + Snowflake.Parse("1455202427115536514") + ); + + // Assert + message + .Text() + .ReplaceWhiteSpace(' ') + .Should() + .ContainAll("Forwarded", @"¯\_(ツ)_/¯", "12/29/2025 2:14 PM"); + } +} diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlGroupingSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlGroupingSpecs.cs index 8ccb64a2..2eb7651c 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlGroupingSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlGroupingSpecs.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using System.Linq; using System.Threading.Tasks; using AngleSharp.Dom; @@ -8,6 +8,7 @@ using DiscordChatExporter.Cli.Tests.Infra; using DiscordChatExporter.Cli.Tests.Utils; using DiscordChatExporter.Core.Exporting; using FluentAssertions; +using PowerKit; using Xunit; namespace DiscordChatExporter.Cli.Tests.Specs; @@ -28,7 +29,7 @@ public class HtmlGroupingSpecs Token = Secrets.DiscordToken, ChannelIds = [ChannelIds.GroupingTestCases], ExportFormat = ExportFormat.HtmlDark, - OutputPath = file.Path + OutputPath = file.Path, }.ExecuteAsync(new FakeConsole()); // Assert diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlMarkdownSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlMarkdownSpecs.cs index 7340467c..893324b4 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlMarkdownSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlMarkdownSpecs.cs @@ -1,8 +1,9 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using AngleSharp.Dom; using DiscordChatExporter.Cli.Tests.Infra; using DiscordChatExporter.Core.Discord; using FluentAssertions; +using PowerKit.Extensions; using Xunit; namespace DiscordChatExporter.Cli.Tests.Specs; @@ -19,8 +20,16 @@ public class HtmlMarkdownSpecs ); // Assert - message.Text().Should().Contain("Default timestamp: 2/12/2023 1:36 PM"); - message.InnerHtml.Should().Contain("Sunday, February 12, 2023 1:36 PM"); + message + .Text() + .ReplaceWhiteSpace(' ') + .Should() + .Contain("Default timestamp: 2/12/2023 1:36 PM"); + + message + .InnerHtml.ReplaceWhiteSpace(' ') + .Should() + .Contain("Sunday, February 12, 2023 1:36 PM"); } [Fact] @@ -33,8 +42,11 @@ public class HtmlMarkdownSpecs ); // Assert - message.Text().Should().Contain("Short time timestamp: 1:36 PM"); - message.InnerHtml.Should().Contain("Sunday, February 12, 2023 1:36 PM"); + message.Text().ReplaceWhiteSpace(' ').Should().Contain("Short time timestamp: 1:36 PM"); + message + .InnerHtml.ReplaceWhiteSpace(' ') + .Should() + .Contain("Sunday, February 12, 2023 1:36 PM"); } [Fact] @@ -47,8 +59,11 @@ public class HtmlMarkdownSpecs ); // Assert - message.Text().Should().Contain("Long time timestamp: 1:36:12 PM"); - message.InnerHtml.Should().Contain("Sunday, February 12, 2023 1:36 PM"); + message.Text().ReplaceWhiteSpace(' ').Should().Contain("Long time timestamp: 1:36:12 PM"); + message + .InnerHtml.ReplaceWhiteSpace(' ') + .Should() + .Contain("Sunday, February 12, 2023 1:36 PM"); } [Fact] @@ -61,8 +76,11 @@ public class HtmlMarkdownSpecs ); // Assert - message.Text().Should().Contain("Short date timestamp: 2/12/2023"); - message.InnerHtml.Should().Contain("Sunday, February 12, 2023 1:36 PM"); + message.Text().ReplaceWhiteSpace(' ').Should().Contain("Short date timestamp: 2/12/2023"); + message + .InnerHtml.ReplaceWhiteSpace(' ') + .Should() + .Contain("Sunday, February 12, 2023 1:36 PM"); } [Fact] @@ -75,8 +93,16 @@ public class HtmlMarkdownSpecs ); // Assert - message.Text().Should().Contain("Long date timestamp: Sunday, February 12, 2023"); - message.InnerHtml.Should().Contain("Sunday, February 12, 2023 1:36 PM"); + message + .Text() + .ReplaceWhiteSpace(' ') + .Should() + .Contain("Long date timestamp: Sunday, February 12, 2023"); + + message + .InnerHtml.ReplaceWhiteSpace(' ') + .Should() + .Contain("Sunday, February 12, 2023 1:36 PM"); } [Fact] @@ -89,8 +115,16 @@ public class HtmlMarkdownSpecs ); // Assert - message.Text().Should().Contain("Full timestamp: Sunday, February 12, 2023 1:36 PM"); - message.InnerHtml.Should().Contain("Sunday, February 12, 2023 1:36 PM"); + message + .Text() + .ReplaceWhiteSpace(' ') + .Should() + .Contain("Full timestamp: Sunday, February 12, 2023 1:36 PM"); + + message + .InnerHtml.ReplaceWhiteSpace(' ') + .Should() + .Contain("Sunday, February 12, 2023 1:36 PM"); } [Fact] @@ -105,9 +139,14 @@ public class HtmlMarkdownSpecs // Assert message .Text() + .ReplaceWhiteSpace(' ') .Should() .Contain("Full long timestamp: Sunday, February 12, 2023 1:36:12 PM"); - message.InnerHtml.Should().Contain("Sunday, February 12, 2023 1:36 PM"); + + message + .InnerHtml.ReplaceWhiteSpace(' ') + .Should() + .Contain("Sunday, February 12, 2023 1:36 PM"); } [Fact] @@ -120,8 +159,16 @@ public class HtmlMarkdownSpecs ); // Assert - message.Text().Should().Contain("Relative timestamp: 2/12/2023 1:36 PM"); - message.InnerHtml.Should().Contain("Sunday, February 12, 2023 1:36 PM"); + message + .Text() + .ReplaceWhiteSpace(' ') + .Should() + .Contain("Relative timestamp: 2/12/2023 1:36 PM"); + + message + .InnerHtml.ReplaceWhiteSpace(' ') + .Should() + .Contain("Sunday, February 12, 2023 1:36 PM"); } [Fact] diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlMentionSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlMentionSpecs.cs index afb21b8f..6f9af27e 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlMentionSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlMentionSpecs.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using AngleSharp.Dom; using DiscordChatExporter.Cli.Tests.Infra; using DiscordChatExporter.Core.Discord; @@ -61,4 +61,17 @@ public class HtmlMentionSpecs // Assert message.Text().Should().Contain("Role mention: @Role 1"); } + + [Fact] + public async Task I_can_export_a_channel_that_contains_a_message_with_a_thread_mention() + { + // Act + var message = await ExportWrapper.GetMessageAsHtmlAsync( + ChannelIds.MentionTestCases, + Snowflake.Parse("1474874276828938290") + ); + + // Assert + message.Text().Should().Contain("Thread mention: #Thread starting message"); + } } diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlReplySpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlReplySpecs.cs index 01c6b682..da343972 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlReplySpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlReplySpecs.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using AngleSharp.Dom; using DiscordChatExporter.Cli.Tests.Infra; using DiscordChatExporter.Core.Discord; diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlStickerSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlStickerSpecs.cs index 44d7932a..f8516952 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlStickerSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlStickerSpecs.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using DiscordChatExporter.Cli.Tests.Infra; using DiscordChatExporter.Core.Discord; using FluentAssertions; diff --git a/DiscordChatExporter.Cli.Tests/Specs/JsonAttachmentSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/JsonAttachmentSpecs.cs index 20539ebc..b54d4560 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/JsonAttachmentSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/JsonAttachmentSpecs.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Threading.Tasks; using DiscordChatExporter.Cli.Tests.Infra; using DiscordChatExporter.Core.Discord; diff --git a/DiscordChatExporter.Cli.Tests/Specs/JsonContentSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/JsonContentSpecs.cs index 0acc5f63..a93b6b07 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/JsonContentSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/JsonContentSpecs.cs @@ -1,7 +1,13 @@ -using System.Linq; +using System.IO; +using System.Linq; using System.Threading.Tasks; +using CliFx.Infrastructure; +using DiscordChatExporter.Cli.Commands; using DiscordChatExporter.Cli.Tests.Infra; +using DiscordChatExporter.Core.Exporting; using FluentAssertions; +using JsonExtensions; +using PowerKit; using Xunit; namespace DiscordChatExporter.Cli.Tests.Specs; @@ -43,4 +49,43 @@ public class JsonContentSpecs "Yeet" ); } + + [Fact] + public async Task I_can_export_a_channel_in_the_JSON_format_in_the_reverse_order() + { + // Arrange + using var file = TempFile.Create(); + + // Act + await new ExportChannelsCommand + { + Token = Secrets.DiscordToken, + ChannelIds = [ChannelIds.DateRangeTestCases], + ExportFormat = ExportFormat.Json, + OutputPath = file.Path, + Locale = "en-US", + IsUtcNormalizationEnabled = true, + IsReverseMessageOrder = true, + }.ExecuteAsync(new FakeConsole()); + + var messages = Json.Parse(await File.ReadAllTextAsync(file.Path)) + .GetProperty("messages") + .EnumerateArray() + .ToArray(); + + // Assert + messages + .Select(j => j.GetProperty("id").GetString()) + .Should() + .Equal( + "885169254029213696", + "868505973294268457", + "868505969821364245", + "868505966528835604", + "868490009366396958", + "866732113319428096", + "866710679758045195", + "866674314627121232" + ); + } } diff --git a/DiscordChatExporter.Cli.Tests/Specs/JsonEmbedSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/JsonEmbedSpecs.cs index ad614b0f..f2bc3ecf 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/JsonEmbedSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/JsonEmbedSpecs.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Threading.Tasks; using DiscordChatExporter.Cli.Tests.Infra; using DiscordChatExporter.Core.Discord; diff --git a/DiscordChatExporter.Cli.Tests/Specs/JsonEmojiSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/JsonEmojiSpecs.cs new file mode 100644 index 00000000..d45888d5 --- /dev/null +++ b/DiscordChatExporter.Cli.Tests/Specs/JsonEmojiSpecs.cs @@ -0,0 +1,69 @@ +using System.Linq; +using System.Threading.Tasks; +using DiscordChatExporter.Cli.Tests.Infra; +using DiscordChatExporter.Core.Discord; +using FluentAssertions; +using Xunit; + +namespace DiscordChatExporter.Cli.Tests.Specs; + +public class JsonEmojiSpecs +{ + [Fact] + public async Task I_can_export_a_channel_that_contains_a_message_with_inline_emoji_and_have_them_listed_separately() + { + // Act + var message = await ExportWrapper.GetMessageAsJsonAsync( + ChannelIds.EmojiTestCases, + Snowflake.Parse("866768521052553216") + ); + + // Assert + var inlineEmojis = message.GetProperty("inlineEmojis").EnumerateArray().ToArray(); + inlineEmojis.Should().HaveCount(4); + + inlineEmojis[0].GetProperty("id").GetString().Should().BeNullOrEmpty(); + inlineEmojis[0].GetProperty("name").GetString().Should().Be("🙂"); + inlineEmojis[0].GetProperty("code").GetString().Should().Be("slight_smile"); + inlineEmojis[0].GetProperty("isAnimated").GetBoolean().Should().BeFalse(); + inlineEmojis[0].GetProperty("imageUrl").GetString().Should().NotBeNullOrWhiteSpace(); + + inlineEmojis[1].GetProperty("id").GetString().Should().BeNullOrEmpty(); + inlineEmojis[1].GetProperty("name").GetString().Should().Be("😦"); + inlineEmojis[1].GetProperty("code").GetString().Should().Be("frowning"); + inlineEmojis[1].GetProperty("isAnimated").GetBoolean().Should().BeFalse(); + inlineEmojis[1].GetProperty("imageUrl").GetString().Should().NotBeNullOrWhiteSpace(); + + inlineEmojis[2].GetProperty("id").GetString().Should().BeNullOrEmpty(); + inlineEmojis[2].GetProperty("name").GetString().Should().Be("😔"); + inlineEmojis[2].GetProperty("code").GetString().Should().Be("pensive"); + inlineEmojis[2].GetProperty("isAnimated").GetBoolean().Should().BeFalse(); + inlineEmojis[2].GetProperty("imageUrl").GetString().Should().NotBeNullOrWhiteSpace(); + + inlineEmojis[3].GetProperty("id").GetString().Should().BeNullOrEmpty(); + inlineEmojis[3].GetProperty("name").GetString().Should().Be("😂"); + inlineEmojis[3].GetProperty("code").GetString().Should().Be("joy"); + inlineEmojis[3].GetProperty("isAnimated").GetBoolean().Should().BeFalse(); + inlineEmojis[3].GetProperty("imageUrl").GetString().Should().NotBeNullOrWhiteSpace(); + } + + [Fact] + public async Task I_can_export_a_channel_that_contains_a_message_with_custom_inline_emoji_and_have_them_listed_separately() + { + // Act + var message = await ExportWrapper.GetMessageAsJsonAsync( + ChannelIds.EmojiTestCases, + Snowflake.Parse("1299804867447230594") + ); + + // Assert + var inlineEmojis = message.GetProperty("inlineEmojis").EnumerateArray().ToArray(); + inlineEmojis.Should().HaveCount(1); + + inlineEmojis[0].GetProperty("id").GetString().Should().Be("754441880066064584"); + inlineEmojis[0].GetProperty("name").GetString().Should().Be("lemon_blush"); + inlineEmojis[0].GetProperty("code").GetString().Should().Be("lemon_blush"); + inlineEmojis[0].GetProperty("isAnimated").GetBoolean().Should().BeFalse(); + inlineEmojis[0].GetProperty("imageUrl").GetString().Should().NotBeNullOrWhiteSpace(); + } +} diff --git a/DiscordChatExporter.Cli.Tests/Specs/JsonForwardSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/JsonForwardSpecs.cs new file mode 100644 index 00000000..4fa5cae6 --- /dev/null +++ b/DiscordChatExporter.Cli.Tests/Specs/JsonForwardSpecs.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using DiscordChatExporter.Cli.Tests.Infra; +using DiscordChatExporter.Core.Discord; +using FluentAssertions; +using Xunit; + +namespace DiscordChatExporter.Cli.Tests.Specs; + +public class JsonForwardSpecs +{ + [Fact] + public async Task I_can_export_a_channel_that_contains_a_forwarded_message() + { + // Act + var message = await ExportWrapper.GetMessageAsJsonAsync( + ChannelIds.ForwardTestCases, + Snowflake.Parse("1455202427115536514") + ); + + // Assert + var reference = message.GetProperty("reference"); + reference.GetProperty("type").GetString().Should().Be("Forward"); + reference.GetProperty("guildId").GetString().Should().Be("869237470565392384"); + + var forwardedMessage = message.GetProperty("forwardedMessage"); + forwardedMessage.GetProperty("content").GetString().Should().Contain(@"¯\_(ツ)_/¯"); + forwardedMessage + .GetProperty("timestamp") + .GetString() + .Should() + .StartWith("2025-12-28T22:52:42.175+00:00"); + } +} diff --git a/DiscordChatExporter.Cli.Tests/Specs/JsonMentionSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/JsonMentionSpecs.cs index 171dbea6..1f93d598 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/JsonMentionSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/JsonMentionSpecs.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Threading.Tasks; using DiscordChatExporter.Cli.Tests.Infra; using DiscordChatExporter.Core.Discord; @@ -75,4 +75,21 @@ public class JsonMentionSpecs // Assert message.GetProperty("content").GetString().Should().Be("Role mention: @Role 1"); } + + [Fact] + public async Task I_can_export_a_channel_that_contains_a_message_with_a_thread_mention() + { + // Act + var message = await ExportWrapper.GetMessageAsJsonAsync( + ChannelIds.MentionTestCases, + Snowflake.Parse("1474874276828938290") + ); + + // Assert + message + .GetProperty("content") + .GetString() + .Should() + .Be("Thread mention: #Thread starting message"); + } } diff --git a/DiscordChatExporter.Cli.Tests/Specs/JsonStickerSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/JsonStickerSpecs.cs index d8d81202..0e8ba8c6 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/JsonStickerSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/JsonStickerSpecs.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Threading.Tasks; using DiscordChatExporter.Cli.Tests.Infra; using DiscordChatExporter.Core.Discord; diff --git a/DiscordChatExporter.Cli.Tests/Specs/PartitioningSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/PartitioningSpecs.cs index e245f914..dcae0ad3 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/PartitioningSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/PartitioningSpecs.cs @@ -1,12 +1,12 @@ -using System.IO; +using System.IO; using System.Threading.Tasks; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands; using DiscordChatExporter.Cli.Tests.Infra; -using DiscordChatExporter.Cli.Tests.Utils; using DiscordChatExporter.Core.Exporting; using DiscordChatExporter.Core.Exporting.Partitioning; using FluentAssertions; +using PowerKit; using Xunit; namespace DiscordChatExporter.Cli.Tests.Specs; @@ -17,7 +17,7 @@ public class PartitioningSpecs public async Task I_can_export_a_channel_with_partitioning_based_on_message_count() { // Arrange - using var dir = TempDir.Create(); + using var dir = TempDirectory.Create(); var filePath = Path.Combine(dir.Path, "output.html"); // Act @@ -27,7 +27,7 @@ public class PartitioningSpecs ChannelIds = [ChannelIds.DateRangeTestCases], ExportFormat = ExportFormat.HtmlDark, OutputPath = filePath, - PartitionLimit = PartitionLimit.Parse("3") + PartitionLimit = PartitionLimit.Parse("3"), }.ExecuteAsync(new FakeConsole()); // Assert @@ -38,7 +38,7 @@ public class PartitioningSpecs public async Task I_can_export_a_channel_with_partitioning_based_on_file_size() { // Arrange - using var dir = TempDir.Create(); + using var dir = TempDirectory.Create(); var filePath = Path.Combine(dir.Path, "output.html"); // Act @@ -48,7 +48,7 @@ public class PartitioningSpecs ChannelIds = [ChannelIds.DateRangeTestCases], ExportFormat = ExportFormat.HtmlDark, OutputPath = filePath, - PartitionLimit = PartitionLimit.Parse("1kb") + PartitionLimit = PartitionLimit.Parse("1kb"), }.ExecuteAsync(new FakeConsole()); // Assert diff --git a/DiscordChatExporter.Cli.Tests/Specs/PlainTextContentSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/PlainTextContentSpecs.cs index a65cae8c..ef7b941f 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/PlainTextContentSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/PlainTextContentSpecs.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using DiscordChatExporter.Cli.Tests.Infra; using FluentAssertions; using Xunit; diff --git a/DiscordChatExporter.Cli.Tests/Specs/PlainTextForwardSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/PlainTextForwardSpecs.cs new file mode 100644 index 00000000..5b0a1f88 --- /dev/null +++ b/DiscordChatExporter.Cli.Tests/Specs/PlainTextForwardSpecs.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using DiscordChatExporter.Cli.Tests.Infra; +using FluentAssertions; +using PowerKit.Extensions; +using Xunit; + +namespace DiscordChatExporter.Cli.Tests.Specs; + +public class PlainTextForwardSpecs +{ + [Fact] + public async Task I_can_export_a_channel_that_contains_a_forwarded_message() + { + // Act + var document = await ExportWrapper.ExportAsPlainTextAsync(ChannelIds.ForwardTestCases); + + // Assert + document + .ReplaceWhiteSpace(' ') + .Should() + .ContainAll("{Forwarded Message}", @"¯\_(ツ)_/¯", "12/28/2025 10:52 PM"); + } +} diff --git a/DiscordChatExporter.Cli.Tests/Specs/SelfContainedSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/SelfContainedSpecs.cs index 8699e92e..25bda4ba 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/SelfContainedSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/SelfContainedSpecs.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using System.Linq; using System.Threading.Tasks; using CliFx.Infrastructure; @@ -7,6 +7,7 @@ using DiscordChatExporter.Cli.Tests.Infra; using DiscordChatExporter.Cli.Tests.Utils; using DiscordChatExporter.Core.Exporting; using FluentAssertions; +using PowerKit; using Xunit; namespace DiscordChatExporter.Cli.Tests.Specs; @@ -17,7 +18,7 @@ public class SelfContainedSpecs public async Task I_can_export_a_channel_and_download_all_referenced_assets() { // Arrange - using var dir = TempDir.Create(); + using var dir = TempDirectory.Create(); var filePath = Path.Combine(dir.Path, "output.html"); // Act @@ -27,7 +28,7 @@ public class SelfContainedSpecs ChannelIds = [ChannelIds.SelfContainedTestCases], ExportFormat = ExportFormat.HtmlDark, OutputPath = filePath, - ShouldDownloadAssets = true + ShouldDownloadAssets = true, }.ExecuteAsync(new FakeConsole()); // Assert diff --git a/DiscordChatExporter.Cli.Tests/Utils/Html.cs b/DiscordChatExporter.Cli.Tests/Utils/Html.cs index 482e7d32..c3d4b60d 100644 --- a/DiscordChatExporter.Cli.Tests/Utils/Html.cs +++ b/DiscordChatExporter.Cli.Tests/Utils/Html.cs @@ -1,4 +1,4 @@ -using AngleSharp.Html.Dom; +using AngleSharp.Html.Dom; using AngleSharp.Html.Parser; namespace DiscordChatExporter.Cli.Tests.Utils; diff --git a/DiscordChatExporter.Cli.Tests/Utils/TempDir.cs b/DiscordChatExporter.Cli.Tests/Utils/TempDir.cs deleted file mode 100644 index b65888e9..00000000 --- a/DiscordChatExporter.Cli.Tests/Utils/TempDir.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.IO; -using System.Reflection; -using PathEx = System.IO.Path; - -namespace DiscordChatExporter.Cli.Tests.Utils; - -internal partial class TempDir(string path) : IDisposable -{ - public string Path { get; } = path; - - public void Dispose() - { - try - { - Directory.Delete(Path, true); - } - catch (DirectoryNotFoundException) { } - } -} - -internal partial class TempDir -{ - public static TempDir Create() - { - var dirPath = PathEx.Combine( - PathEx.GetDirectoryName(Assembly.GetExecutingAssembly().Location) - ?? Directory.GetCurrentDirectory(), - "Temp", - Guid.NewGuid().ToString() - ); - - Directory.CreateDirectory(dirPath); - - return new TempDir(dirPath); - } -} diff --git a/DiscordChatExporter.Cli.Tests/Utils/TempFile.cs b/DiscordChatExporter.Cli.Tests/Utils/TempFile.cs deleted file mode 100644 index e42ccbe8..00000000 --- a/DiscordChatExporter.Cli.Tests/Utils/TempFile.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.IO; -using System.Reflection; -using PathEx = System.IO.Path; - -namespace DiscordChatExporter.Cli.Tests.Utils; - -internal partial class TempFile(string path) : IDisposable -{ - public string Path { get; } = path; - - public void Dispose() - { - try - { - File.Delete(Path); - } - catch (FileNotFoundException) { } - } -} - -internal partial class TempFile -{ - public static TempFile Create() - { - var dirPath = PathEx.Combine( - PathEx.GetDirectoryName(Assembly.GetExecutingAssembly().Location) - ?? Directory.GetCurrentDirectory(), - "Temp" - ); - - Directory.CreateDirectory(dirPath); - - var filePath = PathEx.Combine(dirPath, Guid.NewGuid() + ".tmp"); - - return new TempFile(filePath); - } -} diff --git a/DiscordChatExporter.Cli.Tests/xunit.runner.json b/DiscordChatExporter.Cli.Tests/xunit.runner.json index 186540e3..5ab050cf 100644 --- a/DiscordChatExporter.Cli.Tests/xunit.runner.json +++ b/DiscordChatExporter.Cli.Tests/xunit.runner.json @@ -1,4 +1,4 @@ -{ +{ "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", "methodDisplayOptions": "all", "methodDisplay": "method" diff --git a/DiscordChatExporter.Cli.dockerfile b/DiscordChatExporter.Cli.dockerfile index 9bd0a266..8f2fb7fd 100644 --- a/DiscordChatExporter.Cli.dockerfile +++ b/DiscordChatExporter.Cli.dockerfile @@ -1,7 +1,7 @@ # -- Build # Specify the platform here so that we pull the SDK image matching the host platform, # instead of the target platform specified during build by the `--platform` option. -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build # Expose the target architecture set by the `docker build --platform` option, so that # we can build the assembly for the correct platform. @@ -15,6 +15,7 @@ WORKDIR /tmp/app COPY favicon.ico . COPY NuGet.config . COPY Directory.Build.props . +COPY Directory.Packages.props . COPY DiscordChatExporter.Core DiscordChatExporter.Core COPY DiscordChatExporter.Cli DiscordChatExporter.Cli @@ -30,7 +31,7 @@ RUN dotnet publish DiscordChatExporter.Cli \ # -- Run # Use `runtime-deps` instead of `runtime` because we have a self-contained assembly -FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/runtime-deps:8.0-alpine AS run +FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine AS run LABEL org.opencontainers.image.title="DiscordChatExporter.Cli" LABEL org.opencontainers.image.description="DiscordChatExporter is an application that can be used to export message history from any Discord channel to a file." @@ -40,20 +41,24 @@ LABEL org.opencontainers.image.licenses="MIT" # Alpine image doesn't come with the ICU libraries pre-installed, so we need to install them manually. # We need the full ICU data because we allow the user to specify any locale for formatting purposes. -RUN apk add --no-cache icu-libs -RUN apk add --no-cache icu-data-full +RUN apk add --no-cache icu-libs icu-data-full ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false ENV LC_ALL=en_US.UTF-8 ENV LANG=en_US.UTF-8 +# Alpine is missing tzdata, which we need to support timezones +RUN apk add --no-cache tzdata + # Use a non-root user to ensure that the files shared with the host are accessible by the host user # https://github.com/Tyrrrz/DiscordChatExporter/issues/851 -RUN adduser --disabled-password --no-create-home dce -USER dce +# https://github.com/Tyrrrz/DiscordChatExporter/issues/1174 +RUN apk add --no-cache su-exec +RUN addgroup -S -g 1000 dce && adduser -S -H -G dce -u 1000 dce # This directory is exposed to the user for mounting purposes, so it's important that it always # stays the same for backwards compatibility. WORKDIR /out COPY --from=build /tmp/app/DiscordChatExporter.Cli/bin/publish /opt/app -ENTRYPOINT ["/opt/app/DiscordChatExporter.Cli"] \ No newline at end of file +COPY docker-entrypoint.sh /opt/app +ENTRYPOINT ["/opt/app/docker-entrypoint.sh"] diff --git a/DiscordChatExporter.Cli/Commands/Base/DiscordCommandBase.cs b/DiscordChatExporter.Cli/Commands/Base/DiscordCommandBase.cs index 760c0932..b7546a41 100644 --- a/DiscordChatExporter.Cli/Commands/Base/DiscordCommandBase.cs +++ b/DiscordChatExporter.Cli/Commands/Base/DiscordCommandBase.cs @@ -1,7 +1,8 @@ -using System; +using System; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using CliFx; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Utils; @@ -16,19 +17,29 @@ public abstract class DiscordCommandBase : ICommand EnvironmentVariable = "DISCORD_TOKEN", Description = "Authentication token." )] - public required string Token { get; init; } + public required string Token { get; set; } - [Obsolete("This option doesn't do anything. Kept for backwards compatibility.")] [CommandOption( "bot", 'b', EnvironmentVariable = "DISCORD_TOKEN_BOT", Description = "This option doesn't do anything. Kept for backwards compatibility." )] - public bool IsBotToken { get; init; } = false; + public bool IsBotToken { get; set; } = false; - private DiscordClient? _discordClient; - protected DiscordClient Discord => _discordClient ??= new DiscordClient(Token); + [CommandOption( + "respect-rate-limits", + Description = "Whether to respect advisory rate limits. " + + "If disabled, only hard rate limits (i.e. 429 responses) will be respected." + )] + public bool ShouldRespectRateLimits { get; set; } = true; + + [field: AllowNull, MaybeNull] + protected DiscordClient Discord => + field ??= new DiscordClient( + Token, + ShouldRespectRateLimits ? RateLimitPreference.RespectAll : RateLimitPreference.IgnoreAll + ); public virtual ValueTask ExecuteAsync(IConsole console) { diff --git a/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs b/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs index e494583b..9476acbb 100644 --- a/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs +++ b/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs @@ -1,13 +1,15 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Threading.Tasks; -using CliFx.Attributes; -using CliFx.Exceptions; +using CliFx; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands.Converters; +using DiscordChatExporter.Cli.Commands.Shared; using DiscordChatExporter.Cli.Utils.Extensions; using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Discord.Data; @@ -15,7 +17,6 @@ using DiscordChatExporter.Core.Exceptions; using DiscordChatExporter.Core.Exporting; using DiscordChatExporter.Core.Exporting.Filtering; using DiscordChatExporter.Core.Exporting.Partitioning; -using DiscordChatExporter.Core.Utils.Extensions; using Gress; using Spectre.Console; @@ -23,8 +24,6 @@ namespace DiscordChatExporter.Cli.Commands.Base; public abstract class ExportCommandBase : DiscordCommandBase { - private readonly string _outputPath = Directory.GetCurrentDirectory(); - [CommandOption( "output", 'o', @@ -35,26 +34,26 @@ public abstract class ExportCommandBase : DiscordCommandBase )] public string OutputPath { - get => _outputPath; + get; // Handle ~/ in paths on Unix systems // https://github.com/Tyrrrz/DiscordChatExporter/pull/903 - init => _outputPath = Path.GetFullPath(value); - } + set => field = Path.GetFullPath(value); + } = Directory.GetCurrentDirectory(); [CommandOption("format", 'f', Description = "Export format.")] - public ExportFormat ExportFormat { get; init; } = ExportFormat.HtmlDark; + public ExportFormat ExportFormat { get; set; } = ExportFormat.HtmlDark; [CommandOption( "after", Description = "Only include messages sent after this date or message ID." )] - public Snowflake? After { get; init; } + public Snowflake? After { get; set; } [CommandOption( "before", Description = "Only include messages sent before this date or message ID." )] - public Snowflake? Before { get; init; } + public Snowflake? Before { get; set; } [CommandOption( "partition", @@ -62,40 +61,51 @@ public abstract class ExportCommandBase : DiscordCommandBase Description = "Split the output into partitions, each limited to the specified " + "number of messages (e.g. '100') or file size (e.g. '10mb')." )] - public PartitionLimit PartitionLimit { get; init; } = PartitionLimit.Null; + public PartitionLimit PartitionLimit { get; set; } = PartitionLimit.Null; + + [CommandOption( + "include-threads", + Description = "Which types of threads should be included.", + Converter = typeof(ThreadInclusionModeInputConverter) + )] + public ThreadInclusionMode ThreadInclusionMode { get; set; } = ThreadInclusionMode.None; [CommandOption( "filter", Description = "Only include messages that satisfy this filter. " + "See the documentation for more info." )] - public MessageFilter MessageFilter { get; init; } = MessageFilter.Null; + public MessageFilter MessageFilter { get; set; } = MessageFilter.Null; [CommandOption( "parallel", Description = "Limits how many channels can be exported in parallel." )] - public int ParallelLimit { get; init; } = 1; + public int ParallelLimit { get; set; } = 1; + + [CommandOption( + "reverse", + Description = "Export messages in reverse chronological order (newest first)." + )] + public bool IsReverseMessageOrder { get; set; } [CommandOption( "markdown", Description = "Process markdown, mentions, and other special tokens." )] - public bool ShouldFormatMarkdown { get; init; } = true; + public bool ShouldFormatMarkdown { get; set; } = true; [CommandOption( "media", Description = "Download assets referenced by the export (user avatars, attached files, embedded images, etc.)." )] - public bool ShouldDownloadAssets { get; init; } + public bool ShouldDownloadAssets { get; set; } [CommandOption( "reuse-media", Description = "Reuse previously downloaded assets to avoid redundant requests." )] - public bool ShouldReuseAssets { get; init; } = false; - - private readonly string? _assetsDirPath; + public bool ShouldReuseAssets { get; set; } = false; [CommandOption( "media-dir", @@ -104,43 +114,44 @@ public abstract class ExportCommandBase : DiscordCommandBase )] public string? AssetsDirPath { - get => _assetsDirPath; + get; // Handle ~/ in paths on Unix systems // https://github.com/Tyrrrz/DiscordChatExporter/pull/903 - init => _assetsDirPath = value is not null ? Path.GetFullPath(value) : null; + set => field = value is not null ? Path.GetFullPath(value) : null; } - [Obsolete("This option doesn't do anything. Kept for backwards compatibility.")] [CommandOption( "dateformat", Description = "This option doesn't do anything. Kept for backwards compatibility." )] - public string DateFormat { get; init; } = "MM/dd/yyyy h:mm tt"; + public string DateFormat { get; set; } = "MM/dd/yyyy h:mm tt"; [CommandOption( "locale", Description = "Locale to use when formatting dates and numbers. " + "If not specified, the default system locale will be used." )] - public string? Locale { get; init; } + public string? Locale { get; set; } [CommandOption("utc", Description = "Normalize all timestamps to UTC+0.")] - public bool IsUtcNormalizationEnabled { get; init; } = false; + public bool IsUtcNormalizationEnabled { get; set; } = false; [CommandOption( "fuck-russia", EnvironmentVariable = "FUCK_RUSSIA", Description = "Don't print the Support Ukraine message to the console.", // Use a converter to accept '1' as 'true' to reuse the existing environment variable - Converter = typeof(TruthyBooleanBindingConverter) + Converter = typeof(TruthyBooleanInputConverter) )] - public bool IsUkraineSupportMessageDisabled { get; init; } = false; + public bool IsUkraineSupportMessageDisabled { get; set; } = false; - private ChannelExporter? _channelExporter; - protected ChannelExporter Exporter => _channelExporter ??= new ChannelExporter(Discord); + [field: AllowNull, MaybeNull] + protected ChannelExporter Exporter => field ??= new ChannelExporter(Discord); protected async ValueTask ExportAsync(IConsole console, IReadOnlyList channels) { + var cancellationToken = console.RegisterCancellationHandler(); + // Asset reuse can only be enabled if the download assets option is set // https://github.com/Tyrrrz/DiscordChatExporter/issues/425 if (ShouldReuseAssets && !ShouldDownloadAssets) @@ -154,13 +165,53 @@ public abstract class ExportCommandBase : DiscordCommandBase throw new CommandException("Option --media-dir cannot be used without --media."); } + var unwrappedChannels = new List(channels); + + // Unwrap threads + if (ThreadInclusionMode != ThreadInclusionMode.None) + { + await console.Output.WriteLineAsync("Fetching threads..."); + + var fetchedThreadsCount = 0; + await console + .CreateStatusTicker() + .StartAsync( + "...", + async ctx => + { + await foreach ( + var thread in Discord.GetChannelThreadsAsync( + channels, + ThreadInclusionMode == ThreadInclusionMode.All, + Before, + After, + cancellationToken + ) + ) + { + unwrappedChannels.Add(thread); + + ctx.Status(Markup.Escape($"Fetched '{thread.GetHierarchicalName()}'.")); + + fetchedThreadsCount++; + } + } + ); + + // Remove forums, as they cannot be exported directly and their constituent threads + // have already been fetched. + unwrappedChannels.RemoveAll(channel => channel.Kind == ChannelKind.GuildForum); + + await console.Output.WriteLineAsync($"Fetched {fetchedThreadsCount} thread(s)."); + } + // Make sure the user does not try to export multiple channels into one file. // Output path must either be a directory or contain template tokens for this to work. // https://github.com/Tyrrrz/DiscordChatExporter/issues/799 // https://github.com/Tyrrrz/DiscordChatExporter/issues/917 var isValidOutputPath = // Anything is valid when exporting a single channel - channels.Count <= 1 + unwrappedChannels.Count <= 1 // When using template tokens, assume the user knows what they're doing || OutputPath.Contains('%') // Otherwise, require an existing directory or an unambiguous directory path @@ -177,10 +228,10 @@ public abstract class ExportCommandBase : DiscordCommandBase } // Export - var cancellationToken = console.RegisterCancellationHandler(); var errorsByChannel = new ConcurrentDictionary(); + var warningsByChannel = new ConcurrentDictionary(); - await console.Output.WriteLineAsync($"Exporting {channels.Count} channel(s)..."); + await console.Output.WriteLineAsync($"Exporting {unwrappedChannels.Count} channel(s)..."); await console .CreateProgressTicker() .HideCompleted( @@ -192,11 +243,11 @@ public abstract class ExportCommandBase : DiscordCommandBase .StartAsync(async ctx => { await Parallel.ForEachAsync( - channels, + unwrappedChannels, new ParallelOptions { MaxDegreeOfParallelism = Math.Max(1, ParallelLimit), - CancellationToken = cancellationToken + CancellationToken = cancellationToken, }, async (channel, innerCancellationToken) => { @@ -221,6 +272,7 @@ public abstract class ExportCommandBase : DiscordCommandBase Before, PartitionLimit, MessageFilter, + IsReverseMessageOrder, ShouldFormatMarkdown, ShouldDownloadAssets, ShouldReuseAssets, @@ -236,6 +288,10 @@ public abstract class ExportCommandBase : DiscordCommandBase } ); } + catch (ChannelEmptyException ex) + { + warningsByChannel[channel] = ex.Message; + } catch (DiscordChatExporterException ex) when (!ex.IsFatal) { errorsByChannel[channel] = ex.Message; @@ -248,10 +304,32 @@ public abstract class ExportCommandBase : DiscordCommandBase using (console.WithForegroundColor(ConsoleColor.White)) { await console.Output.WriteLineAsync( - $"Successfully exported {channels.Count - errorsByChannel.Count} channel(s)." + $"Successfully exported {unwrappedChannels.Count - errorsByChannel.Count} channel(s)." ); } + // Print warnings + if (warningsByChannel.Any()) + { + await console.Output.WriteLineAsync(); + + using (console.WithForegroundColor(ConsoleColor.Yellow)) + { + await console.Error.WriteLineAsync( + "Warnings reported for the following channel(s):" + ); + } + + foreach (var (channel, message) in warningsByChannel) + { + await console.Error.WriteAsync($"{channel.GetHierarchicalName()}: "); + using (console.WithForegroundColor(ConsoleColor.Yellow)) + await console.Error.WriteLineAsync(message); + } + + await console.Error.WriteLineAsync(); + } + // Print errors if (errorsByChannel.Any()) { @@ -259,16 +337,14 @@ public abstract class ExportCommandBase : DiscordCommandBase using (console.WithForegroundColor(ConsoleColor.Red)) { - await console.Error.WriteLineAsync( - $"Failed to export {errorsByChannel.Count} the following channel(s):" - ); + await console.Error.WriteLineAsync("Failed to export the following channel(s):"); } - foreach (var (channel, error) in errorsByChannel) + foreach (var (channel, message) in errorsByChannel) { await console.Error.WriteAsync($"{channel.GetHierarchicalName()}: "); using (console.WithForegroundColor(ConsoleColor.Red)) - await console.Error.WriteLineAsync(error); + await console.Error.WriteLineAsync(message); } await console.Error.WriteLineAsync(); @@ -276,48 +352,10 @@ public abstract class ExportCommandBase : DiscordCommandBase // Fail the command only if ALL channels failed to export. // If only some channels failed to export, it's okay. - if (errorsByChannel.Count >= channels.Count) + if (errorsByChannel.Count >= unwrappedChannels.Count) throw new CommandException("Export failed."); } - protected async ValueTask ExportAsync(IConsole console, IReadOnlyList channelIds) - { - var cancellationToken = console.RegisterCancellationHandler(); - - await console.Output.WriteLineAsync("Resolving channel(s)..."); - - var channels = new List(); - var channelsByGuild = new Dictionary>(); - - foreach (var channelId in channelIds) - { - var channel = await Discord.GetChannelAsync(channelId, cancellationToken); - - // Unwrap categories - if (channel.IsCategory) - { - var guildChannels = - channelsByGuild.GetValueOrDefault(channel.GuildId) - ?? await Discord.GetGuildChannelsAsync(channel.GuildId, cancellationToken); - - foreach (var guildChannel in guildChannels) - { - if (guildChannel.Parent?.Id == channel.Id) - channels.Add(guildChannel); - } - - // Cache the guild channels to avoid redundant work - channelsByGuild[channel.GuildId] = guildChannels; - } - else - { - channels.Add(channel); - } - } - - await ExportAsync(console, channels); - } - public override async ValueTask ExecuteAsync(IConsole console) { // Support Ukraine callout diff --git a/DiscordChatExporter.Cli/Commands/Converters/ThreadInclusionModeBindingConverter.cs b/DiscordChatExporter.Cli/Commands/Converters/ThreadInclusionModeInputConverter.cs similarity index 84% rename from DiscordChatExporter.Cli/Commands/Converters/ThreadInclusionModeBindingConverter.cs rename to DiscordChatExporter.Cli/Commands/Converters/ThreadInclusionModeInputConverter.cs index 7857024f..68410b52 100644 --- a/DiscordChatExporter.Cli/Commands/Converters/ThreadInclusionModeBindingConverter.cs +++ b/DiscordChatExporter.Cli/Commands/Converters/ThreadInclusionModeInputConverter.cs @@ -1,10 +1,10 @@ -using System; -using CliFx.Extensibility; +using System; +using CliFx.Activation; using DiscordChatExporter.Cli.Commands.Shared; namespace DiscordChatExporter.Cli.Commands.Converters; -internal class ThreadInclusionModeBindingConverter : BindingConverter +internal class ThreadInclusionModeInputConverter : ScalarInputConverter { public override ThreadInclusionMode Convert(string? rawValue) { diff --git a/DiscordChatExporter.Cli/Commands/Converters/TruthyBooleanBindingConverter.cs b/DiscordChatExporter.Cli/Commands/Converters/TruthyBooleanInputConverter.cs similarity index 82% rename from DiscordChatExporter.Cli/Commands/Converters/TruthyBooleanBindingConverter.cs rename to DiscordChatExporter.Cli/Commands/Converters/TruthyBooleanInputConverter.cs index 5f4abd20..9823e75d 100644 --- a/DiscordChatExporter.Cli/Commands/Converters/TruthyBooleanBindingConverter.cs +++ b/DiscordChatExporter.Cli/Commands/Converters/TruthyBooleanInputConverter.cs @@ -1,9 +1,9 @@ -using System.Globalization; -using CliFx.Extensibility; +using System.Globalization; +using CliFx.Activation; namespace DiscordChatExporter.Cli.Commands.Converters; -internal class TruthyBooleanBindingConverter : BindingConverter +internal class TruthyBooleanInputConverter : ScalarInputConverter { public override bool Convert(string? rawValue) { diff --git a/DiscordChatExporter.Cli/Commands/ExportAllCommand.cs b/DiscordChatExporter.Cli/Commands/ExportAllCommand.cs index 1ec5e289..464ecf0a 100644 --- a/DiscordChatExporter.Cli/Commands/ExportAllCommand.cs +++ b/DiscordChatExporter.Cli/Commands/ExportAllCommand.cs @@ -2,11 +2,9 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands.Base; -using DiscordChatExporter.Cli.Commands.Converters; -using DiscordChatExporter.Cli.Commands.Shared; using DiscordChatExporter.Cli.Utils.Extensions; using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Discord.Dump; @@ -16,30 +14,23 @@ using Spectre.Console; namespace DiscordChatExporter.Cli.Commands; [Command("exportall", Description = "Exports all accessible channels.")] -public class ExportAllCommand : ExportCommandBase +public partial class ExportAllCommand : ExportCommandBase { [CommandOption("include-dm", Description = "Include direct message channels.")] - public bool IncludeDirectChannels { get; init; } = true; + public bool IncludeDirectChannels { get; set; } = true; [CommandOption("include-guilds", Description = "Include server channels.")] - public bool IncludeGuildChannels { get; init; } = true; + public bool IncludeGuildChannels { get; set; } = true; [CommandOption("include-vc", Description = "Include voice channels.")] - public bool IncludeVoiceChannels { get; init; } = true; - - [CommandOption( - "include-threads", - Description = "Which types of threads should be included.", - Converter = typeof(ThreadInclusionModeBindingConverter) - )] - public ThreadInclusionMode ThreadInclusionMode { get; init; } = ThreadInclusionMode.None; + public bool IncludeVoiceChannels { get; set; } = true; [CommandOption( "data-package", Description = "Path to the personal data package (ZIP file) requested from Discord. " + "If provided, only channels referenced in the dump will be exported." )] - public string? DataPackageFilePath { get; init; } + public string? DataPackageFilePath { get; set; } public override async ValueTask ExecuteAsync(IConsole console) { @@ -90,46 +81,6 @@ public class ExportAllCommand : ExportCommandBase ); await console.Output.WriteLineAsync($"Fetched {fetchedChannelsCount} channel(s)."); - - // Threads - if (ThreadInclusionMode != ThreadInclusionMode.None) - { - await console.Output.WriteLineAsync( - $"Fetching threads for server '{guild.Name}'..." - ); - - var fetchedThreadsCount = 0; - await console - .CreateStatusTicker() - .StartAsync( - "...", - async ctx => - { - await foreach ( - var thread in Discord.GetGuildThreadsAsync( - guild.Id, - ThreadInclusionMode == ThreadInclusionMode.All, - Before, - After, - cancellationToken - ) - ) - { - channels.Add(thread); - - ctx.Status( - Markup.Escape($"Fetched '{thread.GetHierarchicalName()}'.") - ); - - fetchedThreadsCount++; - } - } - ); - - await console.Output.WriteLineAsync( - $"Fetched {fetchedThreadsCount} thread(s)." - ); - } } } // Pull from the data package @@ -199,10 +150,6 @@ public class ExportAllCommand : ExportCommandBase channels.RemoveAll(c => c.IsGuild); if (!IncludeVoiceChannels) channels.RemoveAll(c => c.IsVoice); - if (ThreadInclusionMode == ThreadInclusionMode.None) - channels.RemoveAll(c => c.IsThread); - if (ThreadInclusionMode != ThreadInclusionMode.All) - channels.RemoveAll(c => c is { IsThread: true, IsArchived: true }); await ExportAsync(console, channels); } diff --git a/DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs b/DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs index 79566c06..5a3ae6d3 100644 --- a/DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs +++ b/DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs @@ -1,14 +1,16 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands.Base; using DiscordChatExporter.Core.Discord; +using DiscordChatExporter.Core.Discord.Data; +using PowerKit.Extensions; namespace DiscordChatExporter.Cli.Commands; [Command("export", Description = "Exports one or multiple channels.")] -public class ExportChannelsCommand : ExportCommandBase +public partial class ExportChannelsCommand : ExportCommandBase { // TODO: change this to plural (breaking change) [CommandOption( @@ -17,11 +19,45 @@ public class ExportChannelsCommand : ExportCommandBase Description = "Channel ID(s). " + "If provided with category ID(s), all channels inside those categories will be exported." )] - public required IReadOnlyList ChannelIds { get; init; } + public required IReadOnlyList ChannelIds { get; set; } public override async ValueTask ExecuteAsync(IConsole console) { await base.ExecuteAsync(console); - await ExportAsync(console, ChannelIds); + + var cancellationToken = console.RegisterCancellationHandler(); + + await console.Output.WriteLineAsync("Resolving channel(s)..."); + + var channels = new List(); + var channelsByGuild = new Dictionary>(); + + foreach (var channelId in ChannelIds) + { + var channel = await Discord.GetChannelAsync(channelId, cancellationToken); + + // Unwrap categories + if (channel.IsCategory) + { + var guildChannels = + channelsByGuild.GetValueOrDefault(channel.GuildId) + ?? await Discord.GetGuildChannelsAsync(channel.GuildId, cancellationToken); + + foreach (var guildChannel in guildChannels) + { + if (guildChannel.Parent?.Id == channel.Id) + channels.Add(guildChannel); + } + + // Cache the guild channels to avoid redundant work + channelsByGuild[channel.GuildId] = guildChannels; + } + else + { + channels.Add(channel); + } + } + + await ExportAsync(console, channels); } } diff --git a/DiscordChatExporter.Cli/Commands/ExportDirectMessagesCommand.cs b/DiscordChatExporter.Cli/Commands/ExportDirectMessagesCommand.cs index 69c80f79..52af252e 100644 --- a/DiscordChatExporter.Cli/Commands/ExportDirectMessagesCommand.cs +++ b/DiscordChatExporter.Cli/Commands/ExportDirectMessagesCommand.cs @@ -1,14 +1,14 @@ -using System.Threading.Tasks; -using CliFx.Attributes; +using System.Threading.Tasks; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands.Base; using DiscordChatExporter.Core.Discord.Data; -using DiscordChatExporter.Core.Utils.Extensions; +using PowerKit.Extensions; namespace DiscordChatExporter.Cli.Commands; [Command("exportdm", Description = "Exports all direct message channels.")] -public class ExportDirectMessagesCommand : ExportCommandBase +public partial class ExportDirectMessagesCommand : ExportCommandBase { public override async ValueTask ExecuteAsync(IConsole console) { diff --git a/DiscordChatExporter.Cli/Commands/ExportGuildCommand.cs b/DiscordChatExporter.Cli/Commands/ExportGuildCommand.cs index 71b66467..00d27d33 100644 --- a/DiscordChatExporter.Cli/Commands/ExportGuildCommand.cs +++ b/DiscordChatExporter.Cli/Commands/ExportGuildCommand.cs @@ -1,10 +1,8 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands.Base; -using DiscordChatExporter.Cli.Commands.Converters; -using DiscordChatExporter.Cli.Commands.Shared; using DiscordChatExporter.Cli.Utils.Extensions; using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Discord.Data; @@ -13,20 +11,13 @@ using Spectre.Console; namespace DiscordChatExporter.Cli.Commands; [Command("exportguild", Description = "Exports all channels within the specified server.")] -public class ExportGuildCommand : ExportCommandBase +public partial class ExportGuildCommand : ExportCommandBase { [CommandOption("guild", 'g', Description = "Server ID.")] - public required Snowflake GuildId { get; init; } + public required Snowflake GuildId { get; set; } [CommandOption("include-vc", Description = "Include voice channels.")] - public bool IncludeVoiceChannels { get; init; } = true; - - [CommandOption( - "include-threads", - Description = "Which types of threads should be included.", - Converter = typeof(ThreadInclusionModeBindingConverter) - )] - public ThreadInclusionMode ThreadInclusionMode { get; init; } = ThreadInclusionMode.None; + public bool IncludeVoiceChannels { get; set; } = true; public override async ValueTask ExecuteAsync(IConsole console) { @@ -35,7 +26,6 @@ public class ExportGuildCommand : ExportCommandBase var cancellationToken = console.RegisterCancellationHandler(); var channels = new List(); - // Regular channels await console.Output.WriteLineAsync("Fetching channels..."); var fetchedChannelsCount = 0; @@ -66,40 +56,6 @@ public class ExportGuildCommand : ExportCommandBase await console.Output.WriteLineAsync($"Fetched {fetchedChannelsCount} channel(s)."); - // Threads - if (ThreadInclusionMode != ThreadInclusionMode.None) - { - await console.Output.WriteLineAsync("Fetching threads..."); - - var fetchedThreadsCount = 0; - await console - .CreateStatusTicker() - .StartAsync( - "...", - async ctx => - { - await foreach ( - var thread in Discord.GetGuildThreadsAsync( - GuildId, - ThreadInclusionMode == ThreadInclusionMode.All, - Before, - After, - cancellationToken - ) - ) - { - channels.Add(thread); - - ctx.Status(Markup.Escape($"Fetched '{thread.GetHierarchicalName()}'.")); - - fetchedThreadsCount++; - } - } - ); - - await console.Output.WriteLineAsync($"Fetched {fetchedThreadsCount} thread(s)."); - } - await ExportAsync(console, channels); } } diff --git a/DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs b/DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs index a430cac1..f033befa 100644 --- a/DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs +++ b/DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs @@ -1,32 +1,31 @@ -using System; +using System; using System.Linq; using System.Threading.Tasks; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands.Base; using DiscordChatExporter.Cli.Commands.Converters; using DiscordChatExporter.Cli.Commands.Shared; using DiscordChatExporter.Core.Discord; -using DiscordChatExporter.Core.Discord.Data; -using DiscordChatExporter.Core.Utils.Extensions; +using PowerKit.Extensions; namespace DiscordChatExporter.Cli.Commands; [Command("channels", Description = "Get the list of channels in a server.")] -public class GetChannelsCommand : DiscordCommandBase +public partial class GetChannelsCommand : DiscordCommandBase { [CommandOption("guild", 'g', Description = "Server ID.")] - public required Snowflake GuildId { get; init; } + public required Snowflake GuildId { get; set; } [CommandOption("include-vc", Description = "Include voice channels.")] - public bool IncludeVoiceChannels { get; init; } = true; + public bool IncludeVoiceChannels { get; set; } = true; [CommandOption( "include-threads", Description = "Which types of threads should be included.", - Converter = typeof(ThreadInclusionModeBindingConverter) + Converter = typeof(ThreadInclusionModeInputConverter) )] - public ThreadInclusionMode ThreadInclusionMode { get; init; } = ThreadInclusionMode.None; + public ThreadInclusionMode ThreadInclusionMode { get; set; } = ThreadInclusionMode.None; public override async ValueTask ExecuteAsync(IConsole console) { @@ -59,7 +58,7 @@ public class GetChannelsCommand : DiscordCommandBase ) .OrderBy(c => c.Name) .ToArray() - : Array.Empty(); + : []; foreach (var channel in channels) { diff --git a/DiscordChatExporter.Cli/Commands/GetDirectChannelsCommand.cs b/DiscordChatExporter.Cli/Commands/GetDirectChannelsCommand.cs index daedf096..b91b466e 100644 --- a/DiscordChatExporter.Cli/Commands/GetDirectChannelsCommand.cs +++ b/DiscordChatExporter.Cli/Commands/GetDirectChannelsCommand.cs @@ -1,16 +1,16 @@ -using System; +using System; using System.Linq; using System.Threading.Tasks; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands.Base; using DiscordChatExporter.Core.Discord.Data; -using DiscordChatExporter.Core.Utils.Extensions; +using PowerKit.Extensions; namespace DiscordChatExporter.Cli.Commands; [Command("dm", Description = "Gets the list of all direct message channels.")] -public class GetDirectChannelsCommand : DiscordCommandBase +public partial class GetDirectChannelsCommand : DiscordCommandBase { public override async ValueTask ExecuteAsync(IConsole console) { diff --git a/DiscordChatExporter.Cli/Commands/GetGuildsCommand.cs b/DiscordChatExporter.Cli/Commands/GetGuildsCommand.cs index 42642c41..8847298e 100644 --- a/DiscordChatExporter.Cli/Commands/GetGuildsCommand.cs +++ b/DiscordChatExporter.Cli/Commands/GetGuildsCommand.cs @@ -1,16 +1,16 @@ -using System; +using System; using System.Linq; using System.Threading.Tasks; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; using DiscordChatExporter.Cli.Commands.Base; using DiscordChatExporter.Core.Discord.Data; -using DiscordChatExporter.Core.Utils.Extensions; +using PowerKit.Extensions; namespace DiscordChatExporter.Cli.Commands; [Command("guilds", Description = "Gets the list of accessible servers.")] -public class GetGuildsCommand : DiscordCommandBase +public partial class GetGuildsCommand : DiscordCommandBase { public override async ValueTask ExecuteAsync(IConsole console) { diff --git a/DiscordChatExporter.Cli/Commands/GuideCommand.cs b/DiscordChatExporter.Cli/Commands/GuideCommand.cs index c643cb11..c0270c5d 100644 --- a/DiscordChatExporter.Cli/Commands/GuideCommand.cs +++ b/DiscordChatExporter.Cli/Commands/GuideCommand.cs @@ -1,13 +1,13 @@ using System; using System.Threading.Tasks; using CliFx; -using CliFx.Attributes; +using CliFx.Binding; using CliFx.Infrastructure; namespace DiscordChatExporter.Cli.Commands; [Command("guide", Description = "Explains how to obtain the token, server or channel ID.")] -public class GuideCommand : ICommand +public partial class GuideCommand : ICommand { public ValueTask ExecuteAsync(IConsole console) { @@ -34,10 +34,17 @@ public class GuideCommand : ICommand using (console.WithForegroundColor(ConsoleColor.White)) console.Output.WriteLine("To get the token for your bot:"); + console.Output.WriteLine( + " The token is generated during bot creation. If you lost it, generate a new one:" + ); console.Output.WriteLine(" 1. Go to Discord developer portal"); console.Output.WriteLine(" 2. Open your application's settings"); console.Output.WriteLine(" 3. Navigate to the Bot section on the left"); - console.Output.WriteLine(" 4. Under Token click Copy"); + console.Output.WriteLine(" 4. Under Token click Reset Token"); + console.Output.WriteLine(" 5. Click Yes, do it! and authenticate to confirm"); + console.Output.WriteLine( + " * Integrations using the previous token will stop working until updated" + ); console.Output.WriteLine( " * Your bot needs to have the Message Content Intent enabled to read messages" ); @@ -67,7 +74,7 @@ public class GuideCommand : ICommand using (console.WithForegroundColor(ConsoleColor.DarkCyan)) { console.Output.WriteLine( - "https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs" + "https://github.com/Tyrrrz/DiscordChatExporter/blob/prime/.docs" ); } diff --git a/DiscordChatExporter.Cli/Commands/Shared/ThreadInclusionMode.cs b/DiscordChatExporter.Cli/Commands/Shared/ThreadInclusionMode.cs index 878cdff6..f5290514 100644 --- a/DiscordChatExporter.Cli/Commands/Shared/ThreadInclusionMode.cs +++ b/DiscordChatExporter.Cli/Commands/Shared/ThreadInclusionMode.cs @@ -1,8 +1,8 @@ -namespace DiscordChatExporter.Cli.Commands.Shared; +namespace DiscordChatExporter.Cli.Commands.Shared; public enum ThreadInclusionMode { None, Active, - All + All, } diff --git a/DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj b/DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj index 4d012b99..8e92e459 100644 --- a/DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj +++ b/DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj @@ -1,21 +1,21 @@ - - + Exe - ..\favicon.ico + ../favicon.ico + true + false - - - - - - + + + + + + - + - - \ No newline at end of file + diff --git a/DiscordChatExporter.Cli/Program.cs b/DiscordChatExporter.Cli/Program.cs index d0d8ba9b..695b517b 100644 --- a/DiscordChatExporter.Cli/Program.cs +++ b/DiscordChatExporter.Cli/Program.cs @@ -1,3 +1,13 @@ -using CliFx; +using System.Threading.Tasks; +using CliFx; -return await new CliApplicationBuilder().AddCommandsFromThisAssembly().Build().RunAsync(args); +namespace DiscordChatExporter.Cli; + +public static class Program +{ + public static async Task Main(string[] args) => + await new CommandLineApplicationBuilder() + .AddCommandsFromThisAssembly() + .Build() + .RunAsync(args); +} diff --git a/DiscordChatExporter.Cli/Utils/Extensions/ConsoleExtensions.cs b/DiscordChatExporter.Cli/Utils/Extensions/ConsoleExtensions.cs index 239de801..d97f5641 100644 --- a/DiscordChatExporter.Cli/Utils/Extensions/ConsoleExtensions.cs +++ b/DiscordChatExporter.Cli/Utils/Extensions/ConsoleExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using CliFx.Infrastructure; using Spectre.Console; @@ -7,32 +7,35 @@ namespace DiscordChatExporter.Cli.Utils.Extensions; internal static class ConsoleExtensions { - public static IAnsiConsole CreateAnsiConsole(this IConsole console) => - AnsiConsole.Create( - new AnsiConsoleSettings - { - Ansi = AnsiSupport.Detect, - ColorSystem = ColorSystemSupport.Detect, - Out = new AnsiConsoleOutput(console.Output) - } - ); - - public static Status CreateStatusTicker(this IConsole console) => - console.CreateAnsiConsole().Status().AutoRefresh(true); - - public static Progress CreateProgressTicker(this IConsole console) => - console - .CreateAnsiConsole() - .Progress() - .AutoClear(false) - .AutoRefresh(true) - .HideCompleted(false) - .Columns( - new TaskDescriptionColumn { Alignment = Justify.Left }, - new ProgressBarColumn(), - new PercentageColumn() + extension(IConsole console) + { + public IAnsiConsole CreateAnsiConsole() => + AnsiConsole.Create( + new AnsiConsoleSettings + { + Ansi = AnsiSupport.Detect, + ColorSystem = ColorSystemSupport.Detect, + Out = new AnsiConsoleOutput(console.Output), + } ); + public Status CreateStatusTicker() => + console.CreateAnsiConsole().Status().AutoRefresh(true); + + public Progress CreateProgressTicker() => + console + .CreateAnsiConsole() + .Progress() + .AutoClear(false) + .AutoRefresh(true) + .HideCompleted(false) + .Columns( + new TaskDescriptionColumn { Alignment = Justify.Left }, + new ProgressBarColumn(), + new PercentageColumn() + ); + } + public static async ValueTask StartTaskAsync( this ProgressContext context, string description, diff --git a/DiscordChatExporter.Core/Discord/Data/Application.cs b/DiscordChatExporter.Core/Discord/Data/Application.cs index 230f8e21..b1d4437b 100644 --- a/DiscordChatExporter.Core/Discord/Data/Application.cs +++ b/DiscordChatExporter.Core/Discord/Data/Application.cs @@ -1,13 +1,13 @@ -using System.Text.Json; -using DiscordChatExporter.Core.Utils.Extensions; +using System.Text.Json; using JsonExtensions.Reading; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Discord.Data; // https://discord.com/developers/docs/resources/application#application-object public partial record Application(Snowflake Id, string Name, ApplicationFlags Flags) { - public bool IsMessageContentIntentEnabled => + public bool IsMessageContentIntentEnabled { get; } = Flags.HasFlag(ApplicationFlags.GatewayMessageContent) || Flags.HasFlag(ApplicationFlags.GatewayMessageContentLimited); } diff --git a/DiscordChatExporter.Core/Discord/Data/ApplicationFlags.cs b/DiscordChatExporter.Core/Discord/Data/ApplicationFlags.cs index 850ea6fd..d8df9f66 100644 --- a/DiscordChatExporter.Core/Discord/Data/ApplicationFlags.cs +++ b/DiscordChatExporter.Core/Discord/Data/ApplicationFlags.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace DiscordChatExporter.Core.Discord.Data; @@ -16,5 +16,5 @@ public enum ApplicationFlags Embedded = 131072, GatewayMessageContent = 262144, GatewayMessageContentLimited = 524288, - ApplicationCommandBadge = 8388608 + ApplicationCommandBadge = 8388608, } diff --git a/DiscordChatExporter.Core/Discord/Data/Attachment.cs b/DiscordChatExporter.Core/Discord/Data/Attachment.cs index 79d03cf1..0bfa440d 100644 --- a/DiscordChatExporter.Core/Discord/Data/Attachment.cs +++ b/DiscordChatExporter.Core/Discord/Data/Attachment.cs @@ -1,9 +1,9 @@ -using System; +using System; using System.IO; using System.Text.Json; using DiscordChatExporter.Core.Discord.Data.Common; -using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Reading; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Discord.Data; @@ -18,7 +18,7 @@ public partial record Attachment( FileSize FileSize ) : IHasId { - public string FileExtension => Path.GetExtension(FileName); + public string FileExtension { get; } = Path.GetExtension(FileName); public bool IsImage => string.Equals(FileExtension, ".jpg", StringComparison.OrdinalIgnoreCase) @@ -41,7 +41,7 @@ public partial record Attachment( || string.Equals(FileExtension, ".flac", StringComparison.OrdinalIgnoreCase) || string.Equals(FileExtension, ".m4a", StringComparison.OrdinalIgnoreCase); - public bool IsSpoiler => FileName.StartsWith("SPOILER_", StringComparison.Ordinal); + public bool IsSpoiler { get; } = FileName.StartsWith("SPOILER_", StringComparison.Ordinal); } public partial record Attachment diff --git a/DiscordChatExporter.Core/Discord/Data/Channel.cs b/DiscordChatExporter.Core/Discord/Data/Channel.cs index 70d8f17a..41f98f2a 100644 --- a/DiscordChatExporter.Core/Discord/Data/Channel.cs +++ b/DiscordChatExporter.Core/Discord/Data/Channel.cs @@ -1,9 +1,9 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Text.Json; using DiscordChatExporter.Core.Discord.Data.Common; -using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Reading; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Discord.Data; @@ -21,21 +21,23 @@ public partial record Channel( Snowflake? LastMessageId ) : IHasId { - public bool IsDirect => Kind is ChannelKind.DirectTextChat or ChannelKind.DirectGroupTextChat; + public bool IsDirect { get; } = + Kind is ChannelKind.DirectTextChat or ChannelKind.DirectGroupTextChat; public bool IsGuild => !IsDirect; - public bool IsCategory => Kind == ChannelKind.GuildCategory; + public bool IsCategory { get; } = Kind == ChannelKind.GuildCategory; - public bool IsVoice => Kind is ChannelKind.GuildVoiceChat or ChannelKind.GuildStageVoice; + public bool IsVoice { get; } = + Kind is ChannelKind.GuildVoiceChat or ChannelKind.GuildStageVoice; - public bool IsThread => + public bool IsThread { get; } = Kind - is ChannelKind.GuildNewsThread - or ChannelKind.GuildPublicThread - or ChannelKind.GuildPrivateThread; + is ChannelKind.GuildNewsThread + or ChannelKind.GuildPublicThread + or ChannelKind.GuildPrivateThread; - public bool IsEmpty => LastMessageId is null; + public bool IsEmpty { get; } = LastMessageId is null; public IEnumerable GetParents() { @@ -67,7 +69,8 @@ public partial record Channel var guildId = json.GetPropertyOrNull("guild_id") ?.GetNonWhiteSpaceStringOrNull() - ?.Pipe(Snowflake.Parse) ?? Guild.DirectMessages.Id; + ?.Pipe(Snowflake.Parse) + ?? Guild.DirectMessages.Id; var name = // Guild channel @@ -76,6 +79,7 @@ public partial record Channel ?? json.GetPropertyOrNull("recipients") ?.EnumerateArrayOrNull() ?.Select(User.Parse) + .OrderBy(u => u.Id) .Select(u => u.DisplayName) .Pipe(s => string.Join(", ", s)) // Fallback @@ -93,7 +97,8 @@ public partial record Channel var isArchived = json.GetPropertyOrNull("thread_metadata") ?.GetPropertyOrNull("archived") - ?.GetBooleanOrNull() ?? false; + ?.GetBooleanOrNull() + ?? false; var lastMessageId = json.GetPropertyOrNull("last_message_id") ?.GetNonWhiteSpaceStringOrNull() diff --git a/DiscordChatExporter.Core/Discord/Data/ChannelConnection.cs b/DiscordChatExporter.Core/Discord/Data/ChannelConnection.cs new file mode 100644 index 00000000..fba952c8 --- /dev/null +++ b/DiscordChatExporter.Core/Discord/Data/ChannelConnection.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Linq; + +namespace DiscordChatExporter.Core.Discord.Data; + +public record ChannelConnection(Channel Channel, IReadOnlyList Children) +{ + public static IReadOnlyList BuildTree(IReadOnlyList channels) + { + IReadOnlyList GetChildren(Channel parent) => + channels + .Where(c => c.Parent?.Id == parent.Id) + .Select(c => new ChannelConnection(c, GetChildren(c))) + .ToArray(); + + return channels + .Where(c => c.Parent is null) + .Select(c => new ChannelConnection(c, GetChildren(c))) + .ToArray(); + } +} diff --git a/DiscordChatExporter.Core/Discord/Data/ChannelKind.cs b/DiscordChatExporter.Core/Discord/Data/ChannelKind.cs index 6e36b519..c66f1c40 100644 --- a/DiscordChatExporter.Core/Discord/Data/ChannelKind.cs +++ b/DiscordChatExporter.Core/Discord/Data/ChannelKind.cs @@ -1,4 +1,4 @@ -namespace DiscordChatExporter.Core.Discord.Data; +namespace DiscordChatExporter.Core.Discord.Data; // https://discord.com/developers/docs/resources/channel#channel-object-channel-types public enum ChannelKind @@ -14,5 +14,5 @@ public enum ChannelKind GuildPrivateThread = 12, GuildStageVoice = 13, GuildDirectory = 14, - GuildForum = 15 + GuildForum = 15, } diff --git a/DiscordChatExporter.Core/Discord/Data/Common/FileSize.cs b/DiscordChatExporter.Core/Discord/Data/Common/FileSize.cs index 300557a5..5cc127b2 100644 --- a/DiscordChatExporter.Core/Discord/Data/Common/FileSize.cs +++ b/DiscordChatExporter.Core/Discord/Data/Common/FileSize.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; diff --git a/DiscordChatExporter.Core/Discord/Data/Common/IHasId.cs b/DiscordChatExporter.Core/Discord/Data/Common/IHasId.cs index f5012171..2d892397 100644 --- a/DiscordChatExporter.Core/Discord/Data/Common/IHasId.cs +++ b/DiscordChatExporter.Core/Discord/Data/Common/IHasId.cs @@ -1,4 +1,4 @@ -namespace DiscordChatExporter.Core.Discord.Data.Common; +namespace DiscordChatExporter.Core.Discord.Data.Common; public interface IHasId { diff --git a/DiscordChatExporter.Core/Discord/Data/Common/ImageCdn.cs b/DiscordChatExporter.Core/Discord/Data/Common/ImageCdn.cs index 90a282fd..d854b6bc 100644 --- a/DiscordChatExporter.Core/Discord/Data/Common/ImageCdn.cs +++ b/DiscordChatExporter.Core/Discord/Data/Common/ImageCdn.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Linq; diff --git a/DiscordChatExporter.Core/Discord/Data/Embeds/Embed.cs b/DiscordChatExporter.Core/Discord/Data/Embeds/Embed.cs index 1d2d0b35..b9a29e83 100644 --- a/DiscordChatExporter.Core/Discord/Data/Embeds/Embed.cs +++ b/DiscordChatExporter.Core/Discord/Data/Embeds/Embed.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text.Json; -using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Reading; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Discord.Data.Embeds; @@ -31,6 +31,9 @@ public partial record Embed( public SpotifyTrackEmbedProjection? TryGetSpotifyTrack() => SpotifyTrackEmbedProjection.TryResolve(this); + public TwitchClipEmbedProjection? TryGetTwitchClip() => + TwitchClipEmbedProjection.TryResolve(this); + public YouTubeVideoEmbedProjection? TryGetYouTubeVideo() => YouTubeVideoEmbedProjection.TryResolve(this); } @@ -42,7 +45,9 @@ public partial record Embed var title = json.GetPropertyOrNull("title")?.GetStringOrNull(); var kind = - json.GetPropertyOrNull("type")?.GetStringOrNull()?.ParseEnumOrNull() + json.GetPropertyOrNull("type") + ?.GetStringOrNull() + .Pipe(s => Enum.ParseOrNull(s, true)) ?? EmbedKind.Rich; var url = json.GetPropertyOrNull("url")?.GetNonWhiteSpaceStringOrNull(); @@ -51,7 +56,7 @@ public partial record Embed var color = json.GetPropertyOrNull("color") ?.GetInt32OrNull() ?.Pipe(System.Drawing.Color.FromArgb) - .ResetAlpha(); + .WithFullAlpha(); var author = json.GetPropertyOrNull("author")?.Pipe(EmbedAuthor.Parse); var description = json.GetPropertyOrNull("description")?.GetStringOrNull(); @@ -60,7 +65,8 @@ public partial record Embed json.GetPropertyOrNull("fields") ?.EnumerateArrayOrNull() ?.Select(EmbedField.Parse) - .ToArray() ?? Array.Empty(); + .ToArray() + ?? []; var thumbnail = json.GetPropertyOrNull("thumbnail")?.Pipe(EmbedImage.Parse); @@ -75,7 +81,8 @@ public partial record Embed json.GetPropertyOrNull("image") ?.Pipe(EmbedImage.Parse) .ToSingletonEnumerable() - .ToArray() ?? Array.Empty(); + .ToArray() + ?? []; var video = json.GetPropertyOrNull("video")?.Pipe(EmbedVideo.Parse); diff --git a/DiscordChatExporter.Core/Discord/Data/Embeds/EmbedKind.cs b/DiscordChatExporter.Core/Discord/Data/Embeds/EmbedKind.cs index 539f862d..b1240b0f 100644 --- a/DiscordChatExporter.Core/Discord/Data/Embeds/EmbedKind.cs +++ b/DiscordChatExporter.Core/Discord/Data/Embeds/EmbedKind.cs @@ -7,5 +7,5 @@ public enum EmbedKind Image, Video, Gifv, - Link + Link, } diff --git a/DiscordChatExporter.Core/Discord/Data/Embeds/SpotifyTrackEmbedProjection.cs b/DiscordChatExporter.Core/Discord/Data/Embeds/SpotifyTrackEmbedProjection.cs index 4d68d62e..c94b8315 100644 --- a/DiscordChatExporter.Core/Discord/Data/Embeds/SpotifyTrackEmbedProjection.cs +++ b/DiscordChatExporter.Core/Discord/Data/Embeds/SpotifyTrackEmbedProjection.cs @@ -1,10 +1,10 @@ -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; namespace DiscordChatExporter.Core.Discord.Data.Embeds; public partial record SpotifyTrackEmbedProjection(string TrackId) { - public string Url => $"https://open.spotify.com/embed/track/{TrackId}"; + public string Url { get; } = $"https://open.spotify.com/embed/track/{TrackId}"; } public partial record SpotifyTrackEmbedProjection diff --git a/DiscordChatExporter.Core/Discord/Data/Embeds/TwitchClipEmbedProjection.cs b/DiscordChatExporter.Core/Discord/Data/Embeds/TwitchClipEmbedProjection.cs new file mode 100644 index 00000000..ca1e28bd --- /dev/null +++ b/DiscordChatExporter.Core/Discord/Data/Embeds/TwitchClipEmbedProjection.cs @@ -0,0 +1,53 @@ +using System.Text.RegularExpressions; + +namespace DiscordChatExporter.Core.Discord.Data.Embeds; + +public partial record TwitchClipEmbedProjection(string ClipId) +{ + public string Url { get; } = $"https://clips.twitch.tv/embed?clip={ClipId}&parent=localhost"; +} + +public partial record TwitchClipEmbedProjection +{ + private static string? TryParseClipId(string embedUrl) + { + // https://clips.twitch.tv/SpookyTenuousPidgeonPanicVis + { + var clipId = Regex + .Match(embedUrl, @"clips\.twitch\.tv/(.*?)(?:\?|&|/|$)") + .Groups[1] + .Value; + + if (!string.IsNullOrWhiteSpace(clipId)) + return clipId; + } + + // https://twitch.tv/clip/SpookyTenuousPidgeonPanicVis + { + var clipId = Regex + .Match(embedUrl, @"twitch\.tv/clip/(.*?)(?:\?|&|/|$)") + .Groups[1] + .Value; + + if (!string.IsNullOrWhiteSpace(clipId)) + return clipId; + } + + return null; + } + + public static TwitchClipEmbedProjection? TryResolve(Embed embed) + { + if (embed.Kind != EmbedKind.Video) + return null; + + if (string.IsNullOrWhiteSpace(embed.Url)) + return null; + + var clipId = TryParseClipId(embed.Url); + if (string.IsNullOrWhiteSpace(clipId)) + return null; + + return new TwitchClipEmbedProjection(clipId); + } +} diff --git a/DiscordChatExporter.Core/Discord/Data/Embeds/YouTubeVideoEmbedProjection.cs b/DiscordChatExporter.Core/Discord/Data/Embeds/YouTubeVideoEmbedProjection.cs index df02bf62..582e4011 100644 --- a/DiscordChatExporter.Core/Discord/Data/Embeds/YouTubeVideoEmbedProjection.cs +++ b/DiscordChatExporter.Core/Discord/Data/Embeds/YouTubeVideoEmbedProjection.cs @@ -1,8 +1,11 @@ -namespace DiscordChatExporter.Core.Discord.Data.Embeds; +namespace DiscordChatExporter.Core.Discord.Data.Embeds; public partial record YouTubeVideoEmbedProjection(string VideoId) { - public string Url => $"https://www.youtube.com/embed/{VideoId}"; + public string Url { get; } = $"https://www.youtube.com/watch?v={VideoId}"; + + // Using hqdefault.jpg which is guaranteed to exist for all YouTube videos + public string ThumbnailUrl { get; } = $"https://i.ytimg.com/vi/{VideoId}/hqdefault.jpg"; } public partial record YouTubeVideoEmbedProjection diff --git a/DiscordChatExporter.Core/Discord/Data/Emoji.cs b/DiscordChatExporter.Core/Discord/Data/Emoji.cs index 838a371c..1d8ae44a 100644 --- a/DiscordChatExporter.Core/Discord/Data/Emoji.cs +++ b/DiscordChatExporter.Core/Discord/Data/Emoji.cs @@ -1,9 +1,8 @@ -using System; using System.Text.Json; using DiscordChatExporter.Core.Discord.Data.Common; using DiscordChatExporter.Core.Utils; -using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Reading; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Discord.Data; @@ -13,29 +12,22 @@ public partial record Emoji( Snowflake? Id, // Name of a custom emoji (e.g. LUL) or actual representation of a standard emoji (e.g. 🙂) string Name, - bool IsAnimated, - string ImageUrl + bool IsAnimated ) { + public bool IsCustomEmoji { get; } = Id is not null; + // Name of a custom emoji (e.g. LUL) or name of a standard emoji (e.g. slight_smile) - public string Code => Id is not null ? Name : EmojiIndex.TryGetCode(Name) ?? Name; + public string Code { get; } = Id is not null ? Name : EmojiIndex.TryGetCode(Name) ?? Name; + + public string ImageUrl { get; } = + Id is not null + ? ImageCdn.GetCustomEmojiUrl(Id.Value, IsAnimated) + : ImageCdn.GetStandardEmojiUrl(Name); } public partial record Emoji { - public static string GetImageUrl(Snowflake? id, string? name, bool isAnimated) - { - // Custom emoji - if (id is not null) - return ImageCdn.GetCustomEmojiUrl(id.Value, isAnimated); - - // Standard emoji - if (!string.IsNullOrWhiteSpace(name)) - return ImageCdn.GetStandardEmojiUrl(name); - - throw new InvalidOperationException("Either the emoji ID or name should be provided."); - } - public static Emoji Parse(JsonElement json) { var id = json.GetPropertyOrNull("id") @@ -47,8 +39,7 @@ public partial record Emoji json.GetPropertyOrNull("name")?.GetNonWhiteSpaceStringOrNull() ?? "Unknown Emoji"; var isAnimated = json.GetPropertyOrNull("animated")?.GetBooleanOrNull() ?? false; - var imageUrl = GetImageUrl(id, name, isAnimated); - return new Emoji(id, name, isAnimated, imageUrl); + return new Emoji(id, name, isAnimated); } } diff --git a/DiscordChatExporter.Core/Discord/Data/EmojiIndex.cs b/DiscordChatExporter.Core/Discord/Data/EmojiIndex.cs index 80d157c6..5a1dca93 100644 --- a/DiscordChatExporter.Core/Discord/Data/EmojiIndex.cs +++ b/DiscordChatExporter.Core/Discord/Data/EmojiIndex.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; @@ -8,8880 +8,8854 @@ namespace DiscordChatExporter.Core.Utils; [ExcludeFromCodeCoverage] internal static class EmojiIndex { - private static Dictionary _toCodes = - new(5000, StringComparer.Ordinal) - { - ["😀"] = "grinning", - ["😃"] = "smiley", - ["😄"] = "smile", - ["😁"] = "grin", - ["😆"] = "laughing", - ["😅"] = "sweat_smile", - ["😂"] = "joy", - ["🤣"] = "rofl", - ["☺️"] = "relaxed", - ["😊"] = "blush", - ["😇"] = "innocent", - ["🙂"] = "slight_smile", - ["🙃"] = "upside_down", - ["😉"] = "wink", - ["😌"] = "relieved", - ["🥲"] = "smiling_face_with_tear", - ["😍"] = "heart_eyes", - ["🥰"] = "smiling_face_with_3_hearts", - ["😘"] = "kissing_heart", - ["😗"] = "kissing", - ["😙"] = "kissing_smiling_eyes", - ["😚"] = "kissing_closed_eyes", - ["😋"] = "yum", - ["😛"] = "stuck_out_tongue", - ["😝"] = "stuck_out_tongue_closed_eyes", - ["😜"] = "stuck_out_tongue_winking_eye", - ["🤪"] = "zany_face", - ["🤨"] = "face_with_raised_eyebrow", - ["🧐"] = "face_with_monocle", - ["🤓"] = "nerd", - ["😎"] = "sunglasses", - ["🤩"] = "star_struck", - ["🥳"] = "partying_face", - ["😏"] = "smirk", - ["😒"] = "unamused", - ["😞"] = "disappointed", - ["😔"] = "pensive", - ["😟"] = "worried", - ["😕"] = "confused", - ["🙁"] = "slight_frown", - ["☹️"] = "frowning2", - ["😣"] = "persevere", - ["😖"] = "confounded", - ["😫"] = "tired_face", - ["😩"] = "weary", - ["🥺"] = "pleading_face", - ["😢"] = "cry", - ["😭"] = "sob", - ["😤"] = "triumph", - ["😮‍💨"] = "face_exhaling", - ["😠"] = "angry", - ["😡"] = "rage", - ["🤬"] = "face_with_symbols_over_mouth", - ["🤯"] = "exploding_head", - ["😳"] = "flushed", - ["😶‍🌫️"] = "face_in_clouds", - ["🥵"] = "hot_face", - ["🥶"] = "cold_face", - ["😱"] = "scream", - ["😨"] = "fearful", - ["😰"] = "cold_sweat", - ["😥"] = "disappointed_relieved", - ["😓"] = "sweat", - ["🤗"] = "hugging", - ["🤔"] = "thinking", - ["🤭"] = "face_with_hand_over_mouth", - ["🥱"] = "yawning_face", - ["🤫"] = "shushing_face", - ["🤥"] = "lying_face", - ["😶"] = "no_mouth", - ["😐"] = "neutral_face", - ["😑"] = "expressionless", - ["😬"] = "grimacing", - ["🙄"] = "rolling_eyes", - ["😯"] = "hushed", - ["😦"] = "frowning", - ["😧"] = "anguished", - ["😮"] = "open_mouth", - ["😲"] = "astonished", - ["😴"] = "sleeping", - ["🤤"] = "drooling_face", - ["😪"] = "sleepy", - ["😵"] = "dizzy_face", - ["😵‍💫"] = "face_with_spiral_eyes", - ["🤐"] = "zipper_mouth", - ["🥴"] = "woozy_face", - ["🤢"] = "nauseated_face", - ["🤮"] = "face_vomiting", - ["🤧"] = "sneezing_face", - ["😷"] = "mask", - ["🤒"] = "thermometer_face", - ["🤕"] = "head_bandage", - ["🤑"] = "money_mouth", - ["🤠"] = "cowboy", - ["🥸"] = "disguised_face", - ["😈"] = "smiling_imp", - ["👿"] = "imp", - ["👹"] = "japanese_ogre", - ["👺"] = "japanese_goblin", - ["🤡"] = "clown", - ["💩"] = "poop", - ["👻"] = "ghost", - ["💀"] = "skull", - ["☠️"] = "skull_crossbones", - ["👽"] = "alien", - ["👾"] = "space_invader", - ["🤖"] = "robot", - ["🎃"] = "jack_o_lantern", - ["😺"] = "smiley_cat", - ["😸"] = "smile_cat", - ["😹"] = "joy_cat", - ["😻"] = "heart_eyes_cat", - ["😼"] = "smirk_cat", - ["😽"] = "kissing_cat", - ["🙀"] = "scream_cat", - ["😿"] = "crying_cat_face", - ["😾"] = "pouting_cat", - ["🤲"] = "palms_up_together", - ["🤲🏻"] = "palms_up_together_tone1", - ["🤲🏼"] = "palms_up_together_tone2", - ["🤲🏽"] = "palms_up_together_tone3", - ["🤲🏾"] = "palms_up_together_tone4", - ["🤲🏿"] = "palms_up_together_tone5", - ["👐"] = "open_hands", - ["👐🏻"] = "open_hands_tone1", - ["👐🏼"] = "open_hands_tone2", - ["👐🏽"] = "open_hands_tone3", - ["👐🏾"] = "open_hands_tone4", - ["👐🏿"] = "open_hands_tone5", - ["🙌"] = "raised_hands", - ["🙌🏻"] = "raised_hands_tone1", - ["🙌🏼"] = "raised_hands_tone2", - ["🙌🏽"] = "raised_hands_tone3", - ["🙌🏾"] = "raised_hands_tone4", - ["🙌🏿"] = "raised_hands_tone5", - ["👏"] = "clap", - ["👏🏻"] = "clap_tone1", - ["👏🏼"] = "clap_tone2", - ["👏🏽"] = "clap_tone3", - ["👏🏾"] = "clap_tone4", - ["👏🏿"] = "clap_tone5", - ["🤝"] = "handshake", - ["👍"] = "thumbsup", - ["👍🏻"] = "thumbsup_tone1", - ["👍🏼"] = "thumbsup_tone2", - ["👍🏽"] = "thumbsup_tone3", - ["👍🏾"] = "thumbsup_tone4", - ["👍🏿"] = "thumbsup_tone5", - ["👎"] = "thumbsdown", - ["👎🏻"] = "thumbsdown_tone1", - ["👎🏼"] = "thumbsdown_tone2", - ["👎🏽"] = "thumbsdown_tone3", - ["👎🏾"] = "thumbsdown_tone4", - ["👎🏿"] = "thumbsdown_tone5", - ["👊"] = "punch", - ["👊🏻"] = "punch_tone1", - ["👊🏼"] = "punch_tone2", - ["👊🏽"] = "punch_tone3", - ["👊🏾"] = "punch_tone4", - ["👊🏿"] = "punch_tone5", - ["✊"] = "fist", - ["✊🏻"] = "fist_tone1", - ["✊🏼"] = "fist_tone2", - ["✊🏽"] = "fist_tone3", - ["✊🏾"] = "fist_tone4", - ["✊🏿"] = "fist_tone5", - ["🤛"] = "left_facing_fist", - ["🤛🏻"] = "left_facing_fist_tone1", - ["🤛🏼"] = "left_facing_fist_tone2", - ["🤛🏽"] = "left_facing_fist_tone3", - ["🤛🏾"] = "left_facing_fist_tone4", - ["🤛🏿"] = "left_facing_fist_tone5", - ["🤜"] = "right_facing_fist", - ["🤜🏻"] = "right_facing_fist_tone1", - ["🤜🏼"] = "right_facing_fist_tone2", - ["🤜🏽"] = "right_facing_fist_tone3", - ["🤜🏾"] = "right_facing_fist_tone4", - ["🤜🏿"] = "right_facing_fist_tone5", - ["🤞"] = "fingers_crossed", - ["🤞🏻"] = "fingers_crossed_tone1", - ["🤞🏼"] = "fingers_crossed_tone2", - ["🤞🏽"] = "fingers_crossed_tone3", - ["🤞🏾"] = "fingers_crossed_tone4", - ["🤞🏿"] = "fingers_crossed_tone5", - ["✌️"] = "v", - ["✌🏻"] = "v_tone1", - ["✌🏼"] = "v_tone2", - ["✌🏽"] = "v_tone3", - ["✌🏾"] = "v_tone4", - ["✌🏿"] = "v_tone5", - ["🤟"] = "love_you_gesture", - ["🤟🏻"] = "love_you_gesture_tone1", - ["🤟🏼"] = "love_you_gesture_tone2", - ["🤟🏽"] = "love_you_gesture_tone3", - ["🤟🏾"] = "love_you_gesture_tone4", - ["🤟🏿"] = "love_you_gesture_tone5", - ["🤘"] = "metal", - ["🤘🏻"] = "metal_tone1", - ["🤘🏼"] = "metal_tone2", - ["🤘🏽"] = "metal_tone3", - ["🤘🏾"] = "metal_tone4", - ["🤘🏿"] = "metal_tone5", - ["👌"] = "ok_hand", - ["👌🏻"] = "ok_hand_tone1", - ["👌🏼"] = "ok_hand_tone2", - ["👌🏽"] = "ok_hand_tone3", - ["👌🏾"] = "ok_hand_tone4", - ["👌🏿"] = "ok_hand_tone5", - ["🤏"] = "pinching_hand", - ["🤏🏻"] = "pinching_hand_tone1", - ["🤏🏼"] = "pinching_hand_tone2", - ["🤏🏽"] = "pinching_hand_tone3", - ["🤏🏾"] = "pinching_hand_tone4", - ["🤏🏿"] = "pinching_hand_tone5", - ["🤌"] = "pinched_fingers", - ["🤌🏼"] = "pinched_fingers_tone2", - ["🤌🏻"] = "pinched_fingers_tone1", - ["🤌🏽"] = "pinched_fingers_tone3", - ["🤌🏾"] = "pinched_fingers_tone4", - ["🤌🏿"] = "pinched_fingers_tone5", - ["👈"] = "point_left", - ["👈🏻"] = "point_left_tone1", - ["👈🏼"] = "point_left_tone2", - ["👈🏽"] = "point_left_tone3", - ["👈🏾"] = "point_left_tone4", - ["👈🏿"] = "point_left_tone5", - ["👉"] = "point_right", - ["👉🏻"] = "point_right_tone1", - ["👉🏼"] = "point_right_tone2", - ["👉🏽"] = "point_right_tone3", - ["👉🏾"] = "point_right_tone4", - ["👉🏿"] = "point_right_tone5", - ["👆"] = "point_up_2", - ["👆🏻"] = "point_up_2_tone1", - ["👆🏼"] = "point_up_2_tone2", - ["👆🏽"] = "point_up_2_tone3", - ["👆🏾"] = "point_up_2_tone4", - ["👆🏿"] = "point_up_2_tone5", - ["👇"] = "point_down", - ["👇🏻"] = "point_down_tone1", - ["👇🏼"] = "point_down_tone2", - ["👇🏽"] = "point_down_tone3", - ["👇🏾"] = "point_down_tone4", - ["👇🏿"] = "point_down_tone5", - ["☝️"] = "point_up", - ["☝🏻"] = "point_up_tone1", - ["☝🏼"] = "point_up_tone2", - ["☝🏽"] = "point_up_tone3", - ["☝🏾"] = "point_up_tone4", - ["☝🏿"] = "point_up_tone5", - ["✋"] = "raised_hand", - ["✋🏻"] = "raised_hand_tone1", - ["✋🏼"] = "raised_hand_tone2", - ["✋🏽"] = "raised_hand_tone3", - ["✋🏾"] = "raised_hand_tone4", - ["✋🏿"] = "raised_hand_tone5", - ["🤚"] = "raised_back_of_hand", - ["🤚🏻"] = "raised_back_of_hand_tone1", - ["🤚🏼"] = "raised_back_of_hand_tone2", - ["🤚🏽"] = "raised_back_of_hand_tone3", - ["🤚🏾"] = "raised_back_of_hand_tone4", - ["🤚🏿"] = "raised_back_of_hand_tone5", - ["🖐️"] = "hand_splayed", - ["🖐🏻"] = "hand_splayed_tone1", - ["🖐🏼"] = "hand_splayed_tone2", - ["🖐🏽"] = "hand_splayed_tone3", - ["🖐🏾"] = "hand_splayed_tone4", - ["🖐🏿"] = "hand_splayed_tone5", - ["🖖"] = "vulcan", - ["🖖🏻"] = "vulcan_tone1", - ["🖖🏼"] = "vulcan_tone2", - ["🖖🏽"] = "vulcan_tone3", - ["🖖🏾"] = "vulcan_tone4", - ["🖖🏿"] = "vulcan_tone5", - ["👋"] = "wave", - ["👋🏻"] = "wave_tone1", - ["👋🏼"] = "wave_tone2", - ["👋🏽"] = "wave_tone3", - ["👋🏾"] = "wave_tone4", - ["👋🏿"] = "wave_tone5", - ["🤙"] = "call_me", - ["🤙🏻"] = "call_me_tone1", - ["🤙🏼"] = "call_me_tone2", - ["🤙🏽"] = "call_me_tone3", - ["🤙🏾"] = "call_me_tone4", - ["🤙🏿"] = "call_me_tone5", - ["💪"] = "muscle", - ["💪🏻"] = "muscle_tone1", - ["💪🏼"] = "muscle_tone2", - ["💪🏽"] = "muscle_tone3", - ["💪🏾"] = "muscle_tone4", - ["💪🏿"] = "muscle_tone5", - ["🦾"] = "mechanical_arm", - ["🖕"] = "middle_finger", - ["🖕🏻"] = "middle_finger_tone1", - ["🖕🏼"] = "middle_finger_tone2", - ["🖕🏽"] = "middle_finger_tone3", - ["🖕🏾"] = "middle_finger_tone4", - ["🖕🏿"] = "middle_finger_tone5", - ["✍️"] = "writing_hand", - ["✍🏻"] = "writing_hand_tone1", - ["✍🏼"] = "writing_hand_tone2", - ["✍🏽"] = "writing_hand_tone3", - ["✍🏾"] = "writing_hand_tone4", - ["✍🏿"] = "writing_hand_tone5", - ["🙏"] = "pray", - ["🙏🏻"] = "pray_tone1", - ["🙏🏼"] = "pray_tone2", - ["🙏🏽"] = "pray_tone3", - ["🙏🏾"] = "pray_tone4", - ["🙏🏿"] = "pray_tone5", - ["🦶"] = "foot", - ["🦶🏻"] = "foot_tone1", - ["🦶🏼"] = "foot_tone2", - ["🦶🏽"] = "foot_tone3", - ["🦶🏾"] = "foot_tone4", - ["🦶🏿"] = "foot_tone5", - ["🦵"] = "leg", - ["🦵🏻"] = "leg_tone1", - ["🦵🏼"] = "leg_tone2", - ["🦵🏽"] = "leg_tone3", - ["🦵🏾"] = "leg_tone4", - ["🦵🏿"] = "leg_tone5", - ["🦿"] = "mechanical_leg", - ["💄"] = "lipstick", - ["💋"] = "kiss", - ["👄"] = "lips", - ["🦷"] = "tooth", - ["👅"] = "tongue", - ["👂"] = "ear", - ["👂🏻"] = "ear_tone1", - ["👂🏼"] = "ear_tone2", - ["👂🏽"] = "ear_tone3", - ["👂🏾"] = "ear_tone4", - ["👂🏿"] = "ear_tone5", - ["🦻"] = "ear_with_hearing_aid", - ["🦻🏻"] = "ear_with_hearing_aid_tone1", - ["🦻🏼"] = "ear_with_hearing_aid_tone2", - ["🦻🏽"] = "ear_with_hearing_aid_tone3", - ["🦻🏾"] = "ear_with_hearing_aid_tone4", - ["🦻🏿"] = "ear_with_hearing_aid_tone5", - ["👃"] = "nose", - ["👃🏻"] = "nose_tone1", - ["👃🏼"] = "nose_tone2", - ["👃🏽"] = "nose_tone3", - ["👃🏾"] = "nose_tone4", - ["👃🏿"] = "nose_tone5", - ["👣"] = "footprints", - ["👁️"] = "eye", - ["👀"] = "eyes", - ["🧠"] = "brain", - ["🫀"] = "anatomical_heart", - ["🫁"] = "lungs", - ["🦴"] = "bone", - ["🗣️"] = "speaking_head", - ["👤"] = "bust_in_silhouette", - ["👥"] = "busts_in_silhouette", - ["🫂"] = "people_hugging", - ["👶"] = "baby", - ["👶🏻"] = "baby_tone1", - ["👶🏼"] = "baby_tone2", - ["👶🏽"] = "baby_tone3", - ["👶🏾"] = "baby_tone4", - ["👶🏿"] = "baby_tone5", - ["👧"] = "girl", - ["👧🏻"] = "girl_tone1", - ["👧🏼"] = "girl_tone2", - ["👧🏽"] = "girl_tone3", - ["👧🏾"] = "girl_tone4", - ["👧🏿"] = "girl_tone5", - ["🧒"] = "child", - ["🧒🏻"] = "child_tone1", - ["🧒🏼"] = "child_tone2", - ["🧒🏽"] = "child_tone3", - ["🧒🏾"] = "child_tone4", - ["🧒🏿"] = "child_tone5", - ["👦"] = "boy", - ["👦🏻"] = "boy_tone1", - ["👦🏼"] = "boy_tone2", - ["👦🏽"] = "boy_tone3", - ["👦🏾"] = "boy_tone4", - ["👦🏿"] = "boy_tone5", - ["👩"] = "woman", - ["👩🏻"] = "woman_tone1", - ["👩🏼"] = "woman_tone2", - ["👩🏽"] = "woman_tone3", - ["👩🏾"] = "woman_tone4", - ["👩🏿"] = "woman_tone5", - ["🧑"] = "adult", - ["🧑🏻"] = "adult_tone1", - ["🧑🏼"] = "adult_tone2", - ["🧑🏽"] = "adult_tone3", - ["🧑🏾"] = "adult_tone4", - ["🧑🏿"] = "adult_tone5", - ["👨"] = "man", - ["👨🏻"] = "man_tone1", - ["👨🏼"] = "man_tone2", - ["👨🏽"] = "man_tone3", - ["👨🏾"] = "man_tone4", - ["👨🏿"] = "man_tone5", - ["🧑‍🦱"] = "person_curly_hair", - ["🧑🏻‍🦱"] = "person_tone1_curly_hair", - ["🧑🏼‍🦱"] = "person_tone2_curly_hair", - ["🧑🏽‍🦱"] = "person_tone3_curly_hair", - ["🧑🏾‍🦱"] = "person_tone4_curly_hair", - ["🧑🏿‍🦱"] = "person_tone5_curly_hair", - ["👩‍🦱"] = "woman_curly_haired", - ["👩🏻‍🦱"] = "woman_curly_haired_tone1", - ["👩🏼‍🦱"] = "woman_curly_haired_tone2", - ["👩🏽‍🦱"] = "woman_curly_haired_tone3", - ["👩🏾‍🦱"] = "woman_curly_haired_tone4", - ["👩🏿‍🦱"] = "woman_curly_haired_tone5", - ["👨‍🦱"] = "man_curly_haired", - ["👨🏻‍🦱"] = "man_curly_haired_tone1", - ["👨🏼‍🦱"] = "man_curly_haired_tone2", - ["👨🏽‍🦱"] = "man_curly_haired_tone3", - ["👨🏾‍🦱"] = "man_curly_haired_tone4", - ["👨🏿‍🦱"] = "man_curly_haired_tone5", - ["🧑‍🦰"] = "person_red_hair", - ["🧑🏻‍🦰"] = "person_tone1_red_hair", - ["🧑🏼‍🦰"] = "person_tone2_red_hair", - ["🧑🏽‍🦰"] = "person_tone3_red_hair", - ["🧑🏾‍🦰"] = "person_tone4_red_hair", - ["🧑🏿‍🦰"] = "person_tone5_red_hair", - ["👩‍🦰"] = "woman_red_haired", - ["👩🏻‍🦰"] = "woman_red_haired_tone1", - ["👩🏼‍🦰"] = "woman_red_haired_tone2", - ["👩🏽‍🦰"] = "woman_red_haired_tone3", - ["👩🏾‍🦰"] = "woman_red_haired_tone4", - ["👩🏿‍🦰"] = "woman_red_haired_tone5", - ["👨‍🦰"] = "man_red_haired", - ["👨🏻‍🦰"] = "man_red_haired_tone1", - ["👨🏼‍🦰"] = "man_red_haired_tone2", - ["👨🏽‍🦰"] = "man_red_haired_tone3", - ["👨🏾‍🦰"] = "man_red_haired_tone4", - ["👨🏿‍🦰"] = "man_red_haired_tone5", - ["👱‍♀️"] = "blond_haired_woman", - ["👱🏻‍♀️"] = "blond_haired_woman_tone1", - ["👱🏼‍♀️"] = "blond_haired_woman_tone2", - ["👱🏽‍♀️"] = "blond_haired_woman_tone3", - ["👱🏾‍♀️"] = "blond_haired_woman_tone4", - ["👱🏿‍♀️"] = "blond_haired_woman_tone5", - ["👱"] = "blond_haired_person", - ["👱🏻"] = "blond_haired_person_tone1", - ["👱🏼"] = "blond_haired_person_tone2", - ["👱🏽"] = "blond_haired_person_tone3", - ["👱🏾"] = "blond_haired_person_tone4", - ["👱🏿"] = "blond_haired_person_tone5", - ["👱‍♂️"] = "blond_haired_man", - ["👱🏻‍♂️"] = "blond_haired_man_tone1", - ["👱🏼‍♂️"] = "blond_haired_man_tone2", - ["👱🏽‍♂️"] = "blond_haired_man_tone3", - ["👱🏾‍♂️"] = "blond_haired_man_tone4", - ["👱🏿‍♂️"] = "blond_haired_man_tone5", - ["🧑‍🦳"] = "person_white_hair", - ["🧑🏻‍🦳"] = "person_tone1_white_hair", - ["🧑🏼‍🦳"] = "person_tone2_white_hair", - ["🧑🏽‍🦳"] = "person_tone3_white_hair", - ["🧑🏾‍🦳"] = "person_tone4_white_hair", - ["🧑🏿‍🦳"] = "person_tone5_white_hair", - ["👩‍🦳"] = "woman_white_haired", - ["👩🏻‍🦳"] = "woman_white_haired_tone1", - ["👩🏼‍🦳"] = "woman_white_haired_tone2", - ["👩🏽‍🦳"] = "woman_white_haired_tone3", - ["👩🏾‍🦳"] = "woman_white_haired_tone4", - ["👩🏿‍🦳"] = "woman_white_haired_tone5", - ["👨‍🦳"] = "man_white_haired", - ["👨🏻‍🦳"] = "man_white_haired_tone1", - ["👨🏼‍🦳"] = "man_white_haired_tone2", - ["👨🏽‍🦳"] = "man_white_haired_tone3", - ["👨🏾‍🦳"] = "man_white_haired_tone4", - ["👨🏿‍🦳"] = "man_white_haired_tone5", - ["🧑‍🦲"] = "person_bald", - ["🧑🏻‍🦲"] = "person_tone1_bald", - ["🧑🏼‍🦲"] = "person_tone2_bald", - ["🧑🏽‍🦲"] = "person_tone3_bald", - ["🧑🏾‍🦲"] = "person_tone4_bald", - ["🧑🏿‍🦲"] = "person_tone5_bald", - ["👩‍🦲"] = "woman_bald", - ["👩🏻‍🦲"] = "woman_bald_tone1", - ["👩🏼‍🦲"] = "woman_bald_tone2", - ["👩🏽‍🦲"] = "woman_bald_tone3", - ["👩🏾‍🦲"] = "woman_bald_tone4", - ["👩🏿‍🦲"] = "woman_bald_tone5", - ["👨‍🦲"] = "man_bald", - ["👨🏻‍🦲"] = "man_bald_tone1", - ["👨🏼‍🦲"] = "man_bald_tone2", - ["👨🏽‍🦲"] = "man_bald_tone3", - ["👨🏾‍🦲"] = "man_bald_tone4", - ["👨🏿‍🦲"] = "man_bald_tone5", - ["🧔"] = "bearded_person", - ["🧔🏻"] = "bearded_person_tone1", - ["🧔🏼"] = "bearded_person_tone2", - ["🧔🏽"] = "bearded_person_tone3", - ["🧔🏾"] = "bearded_person_tone4", - ["🧔🏿"] = "bearded_person_tone5", - ["🧔‍♂️"] = "man_beard", - ["🧔🏻‍♂️"] = "man_tone1_beard", - ["🧔🏼‍♂️"] = "man_tone2_beard", - ["🧔🏽‍♂️"] = "man_tone3_beard", - ["🧔🏾‍♂️"] = "man_tone4_beard", - ["🧔🏿‍♂️"] = "man_tone5_beard", - ["🧔‍♀️"] = "woman_beard", - ["🧔🏻‍♀️"] = "woman_tone1_beard", - ["🧔🏼‍♀️"] = "woman_tone2_beard", - ["🧔🏽‍♀️"] = "woman_tone3_beard", - ["🧔🏾‍♀️"] = "woman_tone4_beard", - ["🧔🏿‍♀️"] = "woman_tone5_beard", - ["👵"] = "older_woman", - ["👵🏻"] = "older_woman_tone1", - ["👵🏼"] = "older_woman_tone2", - ["👵🏽"] = "older_woman_tone3", - ["👵🏾"] = "older_woman_tone4", - ["👵🏿"] = "older_woman_tone5", - ["🧓"] = "older_adult", - ["🧓🏻"] = "older_adult_tone1", - ["🧓🏼"] = "older_adult_tone2", - ["🧓🏽"] = "older_adult_tone3", - ["🧓🏾"] = "older_adult_tone4", - ["🧓🏿"] = "older_adult_tone5", - ["👴"] = "older_man", - ["👴🏻"] = "older_man_tone1", - ["👴🏼"] = "older_man_tone2", - ["👴🏽"] = "older_man_tone3", - ["👴🏾"] = "older_man_tone4", - ["👴🏿"] = "older_man_tone5", - ["👲"] = "man_with_chinese_cap", - ["👲🏻"] = "man_with_chinese_cap_tone1", - ["👲🏼"] = "man_with_chinese_cap_tone2", - ["👲🏽"] = "man_with_chinese_cap_tone3", - ["👲🏾"] = "man_with_chinese_cap_tone4", - ["👲🏿"] = "man_with_chinese_cap_tone5", - ["👳"] = "person_wearing_turban", - ["👳🏻"] = "person_wearing_turban_tone1", - ["👳🏼"] = "person_wearing_turban_tone2", - ["👳🏽"] = "person_wearing_turban_tone3", - ["👳🏾"] = "person_wearing_turban_tone4", - ["👳🏿"] = "person_wearing_turban_tone5", - ["👳‍♀️"] = "woman_wearing_turban", - ["👳🏻‍♀️"] = "woman_wearing_turban_tone1", - ["👳🏼‍♀️"] = "woman_wearing_turban_tone2", - ["👳🏽‍♀️"] = "woman_wearing_turban_tone3", - ["👳🏾‍♀️"] = "woman_wearing_turban_tone4", - ["👳🏿‍♀️"] = "woman_wearing_turban_tone5", - ["👳‍♂️"] = "man_wearing_turban", - ["👳🏻‍♂️"] = "man_wearing_turban_tone1", - ["👳🏼‍♂️"] = "man_wearing_turban_tone2", - ["👳🏽‍♂️"] = "man_wearing_turban_tone3", - ["👳🏾‍♂️"] = "man_wearing_turban_tone4", - ["👳🏿‍♂️"] = "man_wearing_turban_tone5", - ["🧕"] = "woman_with_headscarf", - ["🧕🏻"] = "woman_with_headscarf_tone1", - ["🧕🏼"] = "woman_with_headscarf_tone2", - ["🧕🏽"] = "woman_with_headscarf_tone3", - ["🧕🏾"] = "woman_with_headscarf_tone4", - ["🧕🏿"] = "woman_with_headscarf_tone5", - ["👮"] = "police_officer", - ["👮🏻"] = "police_officer_tone1", - ["👮🏼"] = "police_officer_tone2", - ["👮🏽"] = "police_officer_tone3", - ["👮🏾"] = "police_officer_tone4", - ["👮🏿"] = "police_officer_tone5", - ["👮‍♀️"] = "woman_police_officer", - ["👮🏻‍♀️"] = "woman_police_officer_tone1", - ["👮🏼‍♀️"] = "woman_police_officer_tone2", - ["👮🏽‍♀️"] = "woman_police_officer_tone3", - ["👮🏾‍♀️"] = "woman_police_officer_tone4", - ["👮🏿‍♀️"] = "woman_police_officer_tone5", - ["👮‍♂️"] = "man_police_officer", - ["👮🏻‍♂️"] = "man_police_officer_tone1", - ["👮🏼‍♂️"] = "man_police_officer_tone2", - ["👮🏽‍♂️"] = "man_police_officer_tone3", - ["👮🏾‍♂️"] = "man_police_officer_tone4", - ["👮🏿‍♂️"] = "man_police_officer_tone5", - ["👷"] = "construction_worker", - ["👷🏻"] = "construction_worker_tone1", - ["👷🏼"] = "construction_worker_tone2", - ["👷🏽"] = "construction_worker_tone3", - ["👷🏾"] = "construction_worker_tone4", - ["👷🏿"] = "construction_worker_tone5", - ["👷‍♀️"] = "woman_construction_worker", - ["👷🏻‍♀️"] = "woman_construction_worker_tone1", - ["👷🏼‍♀️"] = "woman_construction_worker_tone2", - ["👷🏽‍♀️"] = "woman_construction_worker_tone3", - ["👷🏾‍♀️"] = "woman_construction_worker_tone4", - ["👷🏿‍♀️"] = "woman_construction_worker_tone5", - ["👷‍♂️"] = "man_construction_worker", - ["👷🏻‍♂️"] = "man_construction_worker_tone1", - ["👷🏼‍♂️"] = "man_construction_worker_tone2", - ["👷🏽‍♂️"] = "man_construction_worker_tone3", - ["👷🏾‍♂️"] = "man_construction_worker_tone4", - ["👷🏿‍♂️"] = "man_construction_worker_tone5", - ["💂"] = "guard", - ["💂🏻"] = "guard_tone1", - ["💂🏼"] = "guard_tone2", - ["💂🏽"] = "guard_tone3", - ["💂🏾"] = "guard_tone4", - ["💂🏿"] = "guard_tone5", - ["💂‍♀️"] = "woman_guard", - ["💂🏻‍♀️"] = "woman_guard_tone1", - ["💂🏼‍♀️"] = "woman_guard_tone2", - ["💂🏽‍♀️"] = "woman_guard_tone3", - ["💂🏾‍♀️"] = "woman_guard_tone4", - ["💂🏿‍♀️"] = "woman_guard_tone5", - ["💂‍♂️"] = "man_guard", - ["💂🏻‍♂️"] = "man_guard_tone1", - ["💂🏼‍♂️"] = "man_guard_tone2", - ["💂🏽‍♂️"] = "man_guard_tone3", - ["💂🏾‍♂️"] = "man_guard_tone4", - ["💂🏿‍♂️"] = "man_guard_tone5", - ["🕵️"] = "detective", - ["🕵🏻"] = "detective_tone1", - ["🕵🏼"] = "detective_tone2", - ["🕵🏽"] = "detective_tone3", - ["🕵🏾"] = "detective_tone4", - ["🕵🏿"] = "detective_tone5", - ["🕵️‍♀️"] = "woman_detective", - ["🕵🏻‍♀️"] = "woman_detective_tone1", - ["🕵🏼‍♀️"] = "woman_detective_tone2", - ["🕵🏽‍♀️"] = "woman_detective_tone3", - ["🕵🏾‍♀️"] = "woman_detective_tone4", - ["🕵🏿‍♀️"] = "woman_detective_tone5", - ["🕵️‍♂️"] = "man_detective", - ["🕵🏻‍♂️"] = "man_detective_tone1", - ["🕵🏼‍♂️"] = "man_detective_tone2", - ["🕵🏽‍♂️"] = "man_detective_tone3", - ["🕵🏾‍♂️"] = "man_detective_tone4", - ["🕵🏿‍♂️"] = "man_detective_tone5", - ["🧑‍⚕️"] = "health_worker", - ["🧑🏻‍⚕️"] = "health_worker_tone1", - ["🧑🏼‍⚕️"] = "health_worker_tone2", - ["🧑🏽‍⚕️"] = "health_worker_tone3", - ["🧑🏾‍⚕️"] = "health_worker_tone4", - ["🧑🏿‍⚕️"] = "health_worker_tone5", - ["👩‍⚕️"] = "woman_health_worker", - ["👩🏻‍⚕️"] = "woman_health_worker_tone1", - ["👩🏼‍⚕️"] = "woman_health_worker_tone2", - ["👩🏽‍⚕️"] = "woman_health_worker_tone3", - ["👩🏾‍⚕️"] = "woman_health_worker_tone4", - ["👩🏿‍⚕️"] = "woman_health_worker_tone5", - ["👨‍⚕️"] = "man_health_worker", - ["👨🏻‍⚕️"] = "man_health_worker_tone1", - ["👨🏼‍⚕️"] = "man_health_worker_tone2", - ["👨🏽‍⚕️"] = "man_health_worker_tone3", - ["👨🏾‍⚕️"] = "man_health_worker_tone4", - ["👨🏿‍⚕️"] = "man_health_worker_tone5", - ["🧑‍🌾"] = "farmer", - ["🧑🏻‍🌾"] = "farmer_tone1", - ["🧑🏼‍🌾"] = "farmer_tone2", - ["🧑🏽‍🌾"] = "farmer_tone3", - ["🧑🏾‍🌾"] = "farmer_tone4", - ["🧑🏿‍🌾"] = "farmer_tone5", - ["👩‍🌾"] = "woman_farmer", - ["👩🏻‍🌾"] = "woman_farmer_tone1", - ["👩🏼‍🌾"] = "woman_farmer_tone2", - ["👩🏽‍🌾"] = "woman_farmer_tone3", - ["👩🏾‍🌾"] = "woman_farmer_tone4", - ["👩🏿‍🌾"] = "woman_farmer_tone5", - ["👨‍🌾"] = "man_farmer", - ["👨🏻‍🌾"] = "man_farmer_tone1", - ["👨🏼‍🌾"] = "man_farmer_tone2", - ["👨🏽‍🌾"] = "man_farmer_tone3", - ["👨🏾‍🌾"] = "man_farmer_tone4", - ["👨🏿‍🌾"] = "man_farmer_tone5", - ["🧑‍🍳"] = "cook", - ["🧑🏻‍🍳"] = "cook_tone1", - ["🧑🏼‍🍳"] = "cook_tone2", - ["🧑🏽‍🍳"] = "cook_tone3", - ["🧑🏾‍🍳"] = "cook_tone4", - ["🧑🏿‍🍳"] = "cook_tone5", - ["👩‍🍳"] = "woman_cook", - ["👩🏻‍🍳"] = "woman_cook_tone1", - ["👩🏼‍🍳"] = "woman_cook_tone2", - ["👩🏽‍🍳"] = "woman_cook_tone3", - ["👩🏾‍🍳"] = "woman_cook_tone4", - ["👩🏿‍🍳"] = "woman_cook_tone5", - ["👨‍🍳"] = "man_cook", - ["👨🏻‍🍳"] = "man_cook_tone1", - ["👨🏼‍🍳"] = "man_cook_tone2", - ["👨🏽‍🍳"] = "man_cook_tone3", - ["👨🏾‍🍳"] = "man_cook_tone4", - ["👨🏿‍🍳"] = "man_cook_tone5", - ["🧑‍🎓"] = "student", - ["🧑🏻‍🎓"] = "student_tone1", - ["🧑🏼‍🎓"] = "student_tone2", - ["🧑🏽‍🎓"] = "student_tone3", - ["🧑🏾‍🎓"] = "student_tone4", - ["🧑🏿‍🎓"] = "student_tone5", - ["👩‍🎓"] = "woman_student", - ["👩🏻‍🎓"] = "woman_student_tone1", - ["👩🏼‍🎓"] = "woman_student_tone2", - ["👩🏽‍🎓"] = "woman_student_tone3", - ["👩🏾‍🎓"] = "woman_student_tone4", - ["👩🏿‍🎓"] = "woman_student_tone5", - ["👨‍🎓"] = "man_student", - ["👨🏻‍🎓"] = "man_student_tone1", - ["👨🏼‍🎓"] = "man_student_tone2", - ["👨🏽‍🎓"] = "man_student_tone3", - ["👨🏾‍🎓"] = "man_student_tone4", - ["👨🏿‍🎓"] = "man_student_tone5", - ["🧑‍🎤"] = "singer", - ["🧑🏻‍🎤"] = "singer_tone1", - ["🧑🏼‍🎤"] = "singer_tone2", - ["🧑🏽‍🎤"] = "singer_tone3", - ["🧑🏾‍🎤"] = "singer_tone4", - ["🧑🏿‍🎤"] = "singer_tone5", - ["👩‍🎤"] = "woman_singer", - ["👩🏻‍🎤"] = "woman_singer_tone1", - ["👩🏼‍🎤"] = "woman_singer_tone2", - ["👩🏽‍🎤"] = "woman_singer_tone3", - ["👩🏾‍🎤"] = "woman_singer_tone4", - ["👩🏿‍🎤"] = "woman_singer_tone5", - ["👨‍🎤"] = "man_singer", - ["👨🏻‍🎤"] = "man_singer_tone1", - ["👨🏼‍🎤"] = "man_singer_tone2", - ["👨🏽‍🎤"] = "man_singer_tone3", - ["👨🏾‍🎤"] = "man_singer_tone4", - ["👨🏿‍🎤"] = "man_singer_tone5", - ["🧑‍🏫"] = "teacher", - ["🧑🏻‍🏫"] = "teacher_tone1", - ["🧑🏼‍🏫"] = "teacher_tone2", - ["🧑🏽‍🏫"] = "teacher_tone3", - ["🧑🏾‍🏫"] = "teacher_tone4", - ["🧑🏿‍🏫"] = "teacher_tone5", - ["👩‍🏫"] = "woman_teacher", - ["👩🏻‍🏫"] = "woman_teacher_tone1", - ["👩🏼‍🏫"] = "woman_teacher_tone2", - ["👩🏽‍🏫"] = "woman_teacher_tone3", - ["👩🏾‍🏫"] = "woman_teacher_tone4", - ["👩🏿‍🏫"] = "woman_teacher_tone5", - ["👨‍🏫"] = "man_teacher", - ["👨🏻‍🏫"] = "man_teacher_tone1", - ["👨🏼‍🏫"] = "man_teacher_tone2", - ["👨🏽‍🏫"] = "man_teacher_tone3", - ["👨🏾‍🏫"] = "man_teacher_tone4", - ["👨🏿‍🏫"] = "man_teacher_tone5", - ["🧑‍🏭"] = "factory_worker", - ["🧑🏻‍🏭"] = "factory_worker_tone1", - ["🧑🏼‍🏭"] = "factory_worker_tone2", - ["🧑🏽‍🏭"] = "factory_worker_tone3", - ["🧑🏾‍🏭"] = "factory_worker_tone4", - ["🧑🏿‍🏭"] = "factory_worker_tone5", - ["👩‍🏭"] = "woman_factory_worker", - ["👩🏻‍🏭"] = "woman_factory_worker_tone1", - ["👩🏼‍🏭"] = "woman_factory_worker_tone2", - ["👩🏽‍🏭"] = "woman_factory_worker_tone3", - ["👩🏾‍🏭"] = "woman_factory_worker_tone4", - ["👩🏿‍🏭"] = "woman_factory_worker_tone5", - ["👨‍🏭"] = "man_factory_worker", - ["👨🏻‍🏭"] = "man_factory_worker_tone1", - ["👨🏼‍🏭"] = "man_factory_worker_tone2", - ["👨🏽‍🏭"] = "man_factory_worker_tone3", - ["👨🏾‍🏭"] = "man_factory_worker_tone4", - ["👨🏿‍🏭"] = "man_factory_worker_tone5", - ["🧑‍💻"] = "technologist", - ["🧑🏻‍💻"] = "technologist_tone1", - ["🧑🏼‍💻"] = "technologist_tone2", - ["🧑🏽‍💻"] = "technologist_tone3", - ["🧑🏾‍💻"] = "technologist_tone4", - ["🧑🏿‍💻"] = "technologist_tone5", - ["👩‍💻"] = "woman_technologist", - ["👩🏻‍💻"] = "woman_technologist_tone1", - ["👩🏼‍💻"] = "woman_technologist_tone2", - ["👩🏽‍💻"] = "woman_technologist_tone3", - ["👩🏾‍💻"] = "woman_technologist_tone4", - ["👩🏿‍💻"] = "woman_technologist_tone5", - ["👨‍💻"] = "man_technologist", - ["👨🏻‍💻"] = "man_technologist_tone1", - ["👨🏼‍💻"] = "man_technologist_tone2", - ["👨🏽‍💻"] = "man_technologist_tone3", - ["👨🏾‍💻"] = "man_technologist_tone4", - ["👨🏿‍💻"] = "man_technologist_tone5", - ["🧑‍💼"] = "office_worker", - ["🧑🏻‍💼"] = "office_worker_tone1", - ["🧑🏼‍💼"] = "office_worker_tone2", - ["🧑🏽‍💼"] = "office_worker_tone3", - ["🧑🏾‍💼"] = "office_worker_tone4", - ["🧑🏿‍💼"] = "office_worker_tone5", - ["👩‍💼"] = "woman_office_worker", - ["👩🏻‍💼"] = "woman_office_worker_tone1", - ["👩🏼‍💼"] = "woman_office_worker_tone2", - ["👩🏽‍💼"] = "woman_office_worker_tone3", - ["👩🏾‍💼"] = "woman_office_worker_tone4", - ["👩🏿‍💼"] = "woman_office_worker_tone5", - ["👨‍💼"] = "man_office_worker", - ["👨🏻‍💼"] = "man_office_worker_tone1", - ["👨🏼‍💼"] = "man_office_worker_tone2", - ["👨🏽‍💼"] = "man_office_worker_tone3", - ["👨🏾‍💼"] = "man_office_worker_tone4", - ["👨🏿‍💼"] = "man_office_worker_tone5", - ["🧑‍🔧"] = "mechanic", - ["🧑🏻‍🔧"] = "mechanic_tone1", - ["🧑🏼‍🔧"] = "mechanic_tone2", - ["🧑🏽‍🔧"] = "mechanic_tone3", - ["🧑🏾‍🔧"] = "mechanic_tone4", - ["🧑🏿‍🔧"] = "mechanic_tone5", - ["👩‍🔧"] = "woman_mechanic", - ["👩🏻‍🔧"] = "woman_mechanic_tone1", - ["👩🏼‍🔧"] = "woman_mechanic_tone2", - ["👩🏽‍🔧"] = "woman_mechanic_tone3", - ["👩🏾‍🔧"] = "woman_mechanic_tone4", - ["👩🏿‍🔧"] = "woman_mechanic_tone5", - ["👨‍🔧"] = "man_mechanic", - ["👨🏻‍🔧"] = "man_mechanic_tone1", - ["👨🏼‍🔧"] = "man_mechanic_tone2", - ["👨🏽‍🔧"] = "man_mechanic_tone3", - ["👨🏾‍🔧"] = "man_mechanic_tone4", - ["👨🏿‍🔧"] = "man_mechanic_tone5", - ["🧑‍🔬"] = "scientist", - ["🧑🏻‍🔬"] = "scientist_tone1", - ["🧑🏼‍🔬"] = "scientist_tone2", - ["🧑🏽‍🔬"] = "scientist_tone3", - ["🧑🏾‍🔬"] = "scientist_tone4", - ["🧑🏿‍🔬"] = "scientist_tone5", - ["👩‍🔬"] = "woman_scientist", - ["👩🏻‍🔬"] = "woman_scientist_tone1", - ["👩🏼‍🔬"] = "woman_scientist_tone2", - ["👩🏽‍🔬"] = "woman_scientist_tone3", - ["👩🏾‍🔬"] = "woman_scientist_tone4", - ["👩🏿‍🔬"] = "woman_scientist_tone5", - ["👨‍🔬"] = "man_scientist", - ["👨🏻‍🔬"] = "man_scientist_tone1", - ["👨🏼‍🔬"] = "man_scientist_tone2", - ["👨🏽‍🔬"] = "man_scientist_tone3", - ["👨🏾‍🔬"] = "man_scientist_tone4", - ["👨🏿‍🔬"] = "man_scientist_tone5", - ["🧑‍🎨"] = "artist", - ["🧑🏻‍🎨"] = "artist_tone1", - ["🧑🏼‍🎨"] = "artist_tone2", - ["🧑🏽‍🎨"] = "artist_tone3", - ["🧑🏾‍🎨"] = "artist_tone4", - ["🧑🏿‍🎨"] = "artist_tone5", - ["👩‍🎨"] = "woman_artist", - ["👩🏻‍🎨"] = "woman_artist_tone1", - ["👩🏼‍🎨"] = "woman_artist_tone2", - ["👩🏽‍🎨"] = "woman_artist_tone3", - ["👩🏾‍🎨"] = "woman_artist_tone4", - ["👩🏿‍🎨"] = "woman_artist_tone5", - ["👨‍🎨"] = "man_artist", - ["👨🏻‍🎨"] = "man_artist_tone1", - ["👨🏼‍🎨"] = "man_artist_tone2", - ["👨🏽‍🎨"] = "man_artist_tone3", - ["👨🏾‍🎨"] = "man_artist_tone4", - ["👨🏿‍🎨"] = "man_artist_tone5", - ["🧑‍🚒"] = "firefighter", - ["🧑🏻‍🚒"] = "firefighter_tone1", - ["🧑🏼‍🚒"] = "firefighter_tone2", - ["🧑🏽‍🚒"] = "firefighter_tone3", - ["🧑🏾‍🚒"] = "firefighter_tone4", - ["🧑🏿‍🚒"] = "firefighter_tone5", - ["👩‍🚒"] = "woman_firefighter", - ["👩🏻‍🚒"] = "woman_firefighter_tone1", - ["👩🏼‍🚒"] = "woman_firefighter_tone2", - ["👩🏽‍🚒"] = "woman_firefighter_tone3", - ["👩🏾‍🚒"] = "woman_firefighter_tone4", - ["👩🏿‍🚒"] = "woman_firefighter_tone5", - ["👨‍🚒"] = "man_firefighter", - ["👨🏻‍🚒"] = "man_firefighter_tone1", - ["👨🏼‍🚒"] = "man_firefighter_tone2", - ["👨🏽‍🚒"] = "man_firefighter_tone3", - ["👨🏾‍🚒"] = "man_firefighter_tone4", - ["👨🏿‍🚒"] = "man_firefighter_tone5", - ["🧑‍✈️"] = "pilot", - ["🧑🏻‍✈️"] = "pilot_tone1", - ["🧑🏼‍✈️"] = "pilot_tone2", - ["🧑🏽‍✈️"] = "pilot_tone3", - ["🧑🏾‍✈️"] = "pilot_tone4", - ["🧑🏿‍✈️"] = "pilot_tone5", - ["👩‍✈️"] = "woman_pilot", - ["👩🏻‍✈️"] = "woman_pilot_tone1", - ["👩🏼‍✈️"] = "woman_pilot_tone2", - ["👩🏽‍✈️"] = "woman_pilot_tone3", - ["👩🏾‍✈️"] = "woman_pilot_tone4", - ["👩🏿‍✈️"] = "woman_pilot_tone5", - ["👨‍✈️"] = "man_pilot", - ["👨🏻‍✈️"] = "man_pilot_tone1", - ["👨🏼‍✈️"] = "man_pilot_tone2", - ["👨🏽‍✈️"] = "man_pilot_tone3", - ["👨🏾‍✈️"] = "man_pilot_tone4", - ["👨🏿‍✈️"] = "man_pilot_tone5", - ["🧑‍🚀"] = "astronaut", - ["🧑🏻‍🚀"] = "astronaut_tone1", - ["🧑🏼‍🚀"] = "astronaut_tone2", - ["🧑🏽‍🚀"] = "astronaut_tone3", - ["🧑🏾‍🚀"] = "astronaut_tone4", - ["🧑🏿‍🚀"] = "astronaut_tone5", - ["👩‍🚀"] = "woman_astronaut", - ["👩🏻‍🚀"] = "woman_astronaut_tone1", - ["👩🏼‍🚀"] = "woman_astronaut_tone2", - ["👩🏽‍🚀"] = "woman_astronaut_tone3", - ["👩🏾‍🚀"] = "woman_astronaut_tone4", - ["👩🏿‍🚀"] = "woman_astronaut_tone5", - ["👨‍🚀"] = "man_astronaut", - ["👨🏻‍🚀"] = "man_astronaut_tone1", - ["👨🏼‍🚀"] = "man_astronaut_tone2", - ["👨🏽‍🚀"] = "man_astronaut_tone3", - ["👨🏾‍🚀"] = "man_astronaut_tone4", - ["👨🏿‍🚀"] = "man_astronaut_tone5", - ["🧑‍⚖️"] = "judge", - ["🧑🏻‍⚖️"] = "judge_tone1", - ["🧑🏼‍⚖️"] = "judge_tone2", - ["🧑🏽‍⚖️"] = "judge_tone3", - ["🧑🏾‍⚖️"] = "judge_tone4", - ["🧑🏿‍⚖️"] = "judge_tone5", - ["👩‍⚖️"] = "woman_judge", - ["👩🏻‍⚖️"] = "woman_judge_tone1", - ["👩🏼‍⚖️"] = "woman_judge_tone2", - ["👩🏽‍⚖️"] = "woman_judge_tone3", - ["👩🏾‍⚖️"] = "woman_judge_tone4", - ["👩🏿‍⚖️"] = "woman_judge_tone5", - ["👨‍⚖️"] = "man_judge", - ["👨🏻‍⚖️"] = "man_judge_tone1", - ["👨🏼‍⚖️"] = "man_judge_tone2", - ["👨🏽‍⚖️"] = "man_judge_tone3", - ["👨🏾‍⚖️"] = "man_judge_tone4", - ["👨🏿‍⚖️"] = "man_judge_tone5", - ["👰"] = "person_with_veil", - ["👰🏻"] = "person_with_veil_tone1", - ["👰🏼"] = "person_with_veil_tone2", - ["👰🏽"] = "person_with_veil_tone3", - ["👰🏾"] = "person_with_veil_tone4", - ["👰🏿"] = "person_with_veil_tone5", - ["👰‍♀️"] = "woman_with_veil", - ["👰🏻‍♀️"] = "woman_with_veil_tone1", - ["👰🏼‍♀️"] = "woman_with_veil_tone2", - ["👰🏽‍♀️"] = "woman_with_veil_tone3", - ["👰🏾‍♀️"] = "woman_with_veil_tone4", - ["👰🏿‍♀️"] = "woman_with_veil_tone5", - ["👰‍♂️"] = "man_with_veil", - ["👰🏻‍♂️"] = "man_with_veil_tone1", - ["👰🏼‍♂️"] = "man_with_veil_tone2", - ["👰🏽‍♂️"] = "man_with_veil_tone3", - ["👰🏾‍♂️"] = "man_with_veil_tone4", - ["👰🏿‍♂️"] = "man_with_veil_tone5", - ["🤵"] = "person_in_tuxedo", - ["🤵🏻"] = "person_in_tuxedo_tone1", - ["🤵🏼"] = "person_in_tuxedo_tone2", - ["🤵🏽"] = "person_in_tuxedo_tone3", - ["🤵🏾"] = "person_in_tuxedo_tone4", - ["🤵🏿"] = "person_in_tuxedo_tone5", - ["🤵‍♀️"] = "woman_in_tuxedo", - ["🤵🏻‍♀️"] = "woman_in_tuxedo_tone1", - ["🤵🏼‍♀️"] = "woman_in_tuxedo_tone2", - ["🤵🏽‍♀️"] = "woman_in_tuxedo_tone3", - ["🤵🏾‍♀️"] = "woman_in_tuxedo_tone4", - ["🤵🏿‍♀️"] = "woman_in_tuxedo_tone5", - ["🤵‍♂️"] = "man_in_tuxedo", - ["🤵🏻‍♂️"] = "man_in_tuxedo_tone1", - ["🤵🏼‍♂️"] = "man_in_tuxedo_tone2", - ["🤵🏽‍♂️"] = "man_in_tuxedo_tone3", - ["🤵🏾‍♂️"] = "man_in_tuxedo_tone4", - ["🤵🏿‍♂️"] = "man_in_tuxedo_tone5", - ["👸"] = "princess", - ["👸🏻"] = "princess_tone1", - ["👸🏼"] = "princess_tone2", - ["👸🏽"] = "princess_tone3", - ["👸🏾"] = "princess_tone4", - ["👸🏿"] = "princess_tone5", - ["🤴"] = "prince", - ["🤴🏻"] = "prince_tone1", - ["🤴🏼"] = "prince_tone2", - ["🤴🏽"] = "prince_tone3", - ["🤴🏾"] = "prince_tone4", - ["🤴🏿"] = "prince_tone5", - ["🦸"] = "superhero", - ["🦸🏻"] = "superhero_tone1", - ["🦸🏼"] = "superhero_tone2", - ["🦸🏽"] = "superhero_tone3", - ["🦸🏾"] = "superhero_tone4", - ["🦸🏿"] = "superhero_tone5", - ["🦸‍♀️"] = "woman_superhero", - ["🦸🏻‍♀️"] = "woman_superhero_tone1", - ["🦸🏼‍♀️"] = "woman_superhero_tone2", - ["🦸🏽‍♀️"] = "woman_superhero_tone3", - ["🦸🏾‍♀️"] = "woman_superhero_tone4", - ["🦸🏿‍♀️"] = "woman_superhero_tone5", - ["🦸‍♂️"] = "man_superhero", - ["🦸🏻‍♂️"] = "man_superhero_tone1", - ["🦸🏼‍♂️"] = "man_superhero_tone2", - ["🦸🏽‍♂️"] = "man_superhero_tone3", - ["🦸🏾‍♂️"] = "man_superhero_tone4", - ["🦸🏿‍♂️"] = "man_superhero_tone5", - ["🦹"] = "supervillain", - ["🦹🏻"] = "supervillain_tone1", - ["🦹🏼"] = "supervillain_tone2", - ["🦹🏽"] = "supervillain_tone3", - ["🦹🏾"] = "supervillain_tone4", - ["🦹🏿"] = "supervillain_tone5", - ["🦹‍♀️"] = "woman_supervillain", - ["🦹🏻‍♀️"] = "woman_supervillain_tone1", - ["🦹🏼‍♀️"] = "woman_supervillain_tone2", - ["🦹🏽‍♀️"] = "woman_supervillain_tone3", - ["🦹🏾‍♀️"] = "woman_supervillain_tone4", - ["🦹🏿‍♀️"] = "woman_supervillain_tone5", - ["🦹‍♂️"] = "man_supervillain", - ["🦹🏻‍♂️"] = "man_supervillain_tone1", - ["🦹🏼‍♂️"] = "man_supervillain_tone2", - ["🦹🏽‍♂️"] = "man_supervillain_tone3", - ["🦹🏾‍♂️"] = "man_supervillain_tone4", - ["🦹🏿‍♂️"] = "man_supervillain_tone5", - ["🥷"] = "ninja", - ["🥷🏻"] = "ninja_tone1", - ["🥷🏼"] = "ninja_tone2", - ["🥷🏽"] = "ninja_tone3", - ["🥷🏾"] = "ninja_tone4", - ["🥷🏿"] = "ninja_tone5", - ["🧑‍🎄"] = "mx_claus", - ["🧑🏻‍🎄"] = "mx_claus_tone1", - ["🧑🏼‍🎄"] = "mx_claus_tone2", - ["🧑🏽‍🎄"] = "mx_claus_tone3", - ["🧑🏾‍🎄"] = "mx_claus_tone4", - ["🧑🏿‍🎄"] = "mx_claus_tone5", - ["🤶"] = "mrs_claus", - ["🤶🏻"] = "mrs_claus_tone1", - ["🤶🏼"] = "mrs_claus_tone2", - ["🤶🏽"] = "mrs_claus_tone3", - ["🤶🏾"] = "mrs_claus_tone4", - ["🤶🏿"] = "mrs_claus_tone5", - ["🎅"] = "santa", - ["🎅🏻"] = "santa_tone1", - ["🎅🏼"] = "santa_tone2", - ["🎅🏽"] = "santa_tone3", - ["🎅🏾"] = "santa_tone4", - ["🎅🏿"] = "santa_tone5", - ["🧙"] = "mage", - ["🧙🏻"] = "mage_tone1", - ["🧙🏼"] = "mage_tone2", - ["🧙🏽"] = "mage_tone3", - ["🧙🏾"] = "mage_tone4", - ["🧙🏿"] = "mage_tone5", - ["🧙‍♀️"] = "woman_mage", - ["🧙🏻‍♀️"] = "woman_mage_tone1", - ["🧙🏼‍♀️"] = "woman_mage_tone2", - ["🧙🏽‍♀️"] = "woman_mage_tone3", - ["🧙🏾‍♀️"] = "woman_mage_tone4", - ["🧙🏿‍♀️"] = "woman_mage_tone5", - ["🧙‍♂️"] = "man_mage", - ["🧙🏻‍♂️"] = "man_mage_tone1", - ["🧙🏼‍♂️"] = "man_mage_tone2", - ["🧙🏽‍♂️"] = "man_mage_tone3", - ["🧙🏾‍♂️"] = "man_mage_tone4", - ["🧙🏿‍♂️"] = "man_mage_tone5", - ["🧝"] = "elf", - ["🧝🏻"] = "elf_tone1", - ["🧝🏼"] = "elf_tone2", - ["🧝🏽"] = "elf_tone3", - ["🧝🏾"] = "elf_tone4", - ["🧝🏿"] = "elf_tone5", - ["🧝‍♀️"] = "woman_elf", - ["🧝🏻‍♀️"] = "woman_elf_tone1", - ["🧝🏼‍♀️"] = "woman_elf_tone2", - ["🧝🏽‍♀️"] = "woman_elf_tone3", - ["🧝🏾‍♀️"] = "woman_elf_tone4", - ["🧝🏿‍♀️"] = "woman_elf_tone5", - ["🧝‍♂️"] = "man_elf", - ["🧝🏻‍♂️"] = "man_elf_tone1", - ["🧝🏼‍♂️"] = "man_elf_tone2", - ["🧝🏽‍♂️"] = "man_elf_tone3", - ["🧝🏾‍♂️"] = "man_elf_tone4", - ["🧝🏿‍♂️"] = "man_elf_tone5", - ["🧛"] = "vampire", - ["🧛🏻"] = "vampire_tone1", - ["🧛🏼"] = "vampire_tone2", - ["🧛🏽"] = "vampire_tone3", - ["🧛🏾"] = "vampire_tone4", - ["🧛🏿"] = "vampire_tone5", - ["🧛‍♀️"] = "woman_vampire", - ["🧛🏻‍♀️"] = "woman_vampire_tone1", - ["🧛🏼‍♀️"] = "woman_vampire_tone2", - ["🧛🏽‍♀️"] = "woman_vampire_tone3", - ["🧛🏾‍♀️"] = "woman_vampire_tone4", - ["🧛🏿‍♀️"] = "woman_vampire_tone5", - ["🧛‍♂️"] = "man_vampire", - ["🧛🏻‍♂️"] = "man_vampire_tone1", - ["🧛🏼‍♂️"] = "man_vampire_tone2", - ["🧛🏽‍♂️"] = "man_vampire_tone3", - ["🧛🏾‍♂️"] = "man_vampire_tone4", - ["🧛🏿‍♂️"] = "man_vampire_tone5", - ["🧟"] = "zombie", - ["🧟‍♀️"] = "woman_zombie", - ["🧟‍♂️"] = "man_zombie", - ["🧞"] = "genie", - ["🧞‍♀️"] = "woman_genie", - ["🧞‍♂️"] = "man_genie", - ["🧜"] = "merperson", - ["🧜🏻"] = "merperson_tone1", - ["🧜🏼"] = "merperson_tone2", - ["🧜🏽"] = "merperson_tone3", - ["🧜🏾"] = "merperson_tone4", - ["🧜🏿"] = "merperson_tone5", - ["🧜‍♀️"] = "mermaid", - ["🧜🏻‍♀️"] = "mermaid_tone1", - ["🧜🏼‍♀️"] = "mermaid_tone2", - ["🧜🏽‍♀️"] = "mermaid_tone3", - ["🧜🏾‍♀️"] = "mermaid_tone4", - ["🧜🏿‍♀️"] = "mermaid_tone5", - ["🧜‍♂️"] = "merman", - ["🧜🏻‍♂️"] = "merman_tone1", - ["🧜🏼‍♂️"] = "merman_tone2", - ["🧜🏽‍♂️"] = "merman_tone3", - ["🧜🏾‍♂️"] = "merman_tone4", - ["🧜🏿‍♂️"] = "merman_tone5", - ["🧚"] = "fairy", - ["🧚🏻"] = "fairy_tone1", - ["🧚🏼"] = "fairy_tone2", - ["🧚🏽"] = "fairy_tone3", - ["🧚🏾"] = "fairy_tone4", - ["🧚🏿"] = "fairy_tone5", - ["🧚‍♀️"] = "woman_fairy", - ["🧚🏻‍♀️"] = "woman_fairy_tone1", - ["🧚🏼‍♀️"] = "woman_fairy_tone2", - ["🧚🏽‍♀️"] = "woman_fairy_tone3", - ["🧚🏾‍♀️"] = "woman_fairy_tone4", - ["🧚🏿‍♀️"] = "woman_fairy_tone5", - ["🧚‍♂️"] = "man_fairy", - ["🧚🏻‍♂️"] = "man_fairy_tone1", - ["🧚🏼‍♂️"] = "man_fairy_tone2", - ["🧚🏽‍♂️"] = "man_fairy_tone3", - ["🧚🏾‍♂️"] = "man_fairy_tone4", - ["🧚🏿‍♂️"] = "man_fairy_tone5", - ["👼"] = "angel", - ["👼🏻"] = "angel_tone1", - ["👼🏼"] = "angel_tone2", - ["👼🏽"] = "angel_tone3", - ["👼🏾"] = "angel_tone4", - ["👼🏿"] = "angel_tone5", - ["🤰"] = "pregnant_woman", - ["🤰🏻"] = "pregnant_woman_tone1", - ["🤰🏼"] = "pregnant_woman_tone2", - ["🤰🏽"] = "pregnant_woman_tone3", - ["🤰🏾"] = "pregnant_woman_tone4", - ["🤰🏿"] = "pregnant_woman_tone5", - ["🤱"] = "breast_feeding", - ["🤱🏻"] = "breast_feeding_tone1", - ["🤱🏼"] = "breast_feeding_tone2", - ["🤱🏽"] = "breast_feeding_tone3", - ["🤱🏾"] = "breast_feeding_tone4", - ["🤱🏿"] = "breast_feeding_tone5", - ["🧑‍🍼"] = "person_feeding_baby", - ["🧑🏻‍🍼"] = "person_feeding_baby_tone1", - ["🧑🏼‍🍼"] = "person_feeding_baby_tone2", - ["🧑🏽‍🍼"] = "person_feeding_baby_tone3", - ["🧑🏾‍🍼"] = "person_feeding_baby_tone4", - ["🧑🏿‍🍼"] = "person_feeding_baby_tone5", - ["👩‍🍼"] = "woman_feeding_baby", - ["👩🏻‍🍼"] = "woman_feeding_baby_tone1", - ["👩🏼‍🍼"] = "woman_feeding_baby_tone2", - ["👩🏽‍🍼"] = "woman_feeding_baby_tone3", - ["👩🏾‍🍼"] = "woman_feeding_baby_tone4", - ["👩🏿‍🍼"] = "woman_feeding_baby_tone5", - ["👨‍🍼"] = "man_feeding_baby", - ["👨🏻‍🍼"] = "man_feeding_baby_tone1", - ["👨🏼‍🍼"] = "man_feeding_baby_tone2", - ["👨🏽‍🍼"] = "man_feeding_baby_tone3", - ["👨🏾‍🍼"] = "man_feeding_baby_tone4", - ["👨🏿‍🍼"] = "man_feeding_baby_tone5", - ["🙇"] = "person_bowing", - ["🙇🏻"] = "person_bowing_tone1", - ["🙇🏼"] = "person_bowing_tone2", - ["🙇🏽"] = "person_bowing_tone3", - ["🙇🏾"] = "person_bowing_tone4", - ["🙇🏿"] = "person_bowing_tone5", - ["🙇‍♀️"] = "woman_bowing", - ["🙇🏻‍♀️"] = "woman_bowing_tone1", - ["🙇🏼‍♀️"] = "woman_bowing_tone2", - ["🙇🏽‍♀️"] = "woman_bowing_tone3", - ["🙇🏾‍♀️"] = "woman_bowing_tone4", - ["🙇🏿‍♀️"] = "woman_bowing_tone5", - ["🙇‍♂️"] = "man_bowing", - ["🙇🏻‍♂️"] = "man_bowing_tone1", - ["🙇🏼‍♂️"] = "man_bowing_tone2", - ["🙇🏽‍♂️"] = "man_bowing_tone3", - ["🙇🏾‍♂️"] = "man_bowing_tone4", - ["🙇🏿‍♂️"] = "man_bowing_tone5", - ["💁"] = "person_tipping_hand", - ["💁🏻"] = "person_tipping_hand_tone1", - ["💁🏼"] = "person_tipping_hand_tone2", - ["💁🏽"] = "person_tipping_hand_tone3", - ["💁🏾"] = "person_tipping_hand_tone4", - ["💁🏿"] = "person_tipping_hand_tone5", - ["💁‍♀️"] = "woman_tipping_hand", - ["💁🏻‍♀️"] = "woman_tipping_hand_tone1", - ["💁🏼‍♀️"] = "woman_tipping_hand_tone2", - ["💁🏽‍♀️"] = "woman_tipping_hand_tone3", - ["💁🏾‍♀️"] = "woman_tipping_hand_tone4", - ["💁🏿‍♀️"] = "woman_tipping_hand_tone5", - ["💁‍♂️"] = "man_tipping_hand", - ["💁🏻‍♂️"] = "man_tipping_hand_tone1", - ["💁🏼‍♂️"] = "man_tipping_hand_tone2", - ["💁🏽‍♂️"] = "man_tipping_hand_tone3", - ["💁🏾‍♂️"] = "man_tipping_hand_tone4", - ["💁🏿‍♂️"] = "man_tipping_hand_tone5", - ["🙅"] = "person_gesturing_no", - ["🙅🏻"] = "person_gesturing_no_tone1", - ["🙅🏼"] = "person_gesturing_no_tone2", - ["🙅🏽"] = "person_gesturing_no_tone3", - ["🙅🏾"] = "person_gesturing_no_tone4", - ["🙅🏿"] = "person_gesturing_no_tone5", - ["🙅‍♀️"] = "woman_gesturing_no", - ["🙅🏻‍♀️"] = "woman_gesturing_no_tone1", - ["🙅🏼‍♀️"] = "woman_gesturing_no_tone2", - ["🙅🏽‍♀️"] = "woman_gesturing_no_tone3", - ["🙅🏾‍♀️"] = "woman_gesturing_no_tone4", - ["🙅🏿‍♀️"] = "woman_gesturing_no_tone5", - ["🙅‍♂️"] = "man_gesturing_no", - ["🙅🏻‍♂️"] = "man_gesturing_no_tone1", - ["🙅🏼‍♂️"] = "man_gesturing_no_tone2", - ["🙅🏽‍♂️"] = "man_gesturing_no_tone3", - ["🙅🏾‍♂️"] = "man_gesturing_no_tone4", - ["🙅🏿‍♂️"] = "man_gesturing_no_tone5", - ["🙆"] = "person_gesturing_ok", - ["🙆🏻"] = "person_gesturing_ok_tone1", - ["🙆🏼"] = "person_gesturing_ok_tone2", - ["🙆🏽"] = "person_gesturing_ok_tone3", - ["🙆🏾"] = "person_gesturing_ok_tone4", - ["🙆🏿"] = "person_gesturing_ok_tone5", - ["🙆‍♀️"] = "woman_gesturing_ok", - ["🙆🏻‍♀️"] = "woman_gesturing_ok_tone1", - ["🙆🏼‍♀️"] = "woman_gesturing_ok_tone2", - ["🙆🏽‍♀️"] = "woman_gesturing_ok_tone3", - ["🙆🏾‍♀️"] = "woman_gesturing_ok_tone4", - ["🙆🏿‍♀️"] = "woman_gesturing_ok_tone5", - ["🙆‍♂️"] = "man_gesturing_ok", - ["🙆🏻‍♂️"] = "man_gesturing_ok_tone1", - ["🙆🏼‍♂️"] = "man_gesturing_ok_tone2", - ["🙆🏽‍♂️"] = "man_gesturing_ok_tone3", - ["🙆🏾‍♂️"] = "man_gesturing_ok_tone4", - ["🙆🏿‍♂️"] = "man_gesturing_ok_tone5", - ["🙋"] = "person_raising_hand", - ["🙋🏻"] = "person_raising_hand_tone1", - ["🙋🏼"] = "person_raising_hand_tone2", - ["🙋🏽"] = "person_raising_hand_tone3", - ["🙋🏾"] = "person_raising_hand_tone4", - ["🙋🏿"] = "person_raising_hand_tone5", - ["🙋‍♀️"] = "woman_raising_hand", - ["🙋🏻‍♀️"] = "woman_raising_hand_tone1", - ["🙋🏼‍♀️"] = "woman_raising_hand_tone2", - ["🙋🏽‍♀️"] = "woman_raising_hand_tone3", - ["🙋🏾‍♀️"] = "woman_raising_hand_tone4", - ["🙋🏿‍♀️"] = "woman_raising_hand_tone5", - ["🙋‍♂️"] = "man_raising_hand", - ["🙋🏻‍♂️"] = "man_raising_hand_tone1", - ["🙋🏼‍♂️"] = "man_raising_hand_tone2", - ["🙋🏽‍♂️"] = "man_raising_hand_tone3", - ["🙋🏾‍♂️"] = "man_raising_hand_tone4", - ["🙋🏿‍♂️"] = "man_raising_hand_tone5", - ["🧏"] = "deaf_person", - ["🧏🏻"] = "deaf_person_tone1", - ["🧏🏼"] = "deaf_person_tone2", - ["🧏🏽"] = "deaf_person_tone3", - ["🧏🏾"] = "deaf_person_tone4", - ["🧏🏿"] = "deaf_person_tone5", - ["🧏‍♀️"] = "deaf_woman", - ["🧏🏻‍♀️"] = "deaf_woman_tone1", - ["🧏🏼‍♀️"] = "deaf_woman_tone2", - ["🧏🏽‍♀️"] = "deaf_woman_tone3", - ["🧏🏾‍♀️"] = "deaf_woman_tone4", - ["🧏🏿‍♀️"] = "deaf_woman_tone5", - ["🧏‍♂️"] = "deaf_man", - ["🧏🏻‍♂️"] = "deaf_man_tone1", - ["🧏🏼‍♂️"] = "deaf_man_tone2", - ["🧏🏽‍♂️"] = "deaf_man_tone3", - ["🧏🏾‍♂️"] = "deaf_man_tone4", - ["🧏🏿‍♂️"] = "deaf_man_tone5", - ["🤦"] = "person_facepalming", - ["🤦🏻"] = "person_facepalming_tone1", - ["🤦🏼"] = "person_facepalming_tone2", - ["🤦🏽"] = "person_facepalming_tone3", - ["🤦🏾"] = "person_facepalming_tone4", - ["🤦🏿"] = "person_facepalming_tone5", - ["🤦‍♀️"] = "woman_facepalming", - ["🤦🏻‍♀️"] = "woman_facepalming_tone1", - ["🤦🏼‍♀️"] = "woman_facepalming_tone2", - ["🤦🏽‍♀️"] = "woman_facepalming_tone3", - ["🤦🏾‍♀️"] = "woman_facepalming_tone4", - ["🤦🏿‍♀️"] = "woman_facepalming_tone5", - ["🤦‍♂️"] = "man_facepalming", - ["🤦🏻‍♂️"] = "man_facepalming_tone1", - ["🤦🏼‍♂️"] = "man_facepalming_tone2", - ["🤦🏽‍♂️"] = "man_facepalming_tone3", - ["🤦🏾‍♂️"] = "man_facepalming_tone4", - ["🤦🏿‍♂️"] = "man_facepalming_tone5", - ["🤷"] = "person_shrugging", - ["🤷🏻"] = "person_shrugging_tone1", - ["🤷🏼"] = "person_shrugging_tone2", - ["🤷🏽"] = "person_shrugging_tone3", - ["🤷🏾"] = "person_shrugging_tone4", - ["🤷🏿"] = "person_shrugging_tone5", - ["🤷‍♀️"] = "woman_shrugging", - ["🤷🏻‍♀️"] = "woman_shrugging_tone1", - ["🤷🏼‍♀️"] = "woman_shrugging_tone2", - ["🤷🏽‍♀️"] = "woman_shrugging_tone3", - ["🤷🏾‍♀️"] = "woman_shrugging_tone4", - ["🤷🏿‍♀️"] = "woman_shrugging_tone5", - ["🤷‍♂️"] = "man_shrugging", - ["🤷🏻‍♂️"] = "man_shrugging_tone1", - ["🤷🏼‍♂️"] = "man_shrugging_tone2", - ["🤷🏽‍♂️"] = "man_shrugging_tone3", - ["🤷🏾‍♂️"] = "man_shrugging_tone4", - ["🤷🏿‍♂️"] = "man_shrugging_tone5", - ["🙎"] = "person_pouting", - ["🙎🏻"] = "person_pouting_tone1", - ["🙎🏼"] = "person_pouting_tone2", - ["🙎🏽"] = "person_pouting_tone3", - ["🙎🏾"] = "person_pouting_tone4", - ["🙎🏿"] = "person_pouting_tone5", - ["🙎‍♀️"] = "woman_pouting", - ["🙎🏻‍♀️"] = "woman_pouting_tone1", - ["🙎🏼‍♀️"] = "woman_pouting_tone2", - ["🙎🏽‍♀️"] = "woman_pouting_tone3", - ["🙎🏾‍♀️"] = "woman_pouting_tone4", - ["🙎🏿‍♀️"] = "woman_pouting_tone5", - ["🙎‍♂️"] = "man_pouting", - ["🙎🏻‍♂️"] = "man_pouting_tone1", - ["🙎🏼‍♂️"] = "man_pouting_tone2", - ["🙎🏽‍♂️"] = "man_pouting_tone3", - ["🙎🏾‍♂️"] = "man_pouting_tone4", - ["🙎🏿‍♂️"] = "man_pouting_tone5", - ["🙍"] = "person_frowning", - ["🙍🏻"] = "person_frowning_tone1", - ["🙍🏼"] = "person_frowning_tone2", - ["🙍🏽"] = "person_frowning_tone3", - ["🙍🏾"] = "person_frowning_tone4", - ["🙍🏿"] = "person_frowning_tone5", - ["🙍‍♀️"] = "woman_frowning", - ["🙍🏻‍♀️"] = "woman_frowning_tone1", - ["🙍🏼‍♀️"] = "woman_frowning_tone2", - ["🙍🏽‍♀️"] = "woman_frowning_tone3", - ["🙍🏾‍♀️"] = "woman_frowning_tone4", - ["🙍🏿‍♀️"] = "woman_frowning_tone5", - ["🙍‍♂️"] = "man_frowning", - ["🙍🏻‍♂️"] = "man_frowning_tone1", - ["🙍🏼‍♂️"] = "man_frowning_tone2", - ["🙍🏽‍♂️"] = "man_frowning_tone3", - ["🙍🏾‍♂️"] = "man_frowning_tone4", - ["🙍🏿‍♂️"] = "man_frowning_tone5", - ["💇"] = "person_getting_haircut", - ["💇🏻"] = "person_getting_haircut_tone1", - ["💇🏼"] = "person_getting_haircut_tone2", - ["💇🏽"] = "person_getting_haircut_tone3", - ["💇🏾"] = "person_getting_haircut_tone4", - ["💇🏿"] = "person_getting_haircut_tone5", - ["💇‍♀️"] = "woman_getting_haircut", - ["💇🏻‍♀️"] = "woman_getting_haircut_tone1", - ["💇🏼‍♀️"] = "woman_getting_haircut_tone2", - ["💇🏽‍♀️"] = "woman_getting_haircut_tone3", - ["💇🏾‍♀️"] = "woman_getting_haircut_tone4", - ["💇🏿‍♀️"] = "woman_getting_haircut_tone5", - ["💇‍♂️"] = "man_getting_haircut", - ["💇🏻‍♂️"] = "man_getting_haircut_tone1", - ["💇🏼‍♂️"] = "man_getting_haircut_tone2", - ["💇🏽‍♂️"] = "man_getting_haircut_tone3", - ["💇🏾‍♂️"] = "man_getting_haircut_tone4", - ["💇🏿‍♂️"] = "man_getting_haircut_tone5", - ["💆"] = "person_getting_massage", - ["💆🏻"] = "person_getting_massage_tone1", - ["💆🏼"] = "person_getting_massage_tone2", - ["💆🏽"] = "person_getting_massage_tone3", - ["💆🏾"] = "person_getting_massage_tone4", - ["💆🏿"] = "person_getting_massage_tone5", - ["💆‍♀️"] = "woman_getting_face_massage", - ["💆🏻‍♀️"] = "woman_getting_face_massage_tone1", - ["💆🏼‍♀️"] = "woman_getting_face_massage_tone2", - ["💆🏽‍♀️"] = "woman_getting_face_massage_tone3", - ["💆🏾‍♀️"] = "woman_getting_face_massage_tone4", - ["💆🏿‍♀️"] = "woman_getting_face_massage_tone5", - ["💆‍♂️"] = "man_getting_face_massage", - ["💆🏻‍♂️"] = "man_getting_face_massage_tone1", - ["💆🏼‍♂️"] = "man_getting_face_massage_tone2", - ["💆🏽‍♂️"] = "man_getting_face_massage_tone3", - ["💆🏾‍♂️"] = "man_getting_face_massage_tone4", - ["💆🏿‍♂️"] = "man_getting_face_massage_tone5", - ["🧖"] = "person_in_steamy_room", - ["🧖🏻"] = "person_in_steamy_room_tone1", - ["🧖🏼"] = "person_in_steamy_room_tone2", - ["🧖🏽"] = "person_in_steamy_room_tone3", - ["🧖🏾"] = "person_in_steamy_room_tone4", - ["🧖🏿"] = "person_in_steamy_room_tone5", - ["🧖‍♀️"] = "woman_in_steamy_room", - ["🧖🏻‍♀️"] = "woman_in_steamy_room_tone1", - ["🧖🏼‍♀️"] = "woman_in_steamy_room_tone2", - ["🧖🏽‍♀️"] = "woman_in_steamy_room_tone3", - ["🧖🏾‍♀️"] = "woman_in_steamy_room_tone4", - ["🧖🏿‍♀️"] = "woman_in_steamy_room_tone5", - ["🧖‍♂️"] = "man_in_steamy_room", - ["🧖🏻‍♂️"] = "man_in_steamy_room_tone1", - ["🧖🏼‍♂️"] = "man_in_steamy_room_tone2", - ["🧖🏽‍♂️"] = "man_in_steamy_room_tone3", - ["🧖🏾‍♂️"] = "man_in_steamy_room_tone4", - ["🧖🏿‍♂️"] = "man_in_steamy_room_tone5", - ["💅"] = "nail_care", - ["💅🏻"] = "nail_care_tone1", - ["💅🏼"] = "nail_care_tone2", - ["💅🏽"] = "nail_care_tone3", - ["💅🏾"] = "nail_care_tone4", - ["💅🏿"] = "nail_care_tone5", - ["🤳"] = "selfie", - ["🤳🏻"] = "selfie_tone1", - ["🤳🏼"] = "selfie_tone2", - ["🤳🏽"] = "selfie_tone3", - ["🤳🏾"] = "selfie_tone4", - ["🤳🏿"] = "selfie_tone5", - ["💃"] = "dancer", - ["💃🏻"] = "dancer_tone1", - ["💃🏼"] = "dancer_tone2", - ["💃🏽"] = "dancer_tone3", - ["💃🏾"] = "dancer_tone4", - ["💃🏿"] = "dancer_tone5", - ["🕺"] = "man_dancing", - ["🕺🏻"] = "man_dancing_tone1", - ["🕺🏼"] = "man_dancing_tone2", - ["🕺🏽"] = "man_dancing_tone3", - ["🕺🏿"] = "man_dancing_tone5", - ["🕺🏾"] = "man_dancing_tone4", - ["👯"] = "people_with_bunny_ears_partying", - ["👯‍♀️"] = "women_with_bunny_ears_partying", - ["👯‍♂️"] = "men_with_bunny_ears_partying", - ["🕴️"] = "levitate", - ["🕴🏻"] = "levitate_tone1", - ["🕴🏼"] = "levitate_tone2", - ["🕴🏽"] = "levitate_tone3", - ["🕴🏾"] = "levitate_tone4", - ["🕴🏿"] = "levitate_tone5", - ["🧑‍🦽"] = "person_in_manual_wheelchair", - ["🧑🏻‍🦽"] = "person_in_manual_wheelchair_tone1", - ["🧑🏼‍🦽"] = "person_in_manual_wheelchair_tone2", - ["🧑🏽‍🦽"] = "person_in_manual_wheelchair_tone3", - ["🧑🏾‍🦽"] = "person_in_manual_wheelchair_tone4", - ["🧑🏿‍🦽"] = "person_in_manual_wheelchair_tone5", - ["👩‍🦽"] = "woman_in_manual_wheelchair", - ["👩🏻‍🦽"] = "woman_in_manual_wheelchair_tone1", - ["👩🏼‍🦽"] = "woman_in_manual_wheelchair_tone2", - ["👩🏽‍🦽"] = "woman_in_manual_wheelchair_tone3", - ["👩🏾‍🦽"] = "woman_in_manual_wheelchair_tone4", - ["👩🏿‍🦽"] = "woman_in_manual_wheelchair_tone5", - ["👨‍🦽"] = "man_in_manual_wheelchair", - ["👨🏻‍🦽"] = "man_in_manual_wheelchair_tone1", - ["👨🏼‍🦽"] = "man_in_manual_wheelchair_tone2", - ["👨🏽‍🦽"] = "man_in_manual_wheelchair_tone3", - ["👨🏾‍🦽"] = "man_in_manual_wheelchair_tone4", - ["👨🏿‍🦽"] = "man_in_manual_wheelchair_tone5", - ["🧑‍🦼"] = "person_in_motorized_wheelchair", - ["🧑🏻‍🦼"] = "person_in_motorized_wheelchair_tone1", - ["🧑🏼‍🦼"] = "person_in_motorized_wheelchair_tone2", - ["🧑🏽‍🦼"] = "person_in_motorized_wheelchair_tone3", - ["🧑🏾‍🦼"] = "person_in_motorized_wheelchair_tone4", - ["🧑🏿‍🦼"] = "person_in_motorized_wheelchair_tone5", - ["👩‍🦼"] = "woman_in_motorized_wheelchair", - ["👩🏻‍🦼"] = "woman_in_motorized_wheelchair_tone1", - ["👩🏼‍🦼"] = "woman_in_motorized_wheelchair_tone2", - ["👩🏽‍🦼"] = "woman_in_motorized_wheelchair_tone3", - ["👩🏾‍🦼"] = "woman_in_motorized_wheelchair_tone4", - ["👩🏿‍🦼"] = "woman_in_motorized_wheelchair_tone5", - ["👨‍🦼"] = "man_in_motorized_wheelchair", - ["👨🏻‍🦼"] = "man_in_motorized_wheelchair_tone1", - ["👨🏼‍🦼"] = "man_in_motorized_wheelchair_tone2", - ["👨🏽‍🦼"] = "man_in_motorized_wheelchair_tone3", - ["👨🏾‍🦼"] = "man_in_motorized_wheelchair_tone4", - ["👨🏿‍🦼"] = "man_in_motorized_wheelchair_tone5", - ["🚶"] = "person_walking", - ["🚶🏻"] = "person_walking_tone1", - ["🚶🏼"] = "person_walking_tone2", - ["🚶🏽"] = "person_walking_tone3", - ["🚶🏾"] = "person_walking_tone4", - ["🚶🏿"] = "person_walking_tone5", - ["🚶‍♀️"] = "woman_walking", - ["🚶🏻‍♀️"] = "woman_walking_tone1", - ["🚶🏼‍♀️"] = "woman_walking_tone2", - ["🚶🏽‍♀️"] = "woman_walking_tone3", - ["🚶🏾‍♀️"] = "woman_walking_tone4", - ["🚶🏿‍♀️"] = "woman_walking_tone5", - ["🚶‍♂️"] = "man_walking", - ["🚶🏻‍♂️"] = "man_walking_tone1", - ["🚶🏼‍♂️"] = "man_walking_tone2", - ["🚶🏽‍♂️"] = "man_walking_tone3", - ["🚶🏾‍♂️"] = "man_walking_tone4", - ["🚶🏿‍♂️"] = "man_walking_tone5", - ["🧑‍🦯"] = "person_with_probing_cane", - ["🧑🏻‍🦯"] = "person_with_probing_cane_tone1", - ["🧑🏼‍🦯"] = "person_with_probing_cane_tone2", - ["🧑🏽‍🦯"] = "person_with_probing_cane_tone3", - ["🧑🏾‍🦯"] = "person_with_probing_cane_tone4", - ["🧑🏿‍🦯"] = "person_with_probing_cane_tone5", - ["👩‍🦯"] = "woman_with_probing_cane", - ["👩🏻‍🦯"] = "woman_with_probing_cane_tone1", - ["👩🏼‍🦯"] = "woman_with_probing_cane_tone2", - ["👩🏽‍🦯"] = "woman_with_probing_cane_tone3", - ["👩🏾‍🦯"] = "woman_with_probing_cane_tone4", - ["👩🏿‍🦯"] = "woman_with_probing_cane_tone5", - ["👨‍🦯"] = "man_with_probing_cane", - ["👨🏻‍🦯"] = "man_with_probing_cane_tone1", - ["👨🏽‍🦯"] = "man_with_probing_cane_tone3", - ["👨🏼‍🦯"] = "man_with_probing_cane_tone2", - ["👨🏾‍🦯"] = "man_with_probing_cane_tone4", - ["👨🏿‍🦯"] = "man_with_probing_cane_tone5", - ["🧎"] = "person_kneeling", - ["🧎🏻"] = "person_kneeling_tone1", - ["🧎🏼"] = "person_kneeling_tone2", - ["🧎🏽"] = "person_kneeling_tone3", - ["🧎🏾"] = "person_kneeling_tone4", - ["🧎🏿"] = "person_kneeling_tone5", - ["🧎‍♀️"] = "woman_kneeling", - ["🧎🏻‍♀️"] = "woman_kneeling_tone1", - ["🧎🏼‍♀️"] = "woman_kneeling_tone2", - ["🧎🏽‍♀️"] = "woman_kneeling_tone3", - ["🧎🏾‍♀️"] = "woman_kneeling_tone4", - ["🧎🏿‍♀️"] = "woman_kneeling_tone5", - ["🧎‍♂️"] = "man_kneeling", - ["🧎🏻‍♂️"] = "man_kneeling_tone1", - ["🧎🏼‍♂️"] = "man_kneeling_tone2", - ["🧎🏽‍♂️"] = "man_kneeling_tone3", - ["🧎🏾‍♂️"] = "man_kneeling_tone4", - ["🧎🏿‍♂️"] = "man_kneeling_tone5", - ["🏃"] = "person_running", - ["🏃🏻"] = "person_running_tone1", - ["🏃🏼"] = "person_running_tone2", - ["🏃🏽"] = "person_running_tone3", - ["🏃🏾"] = "person_running_tone4", - ["🏃🏿"] = "person_running_tone5", - ["🏃‍♀️"] = "woman_running", - ["🏃🏻‍♀️"] = "woman_running_tone1", - ["🏃🏼‍♀️"] = "woman_running_tone2", - ["🏃🏽‍♀️"] = "woman_running_tone3", - ["🏃🏾‍♀️"] = "woman_running_tone4", - ["🏃🏿‍♀️"] = "woman_running_tone5", - ["🏃‍♂️"] = "man_running", - ["🏃🏻‍♂️"] = "man_running_tone1", - ["🏃🏼‍♂️"] = "man_running_tone2", - ["🏃🏽‍♂️"] = "man_running_tone3", - ["🏃🏾‍♂️"] = "man_running_tone4", - ["🏃🏿‍♂️"] = "man_running_tone5", - ["🧍"] = "person_standing", - ["🧍🏻"] = "person_standing_tone1", - ["🧍🏼"] = "person_standing_tone2", - ["🧍🏽"] = "person_standing_tone3", - ["🧍🏾"] = "person_standing_tone4", - ["🧍🏿"] = "person_standing_tone5", - ["🧍‍♀️"] = "woman_standing", - ["🧍🏻‍♀️"] = "woman_standing_tone1", - ["🧍🏼‍♀️"] = "woman_standing_tone2", - ["🧍🏽‍♀️"] = "woman_standing_tone3", - ["🧍🏾‍♀️"] = "woman_standing_tone4", - ["🧍🏿‍♀️"] = "woman_standing_tone5", - ["🧍‍♂️"] = "man_standing", - ["🧍🏻‍♂️"] = "man_standing_tone1", - ["🧍🏼‍♂️"] = "man_standing_tone2", - ["🧍🏽‍♂️"] = "man_standing_tone3", - ["🧍🏾‍♂️"] = "man_standing_tone4", - ["🧍🏿‍♂️"] = "man_standing_tone5", - ["🧑‍🤝‍🧑"] = "people_holding_hands", - ["🧑🏻‍🤝‍🧑🏻"] = "people_holding_hands_tone1", - ["🧑🏻‍🤝‍🧑🏼"] = "people_holding_hands_tone1_tone2", - ["🧑🏻‍🤝‍🧑🏽"] = "people_holding_hands_tone1_tone3", - ["🧑🏻‍🤝‍🧑🏾"] = "people_holding_hands_tone1_tone4", - ["🧑🏻‍🤝‍🧑🏿"] = "people_holding_hands_tone1_tone5", - ["🧑🏼‍🤝‍🧑🏻"] = "people_holding_hands_tone2_tone1", - ["🧑🏼‍🤝‍🧑🏼"] = "people_holding_hands_tone2", - ["🧑🏼‍🤝‍🧑🏽"] = "people_holding_hands_tone2_tone3", - ["🧑🏼‍🤝‍🧑🏾"] = "people_holding_hands_tone2_tone4", - ["🧑🏼‍🤝‍🧑🏿"] = "people_holding_hands_tone2_tone5", - ["🧑🏽‍🤝‍🧑🏻"] = "people_holding_hands_tone3_tone1", - ["🧑🏽‍🤝‍🧑🏼"] = "people_holding_hands_tone3_tone2", - ["🧑🏽‍🤝‍🧑🏽"] = "people_holding_hands_tone3", - ["🧑🏽‍🤝‍🧑🏾"] = "people_holding_hands_tone3_tone4", - ["🧑🏽‍🤝‍🧑🏿"] = "people_holding_hands_tone3_tone5", - ["🧑🏾‍🤝‍🧑🏻"] = "people_holding_hands_tone4_tone1", - ["🧑🏾‍🤝‍🧑🏼"] = "people_holding_hands_tone4_tone2", - ["🧑🏾‍🤝‍🧑🏽"] = "people_holding_hands_tone4_tone3", - ["🧑🏾‍🤝‍🧑🏾"] = "people_holding_hands_tone4", - ["🧑🏾‍🤝‍🧑🏿"] = "people_holding_hands_tone4_tone5", - ["🧑🏿‍🤝‍🧑🏻"] = "people_holding_hands_tone5_tone1", - ["🧑🏿‍🤝‍🧑🏼"] = "people_holding_hands_tone5_tone2", - ["🧑🏿‍🤝‍🧑🏽"] = "people_holding_hands_tone5_tone3", - ["🧑🏿‍🤝‍🧑🏾"] = "people_holding_hands_tone5_tone4", - ["🧑🏿‍🤝‍🧑🏿"] = "people_holding_hands_tone5", - ["👫"] = "couple", - ["👫🏻"] = "woman_and_man_holding_hands_tone1", - ["👩🏻‍🤝‍👨🏼"] = "woman_and_man_holding_hands_tone1_tone2", - ["👩🏻‍🤝‍👨🏽"] = "woman_and_man_holding_hands_tone1_tone3", - ["👩🏻‍🤝‍👨🏾"] = "woman_and_man_holding_hands_tone1_tone4", - ["👩🏻‍🤝‍👨🏿"] = "woman_and_man_holding_hands_tone1_tone5", - ["👩🏼‍🤝‍👨🏻"] = "woman_and_man_holding_hands_tone2_tone1", - ["👫🏼"] = "woman_and_man_holding_hands_tone2", - ["👩🏼‍🤝‍👨🏽"] = "woman_and_man_holding_hands_tone2_tone3", - ["👩🏼‍🤝‍👨🏾"] = "woman_and_man_holding_hands_tone2_tone4", - ["👩🏼‍🤝‍👨🏿"] = "woman_and_man_holding_hands_tone2_tone5", - ["👩🏽‍🤝‍👨🏻"] = "woman_and_man_holding_hands_tone3_tone1", - ["👩🏽‍🤝‍👨🏼"] = "woman_and_man_holding_hands_tone3_tone2", - ["👫🏽"] = "woman_and_man_holding_hands_tone3", - ["👩🏽‍🤝‍👨🏾"] = "woman_and_man_holding_hands_tone3_tone4", - ["👩🏽‍🤝‍👨🏿"] = "woman_and_man_holding_hands_tone3_tone5", - ["👩🏾‍🤝‍👨🏻"] = "woman_and_man_holding_hands_tone4_tone1", - ["👩🏾‍🤝‍👨🏼"] = "woman_and_man_holding_hands_tone4_tone2", - ["👩🏾‍🤝‍👨🏽"] = "woman_and_man_holding_hands_tone4_tone3", - ["👫🏾"] = "woman_and_man_holding_hands_tone4", - ["👩🏾‍🤝‍👨🏿"] = "woman_and_man_holding_hands_tone4_tone5", - ["👩🏿‍🤝‍👨🏻"] = "woman_and_man_holding_hands_tone5_tone1", - ["👩🏿‍🤝‍👨🏼"] = "woman_and_man_holding_hands_tone5_tone2", - ["👩🏿‍🤝‍👨🏽"] = "woman_and_man_holding_hands_tone5_tone3", - ["👩🏿‍🤝‍👨🏾"] = "woman_and_man_holding_hands_tone5_tone4", - ["👫🏿"] = "woman_and_man_holding_hands_tone5", - ["👭"] = "two_women_holding_hands", - ["👭🏻"] = "women_holding_hands_tone1", - ["👩🏻‍🤝‍👩🏼"] = "women_holding_hands_tone1_tone2", - ["👩🏻‍🤝‍👩🏽"] = "women_holding_hands_tone1_tone3", - ["👩🏻‍🤝‍👩🏾"] = "women_holding_hands_tone1_tone4", - ["👩🏻‍🤝‍👩🏿"] = "women_holding_hands_tone1_tone5", - ["👩🏼‍🤝‍👩🏻"] = "women_holding_hands_tone2_tone1", - ["👭🏼"] = "women_holding_hands_tone2", - ["👩🏼‍🤝‍👩🏽"] = "women_holding_hands_tone2_tone3", - ["👩🏼‍🤝‍👩🏾"] = "women_holding_hands_tone2_tone4", - ["👩🏼‍🤝‍👩🏿"] = "women_holding_hands_tone2_tone5", - ["👩🏽‍🤝‍👩🏻"] = "women_holding_hands_tone3_tone1", - ["👩🏽‍🤝‍👩🏼"] = "women_holding_hands_tone3_tone2", - ["👭🏽"] = "women_holding_hands_tone3", - ["👩🏽‍🤝‍👩🏾"] = "women_holding_hands_tone3_tone4", - ["👩🏽‍🤝‍👩🏿"] = "women_holding_hands_tone3_tone5", - ["👩🏾‍🤝‍👩🏻"] = "women_holding_hands_tone4_tone1", - ["👩🏾‍🤝‍👩🏼"] = "women_holding_hands_tone4_tone2", - ["👩🏾‍🤝‍👩🏽"] = "women_holding_hands_tone4_tone3", - ["👭🏾"] = "women_holding_hands_tone4", - ["👩🏾‍🤝‍👩🏿"] = "women_holding_hands_tone4_tone5", - ["👩🏿‍🤝‍👩🏻"] = "women_holding_hands_tone5_tone1", - ["👩🏿‍🤝‍👩🏼"] = "women_holding_hands_tone5_tone2", - ["👩🏿‍🤝‍👩🏽"] = "women_holding_hands_tone5_tone3", - ["👩🏿‍🤝‍👩🏾"] = "women_holding_hands_tone5_tone4", - ["👭🏿"] = "women_holding_hands_tone5", - ["👬"] = "two_men_holding_hands", - ["👬🏻"] = "men_holding_hands_tone1", - ["👨🏻‍🤝‍👨🏼"] = "men_holding_hands_tone1_tone2", - ["👨🏻‍🤝‍👨🏽"] = "men_holding_hands_tone1_tone3", - ["👨🏻‍🤝‍👨🏾"] = "men_holding_hands_tone1_tone4", - ["👨🏻‍🤝‍👨🏿"] = "men_holding_hands_tone1_tone5", - ["👨🏼‍🤝‍👨🏻"] = "men_holding_hands_tone2_tone1", - ["👬🏼"] = "men_holding_hands_tone2", - ["👨🏼‍🤝‍👨🏽"] = "men_holding_hands_tone2_tone3", - ["👨🏼‍🤝‍👨🏾"] = "men_holding_hands_tone2_tone4", - ["👨🏼‍🤝‍👨🏿"] = "men_holding_hands_tone2_tone5", - ["👨🏽‍🤝‍👨🏻"] = "men_holding_hands_tone3_tone1", - ["👨🏽‍🤝‍👨🏼"] = "men_holding_hands_tone3_tone2", - ["👬🏽"] = "men_holding_hands_tone3", - ["👨🏽‍🤝‍👨🏾"] = "men_holding_hands_tone3_tone4", - ["👨🏽‍🤝‍👨🏿"] = "men_holding_hands_tone3_tone5", - ["👨🏾‍🤝‍👨🏻"] = "men_holding_hands_tone4_tone1", - ["👨🏾‍🤝‍👨🏼"] = "men_holding_hands_tone4_tone2", - ["👨🏾‍🤝‍👨🏽"] = "men_holding_hands_tone4_tone3", - ["👬🏾"] = "men_holding_hands_tone4", - ["👨🏾‍🤝‍👨🏿"] = "men_holding_hands_tone4_tone5", - ["👨🏿‍🤝‍👨🏻"] = "men_holding_hands_tone5_tone1", - ["👨🏿‍🤝‍👨🏼"] = "men_holding_hands_tone5_tone2", - ["👨🏿‍🤝‍👨🏽"] = "men_holding_hands_tone5_tone3", - ["👨🏿‍🤝‍👨🏾"] = "men_holding_hands_tone5_tone4", - ["👬🏿"] = "men_holding_hands_tone5", - ["💑"] = "couple_with_heart", - ["💑🏻"] = "couple_with_heart_tone1", - ["🧑🏻‍❤️‍🧑🏼"] = "couple_with_heart_person_person_tone1_tone2", - ["🧑🏻‍❤️‍🧑🏽"] = "couple_with_heart_person_person_tone1_tone3", - ["🧑🏻‍❤️‍🧑🏾"] = "couple_with_heart_person_person_tone1_tone4", - ["🧑🏻‍❤️‍🧑🏿"] = "couple_with_heart_person_person_tone1_tone5", - ["🧑🏼‍❤️‍🧑🏻"] = "couple_with_heart_person_person_tone2_tone1", - ["💑🏼"] = "couple_with_heart_tone2", - ["🧑🏼‍❤️‍🧑🏽"] = "couple_with_heart_person_person_tone2_tone3", - ["🧑🏼‍❤️‍🧑🏾"] = "couple_with_heart_person_person_tone2_tone4", - ["🧑🏼‍❤️‍🧑🏿"] = "couple_with_heart_person_person_tone2_tone5", - ["🧑🏽‍❤️‍🧑🏻"] = "couple_with_heart_person_person_tone3_tone1", - ["🧑🏽‍❤️‍🧑🏼"] = "couple_with_heart_person_person_tone3_tone2", - ["💑🏽"] = "couple_with_heart_tone3", - ["🧑🏽‍❤️‍🧑🏾"] = "couple_with_heart_person_person_tone3_tone4", - ["🧑🏽‍❤️‍🧑🏿"] = "couple_with_heart_person_person_tone3_tone5", - ["🧑🏾‍❤️‍🧑🏻"] = "couple_with_heart_person_person_tone4_tone1", - ["🧑🏾‍❤️‍🧑🏼"] = "couple_with_heart_person_person_tone4_tone2", - ["🧑🏾‍❤️‍🧑🏽"] = "couple_with_heart_person_person_tone4_tone3", - ["💑🏾"] = "couple_with_heart_tone4", - ["🧑🏾‍❤️‍🧑🏿"] = "couple_with_heart_person_person_tone4_tone5", - ["🧑🏿‍❤️‍🧑🏻"] = "couple_with_heart_person_person_tone5_tone1", - ["🧑🏿‍❤️‍🧑🏼"] = "couple_with_heart_person_person_tone5_tone2", - ["🧑🏿‍❤️‍🧑🏽"] = "couple_with_heart_person_person_tone5_tone3", - ["🧑🏿‍❤️‍🧑🏾"] = "couple_with_heart_person_person_tone5_tone4", - ["💑🏿"] = "couple_with_heart_tone5", - ["👩‍❤️‍👨"] = "couple_with_heart_woman_man", - ["👩🏻‍❤️‍👨🏻"] = "couple_with_heart_woman_man_tone1", - ["👩🏻‍❤️‍👨🏼"] = "couple_with_heart_woman_man_tone1_tone2", - ["👩🏻‍❤️‍👨🏽"] = "couple_with_heart_woman_man_tone1_tone3", - ["👩🏻‍❤️‍👨🏾"] = "couple_with_heart_woman_man_tone1_tone4", - ["👩🏻‍❤️‍👨🏿"] = "couple_with_heart_woman_man_tone1_tone5", - ["👩🏼‍❤️‍👨🏻"] = "couple_with_heart_woman_man_tone2_tone1", - ["👩🏼‍❤️‍👨🏼"] = "couple_with_heart_woman_man_tone2", - ["👩🏼‍❤️‍👨🏽"] = "couple_with_heart_woman_man_tone2_tone3", - ["👩🏼‍❤️‍👨🏾"] = "couple_with_heart_woman_man_tone2_tone4", - ["👩🏼‍❤️‍👨🏿"] = "couple_with_heart_woman_man_tone2_tone5", - ["👩🏽‍❤️‍👨🏻"] = "couple_with_heart_woman_man_tone3_tone1", - ["👩🏽‍❤️‍👨🏼"] = "couple_with_heart_woman_man_tone3_tone2", - ["👩🏽‍❤️‍👨🏽"] = "couple_with_heart_woman_man_tone3", - ["👩🏽‍❤️‍👨🏾"] = "couple_with_heart_woman_man_tone3_tone4", - ["👩🏽‍❤️‍👨🏿"] = "couple_with_heart_woman_man_tone3_tone5", - ["👩🏾‍❤️‍👨🏻"] = "couple_with_heart_woman_man_tone4_tone1", - ["👩🏾‍❤️‍👨🏼"] = "couple_with_heart_woman_man_tone4_tone2", - ["👩🏾‍❤️‍👨🏽"] = "couple_with_heart_woman_man_tone4_tone3", - ["👩🏾‍❤️‍👨🏾"] = "couple_with_heart_woman_man_tone4", - ["👩🏾‍❤️‍👨🏿"] = "couple_with_heart_woman_man_tone4_tone5", - ["👩🏿‍❤️‍👨🏻"] = "couple_with_heart_woman_man_tone5_tone1", - ["👩🏿‍❤️‍👨🏼"] = "couple_with_heart_woman_man_tone5_tone2", - ["👩🏿‍❤️‍👨🏽"] = "couple_with_heart_woman_man_tone5_tone3", - ["👩🏿‍❤️‍👨🏾"] = "couple_with_heart_woman_man_tone5_tone4", - ["👩🏿‍❤️‍👨🏿"] = "couple_with_heart_woman_man_tone5", - ["👩‍❤️‍👩"] = "couple_ww", - ["👩🏻‍❤️‍👩🏻"] = "couple_with_heart_woman_woman_tone1", - ["👩🏻‍❤️‍👩🏼"] = "couple_with_heart_woman_woman_tone1_tone2", - ["👩🏻‍❤️‍👩🏽"] = "couple_with_heart_woman_woman_tone1_tone3", - ["👩🏻‍❤️‍👩🏾"] = "couple_with_heart_woman_woman_tone1_tone4", - ["👩🏻‍❤️‍👩🏿"] = "couple_with_heart_woman_woman_tone1_tone5", - ["👩🏼‍❤️‍👩🏻"] = "couple_with_heart_woman_woman_tone2_tone1", - ["👩🏼‍❤️‍👩🏼"] = "couple_with_heart_woman_woman_tone2", - ["👩🏼‍❤️‍👩🏽"] = "couple_with_heart_woman_woman_tone2_tone3", - ["👩🏼‍❤️‍👩🏾"] = "couple_with_heart_woman_woman_tone2_tone4", - ["👩🏼‍❤️‍👩🏿"] = "couple_with_heart_woman_woman_tone2_tone5", - ["👩🏽‍❤️‍👩🏻"] = "couple_with_heart_woman_woman_tone3_tone1", - ["👩🏽‍❤️‍👩🏼"] = "couple_with_heart_woman_woman_tone3_tone2", - ["👩🏽‍❤️‍👩🏽"] = "couple_with_heart_woman_woman_tone3", - ["👩🏽‍❤️‍👩🏾"] = "couple_with_heart_woman_woman_tone3_tone4", - ["👩🏽‍❤️‍👩🏿"] = "couple_with_heart_woman_woman_tone3_tone5", - ["👩🏾‍❤️‍👩🏻"] = "couple_with_heart_woman_woman_tone4_tone1", - ["👩🏾‍❤️‍👩🏼"] = "couple_with_heart_woman_woman_tone4_tone2", - ["👩🏾‍❤️‍👩🏽"] = "couple_with_heart_woman_woman_tone4_tone3", - ["👩🏾‍❤️‍👩🏾"] = "couple_with_heart_woman_woman_tone4", - ["👩🏾‍❤️‍👩🏿"] = "couple_with_heart_woman_woman_tone4_tone5", - ["👩🏿‍❤️‍👩🏻"] = "couple_with_heart_woman_woman_tone5_tone1", - ["👩🏿‍❤️‍👩🏼"] = "couple_with_heart_woman_woman_tone5_tone2", - ["👩🏿‍❤️‍👩🏽"] = "couple_with_heart_woman_woman_tone5_tone3", - ["👩🏿‍❤️‍👩🏾"] = "couple_with_heart_woman_woman_tone5_tone4", - ["👩🏿‍❤️‍👩🏿"] = "couple_with_heart_woman_woman_tone5", - ["👨‍❤️‍👨"] = "couple_mm", - ["👨🏻‍❤️‍👨🏻"] = "couple_with_heart_man_man_tone1", - ["👨🏻‍❤️‍👨🏼"] = "couple_with_heart_man_man_tone1_tone2", - ["👨🏻‍❤️‍👨🏽"] = "couple_with_heart_man_man_tone1_tone3", - ["👨🏻‍❤️‍👨🏾"] = "couple_with_heart_man_man_tone1_tone4", - ["👨🏻‍❤️‍👨🏿"] = "couple_with_heart_man_man_tone1_tone5", - ["👨🏼‍❤️‍👨🏻"] = "couple_with_heart_man_man_tone2_tone1", - ["👨🏼‍❤️‍👨🏼"] = "couple_with_heart_man_man_tone2", - ["👨🏼‍❤️‍👨🏽"] = "couple_with_heart_man_man_tone2_tone3", - ["👨🏼‍❤️‍👨🏾"] = "couple_with_heart_man_man_tone2_tone4", - ["👨🏼‍❤️‍👨🏿"] = "couple_with_heart_man_man_tone2_tone5", - ["👨🏽‍❤️‍👨🏻"] = "couple_with_heart_man_man_tone3_tone1", - ["👨🏽‍❤️‍👨🏼"] = "couple_with_heart_man_man_tone3_tone2", - ["👨🏽‍❤️‍👨🏽"] = "couple_with_heart_man_man_tone3", - ["👨🏽‍❤️‍👨🏾"] = "couple_with_heart_man_man_tone3_tone4", - ["👨🏽‍❤️‍👨🏿"] = "couple_with_heart_man_man_tone3_tone5", - ["👨🏾‍❤️‍👨🏻"] = "couple_with_heart_man_man_tone4_tone1", - ["👨🏾‍❤️‍👨🏼"] = "couple_with_heart_man_man_tone4_tone2", - ["👨🏾‍❤️‍👨🏽"] = "couple_with_heart_man_man_tone4_tone3", - ["👨🏾‍❤️‍👨🏾"] = "couple_with_heart_man_man_tone4", - ["👨🏾‍❤️‍👨🏿"] = "couple_with_heart_man_man_tone4_tone5", - ["👨🏿‍❤️‍👨🏻"] = "couple_with_heart_man_man_tone5_tone1", - ["👨🏿‍❤️‍👨🏼"] = "couple_with_heart_man_man_tone5_tone2", - ["👨🏿‍❤️‍👨🏽"] = "couple_with_heart_man_man_tone5_tone3", - ["👨🏿‍❤️‍👨🏾"] = "couple_with_heart_man_man_tone5_tone4", - ["👨🏿‍❤️‍👨🏿"] = "couple_with_heart_man_man_tone5", - ["💏"] = "couplekiss", - ["🧑🏿‍❤️‍💋‍🧑🏾"] = "kiss_person_person_tone5_tone4", - ["💏🏻"] = "kiss_tone1", - ["🧑🏻‍❤️‍💋‍🧑🏼"] = "kiss_person_person_tone1_tone2", - ["🧑🏻‍❤️‍💋‍🧑🏽"] = "kiss_person_person_tone1_tone3", - ["🧑🏻‍❤️‍💋‍🧑🏾"] = "kiss_person_person_tone1_tone4", - ["🧑🏻‍❤️‍💋‍🧑🏿"] = "kiss_person_person_tone1_tone5", - ["🧑🏼‍❤️‍💋‍🧑🏻"] = "kiss_person_person_tone2_tone1", - ["💏🏼"] = "kiss_tone2", - ["🧑🏼‍❤️‍💋‍🧑🏽"] = "kiss_person_person_tone2_tone3", - ["🧑🏼‍❤️‍💋‍🧑🏾"] = "kiss_person_person_tone2_tone4", - ["🧑🏼‍❤️‍💋‍🧑🏿"] = "kiss_person_person_tone2_tone5", - ["🧑🏽‍❤️‍💋‍🧑🏻"] = "kiss_person_person_tone3_tone1", - ["🧑🏽‍❤️‍💋‍🧑🏼"] = "kiss_person_person_tone3_tone2", - ["💏🏽"] = "kiss_tone3", - ["🧑🏽‍❤️‍💋‍🧑🏾"] = "kiss_person_person_tone3_tone4", - ["🧑🏽‍❤️‍💋‍🧑🏿"] = "kiss_person_person_tone3_tone5", - ["🧑🏾‍❤️‍💋‍🧑🏻"] = "kiss_person_person_tone4_tone1", - ["🧑🏾‍❤️‍💋‍🧑🏼"] = "kiss_person_person_tone4_tone2", - ["🧑🏾‍❤️‍💋‍🧑🏽"] = "kiss_person_person_tone4_tone3", - ["💏🏾"] = "kiss_tone4", - ["🧑🏾‍❤️‍💋‍🧑🏿"] = "kiss_person_person_tone4_tone5", - ["🧑🏿‍❤️‍💋‍🧑🏻"] = "kiss_person_person_tone5_tone1", - ["🧑🏿‍❤️‍💋‍🧑🏼"] = "kiss_person_person_tone5_tone2", - ["🧑🏿‍❤️‍💋‍🧑🏽"] = "kiss_person_person_tone5_tone3", - ["💏🏿"] = "kiss_tone5", - ["👩‍❤️‍💋‍👨"] = "kiss_woman_man", - ["👩🏻‍❤️‍💋‍👨🏻"] = "kiss_woman_man_tone1", - ["👩🏻‍❤️‍💋‍👨🏼"] = "kiss_woman_man_tone1_tone2", - ["👩🏻‍❤️‍💋‍👨🏽"] = "kiss_woman_man_tone1_tone3", - ["👩🏻‍❤️‍💋‍👨🏾"] = "kiss_woman_man_tone1_tone4", - ["👩🏻‍❤️‍💋‍👨🏿"] = "kiss_woman_man_tone1_tone5", - ["👩🏼‍❤️‍💋‍👨🏻"] = "kiss_woman_man_tone2_tone1", - ["👩🏼‍❤️‍💋‍👨🏼"] = "kiss_woman_man_tone2", - ["👩🏼‍❤️‍💋‍👨🏽"] = "kiss_woman_man_tone2_tone3", - ["👩🏼‍❤️‍💋‍👨🏾"] = "kiss_woman_man_tone2_tone4", - ["👩🏼‍❤️‍💋‍👨🏿"] = "kiss_woman_man_tone2_tone5", - ["👩🏽‍❤️‍💋‍👨🏻"] = "kiss_woman_man_tone3_tone1", - ["👩🏽‍❤️‍💋‍👨🏼"] = "kiss_woman_man_tone3_tone2", - ["👩🏽‍❤️‍💋‍👨🏽"] = "kiss_woman_man_tone3", - ["👩🏽‍❤️‍💋‍👨🏾"] = "kiss_woman_man_tone3_tone4", - ["👩🏽‍❤️‍💋‍👨🏿"] = "kiss_woman_man_tone3_tone5", - ["👩🏾‍❤️‍💋‍👨🏻"] = "kiss_woman_man_tone4_tone1", - ["👩🏾‍❤️‍💋‍👨🏼"] = "kiss_woman_man_tone4_tone2", - ["👩🏾‍❤️‍💋‍👨🏽"] = "kiss_woman_man_tone4_tone3", - ["👩🏾‍❤️‍💋‍👨🏾"] = "kiss_woman_man_tone4", - ["👩🏾‍❤️‍💋‍👨🏿"] = "kiss_woman_man_tone4_tone5", - ["👩🏿‍❤️‍💋‍👨🏻"] = "kiss_woman_man_tone5_tone1", - ["👩🏿‍❤️‍💋‍👨🏼"] = "kiss_woman_man_tone5_tone2", - ["👩🏿‍❤️‍💋‍👨🏽"] = "kiss_woman_man_tone5_tone3", - ["👩🏿‍❤️‍💋‍👨🏾"] = "kiss_woman_man_tone5_tone4", - ["👩🏿‍❤️‍💋‍👨🏿"] = "kiss_woman_man_tone5", - ["👩‍❤️‍💋‍👩"] = "kiss_ww", - ["👩🏻‍❤️‍💋‍👩🏻"] = "kiss_woman_woman_tone1", - ["👩🏻‍❤️‍💋‍👩🏼"] = "kiss_woman_woman_tone1_tone2", - ["👩🏻‍❤️‍💋‍👩🏽"] = "kiss_woman_woman_tone1_tone3", - ["👩🏻‍❤️‍💋‍👩🏾"] = "kiss_woman_woman_tone1_tone4", - ["👩🏻‍❤️‍💋‍👩🏿"] = "kiss_woman_woman_tone1_tone5", - ["👩🏼‍❤️‍💋‍👩🏻"] = "kiss_woman_woman_tone2_tone1", - ["👩🏼‍❤️‍💋‍👩🏼"] = "kiss_woman_woman_tone2", - ["👩🏼‍❤️‍💋‍👩🏽"] = "kiss_woman_woman_tone2_tone3", - ["👩🏼‍❤️‍💋‍👩🏾"] = "kiss_woman_woman_tone2_tone4", - ["👩🏼‍❤️‍💋‍👩🏿"] = "kiss_woman_woman_tone2_tone5", - ["👩🏽‍❤️‍💋‍👩🏻"] = "kiss_woman_woman_tone3_tone1", - ["👩🏽‍❤️‍💋‍👩🏼"] = "kiss_woman_woman_tone3_tone2", - ["👩🏽‍❤️‍💋‍👩🏽"] = "kiss_woman_woman_tone3", - ["👩🏽‍❤️‍💋‍👩🏾"] = "kiss_woman_woman_tone3_tone4", - ["👩🏽‍❤️‍💋‍👩🏿"] = "kiss_woman_woman_tone3_tone5", - ["👩🏾‍❤️‍💋‍👩🏻"] = "kiss_woman_woman_tone4_tone1", - ["👩🏾‍❤️‍💋‍👩🏼"] = "kiss_woman_woman_tone4_tone2", - ["👩🏾‍❤️‍💋‍👩🏽"] = "kiss_woman_woman_tone4_tone3", - ["👩🏾‍❤️‍💋‍👩🏾"] = "kiss_woman_woman_tone4", - ["👩🏾‍❤️‍💋‍👩🏿"] = "kiss_woman_woman_tone4_tone5", - ["👩🏿‍❤️‍💋‍👩🏻"] = "kiss_woman_woman_tone5_tone1", - ["👩🏿‍❤️‍💋‍👩🏼"] = "kiss_woman_woman_tone5_tone2", - ["👩🏿‍❤️‍💋‍👩🏽"] = "kiss_woman_woman_tone5_tone3", - ["👩🏿‍❤️‍💋‍👩🏾"] = "kiss_woman_woman_tone5_tone4", - ["👩🏿‍❤️‍💋‍👩🏿"] = "kiss_woman_woman_tone5", - ["👨‍❤️‍💋‍👨"] = "kiss_mm", - ["👨🏻‍❤️‍💋‍👨🏻"] = "kiss_man_man_tone1", - ["👨🏻‍❤️‍💋‍👨🏼"] = "kiss_man_man_tone1_tone2", - ["👨🏻‍❤️‍💋‍👨🏽"] = "kiss_man_man_tone1_tone3", - ["👨🏻‍❤️‍💋‍👨🏾"] = "kiss_man_man_tone1_tone4", - ["👨🏻‍❤️‍💋‍👨🏿"] = "kiss_man_man_tone1_tone5", - ["👨🏼‍❤️‍💋‍👨🏻"] = "kiss_man_man_tone2_tone1", - ["👨🏼‍❤️‍💋‍👨🏼"] = "kiss_man_man_tone2", - ["👨🏼‍❤️‍💋‍👨🏽"] = "kiss_man_man_tone2_tone3", - ["👨🏼‍❤️‍💋‍👨🏾"] = "kiss_man_man_tone2_tone4", - ["👨🏼‍❤️‍💋‍👨🏿"] = "kiss_man_man_tone2_tone5", - ["👨🏽‍❤️‍💋‍👨🏻"] = "kiss_man_man_tone3_tone1", - ["👨🏽‍❤️‍💋‍👨🏼"] = "kiss_man_man_tone3_tone2", - ["👨🏽‍❤️‍💋‍👨🏽"] = "kiss_man_man_tone3", - ["👨🏽‍❤️‍💋‍👨🏾"] = "kiss_man_man_tone3_tone4", - ["👨🏽‍❤️‍💋‍👨🏿"] = "kiss_man_man_tone3_tone5", - ["👨🏾‍❤️‍💋‍👨🏻"] = "kiss_man_man_tone4_tone1", - ["👨🏾‍❤️‍💋‍👨🏼"] = "kiss_man_man_tone4_tone2", - ["👨🏾‍❤️‍💋‍👨🏽"] = "kiss_man_man_tone4_tone3", - ["👨🏾‍❤️‍💋‍👨🏾"] = "kiss_man_man_tone4", - ["👨🏾‍❤️‍💋‍👨🏿"] = "kiss_man_man_tone4_tone5", - ["👨🏿‍❤️‍💋‍👨🏻"] = "kiss_man_man_tone5_tone1", - ["👨🏿‍❤️‍💋‍👨🏼"] = "kiss_man_man_tone5_tone2", - ["👨🏿‍❤️‍💋‍👨🏽"] = "kiss_man_man_tone5_tone3", - ["👨🏿‍❤️‍💋‍👨🏾"] = "kiss_man_man_tone5_tone4", - ["👨🏿‍❤️‍💋‍👨🏿"] = "kiss_man_man_tone5", - ["👪"] = "family", - ["👨‍👩‍👦"] = "family_man_woman_boy", - ["👨‍👩‍👧"] = "family_mwg", - ["👨‍👩‍👧‍👦"] = "family_mwgb", - ["👨‍👩‍👦‍👦"] = "family_mwbb", - ["👨‍👩‍👧‍👧"] = "family_mwgg", - ["👩‍👩‍👦"] = "family_wwb", - ["👩‍👩‍👧"] = "family_wwg", - ["👩‍👩‍👧‍👦"] = "family_wwgb", - ["👩‍👩‍👦‍👦"] = "family_wwbb", - ["👩‍👩‍👧‍👧"] = "family_wwgg", - ["👨‍👨‍👦"] = "family_mmb", - ["👨‍👨‍👧"] = "family_mmg", - ["👨‍👨‍👧‍👦"] = "family_mmgb", - ["👨‍👨‍👦‍👦"] = "family_mmbb", - ["👨‍👨‍👧‍👧"] = "family_mmgg", - ["👩‍👦"] = "family_woman_boy", - ["👩‍👧"] = "family_woman_girl", - ["👩‍👧‍👦"] = "family_woman_girl_boy", - ["👩‍👦‍👦"] = "family_woman_boy_boy", - ["👩‍👧‍👧"] = "family_woman_girl_girl", - ["👨‍👦"] = "family_man_boy", - ["👨‍👧"] = "family_man_girl", - ["👨‍👧‍👦"] = "family_man_girl_boy", - ["👨‍👦‍👦"] = "family_man_boy_boy", - ["👨‍👧‍👧"] = "family_man_girl_girl", - ["🧶"] = "yarn", - ["🧵"] = "thread", - ["🧥"] = "coat", - ["🥼"] = "lab_coat", - ["🦺"] = "safety_vest", - ["👚"] = "womans_clothes", - ["👕"] = "shirt", - ["👖"] = "jeans", - ["🩲"] = "briefs", - ["🩳"] = "shorts", - ["👔"] = "necktie", - ["👗"] = "dress", - ["👙"] = "bikini", - ["🩱"] = "one_piece_swimsuit", - ["👘"] = "kimono", - ["🥻"] = "sari", - ["🥿"] = "womans_flat_shoe", - ["👠"] = "high_heel", - ["👡"] = "sandal", - ["👢"] = "boot", - ["👞"] = "mans_shoe", - ["👟"] = "athletic_shoe", - ["🥾"] = "hiking_boot", - ["🩴"] = "thong_sandal", - ["🧦"] = "socks", - ["🧤"] = "gloves", - ["🧣"] = "scarf", - ["🎩"] = "tophat", - ["🧢"] = "billed_cap", - ["👒"] = "womans_hat", - ["🎓"] = "mortar_board", - ["⛑️"] = "helmet_with_cross", - ["🪖"] = "military_helmet", - ["👑"] = "crown", - ["💍"] = "ring", - ["👝"] = "pouch", - ["👛"] = "purse", - ["👜"] = "handbag", - ["💼"] = "briefcase", - ["🎒"] = "school_satchel", - ["🧳"] = "luggage", - ["👓"] = "eyeglasses", - ["🕶️"] = "dark_sunglasses", - ["🥽"] = "goggles", - ["🌂"] = "closed_umbrella", - ["🐶"] = "dog", - ["🐱"] = "cat", - ["🐭"] = "mouse", - ["🐹"] = "hamster", - ["🐰"] = "rabbit", - ["🦊"] = "fox", - ["🐻"] = "bear", - ["🐼"] = "panda_face", - ["🐻‍❄️"] = "polar_bear", - ["🐨"] = "koala", - ["🐯"] = "tiger", - ["🦁"] = "lion_face", - ["🐮"] = "cow", - ["🐷"] = "pig", - ["🐽"] = "pig_nose", - ["🐸"] = "frog", - ["🐵"] = "monkey_face", - ["🙈"] = "see_no_evil", - ["🙉"] = "hear_no_evil", - ["🙊"] = "speak_no_evil", - ["🐒"] = "monkey", - ["🐔"] = "chicken", - ["🐧"] = "penguin", - ["🐦"] = "bird", - ["🐤"] = "baby_chick", - ["🐣"] = "hatching_chick", - ["🐥"] = "hatched_chick", - ["🦆"] = "duck", - ["🦤"] = "dodo", - ["🦅"] = "eagle", - ["🦉"] = "owl", - ["🦇"] = "bat", - ["🐺"] = "wolf", - ["🐗"] = "boar", - ["🐴"] = "horse", - ["🦄"] = "unicorn", - ["🐝"] = "bee", - ["🐛"] = "bug", - ["🦋"] = "butterfly", - ["🐌"] = "snail", - ["🪱"] = "worm", - ["🐞"] = "lady_beetle", - ["🐜"] = "ant", - ["🪰"] = "fly", - ["🦟"] = "mosquito", - ["🪳"] = "cockroach", - ["🪲"] = "beetle", - ["🦗"] = "cricket", - ["🕷️"] = "spider", - ["🕸️"] = "spider_web", - ["🦂"] = "scorpion", - ["🐢"] = "turtle", - ["🐍"] = "snake", - ["🦎"] = "lizard", - ["🦖"] = "t_rex", - ["🦕"] = "sauropod", - ["🐙"] = "octopus", - ["🦑"] = "squid", - ["🦐"] = "shrimp", - ["🦞"] = "lobster", - ["🦀"] = "crab", - ["🐡"] = "blowfish", - ["🐠"] = "tropical_fish", - ["🐟"] = "fish", - ["🦭"] = "seal", - ["🐬"] = "dolphin", - ["🐳"] = "whale", - ["🐋"] = "whale2", - ["🦈"] = "shark", - ["🐊"] = "crocodile", - ["🐅"] = "tiger2", - ["🐆"] = "leopard", - ["🦓"] = "zebra", - ["🦍"] = "gorilla", - ["🦧"] = "orangutan", - ["🐘"] = "elephant", - ["🦣"] = "mammoth", - ["🦬"] = "bison", - ["🦛"] = "hippopotamus", - ["🦏"] = "rhino", - ["🐪"] = "dromedary_camel", - ["🐫"] = "camel", - ["🦒"] = "giraffe", - ["🦘"] = "kangaroo", - ["🐃"] = "water_buffalo", - ["🐂"] = "ox", - ["🐄"] = "cow2", - ["🐎"] = "racehorse", - ["🐖"] = "pig2", - ["🐏"] = "ram", - ["🐑"] = "sheep", - ["🦙"] = "llama", - ["🐐"] = "goat", - ["🦌"] = "deer", - ["🐕"] = "dog2", - ["🐩"] = "poodle", - ["🦮"] = "guide_dog", - ["🐕‍🦺"] = "service_dog", - ["🐈"] = "cat2", - ["🐈‍⬛"] = "black_cat", - ["🐓"] = "rooster", - ["🦃"] = "turkey", - ["🦚"] = "peacock", - ["🦜"] = "parrot", - ["🦢"] = "swan", - ["🦩"] = "flamingo", - ["🕊️"] = "dove", - ["🐇"] = "rabbit2", - ["🦝"] = "raccoon", - ["🦨"] = "skunk", - ["🦡"] = "badger", - ["🦫"] = "beaver", - ["🦦"] = "otter", - ["🦥"] = "sloth", - ["🐁"] = "mouse2", - ["🐀"] = "rat", - ["🐿️"] = "chipmunk", - ["🦔"] = "hedgehog", - ["🐾"] = "feet", - ["🐉"] = "dragon", - ["🐲"] = "dragon_face", - ["🌵"] = "cactus", - ["🎄"] = "christmas_tree", - ["🌲"] = "evergreen_tree", - ["🌳"] = "deciduous_tree", - ["🌴"] = "palm_tree", - ["🌱"] = "seedling", - ["🌿"] = "herb", - ["☘️"] = "shamrock", - ["🍀"] = "four_leaf_clover", - ["🎍"] = "bamboo", - ["🎋"] = "tanabata_tree", - ["🍃"] = "leaves", - ["🍂"] = "fallen_leaf", - ["🍁"] = "maple_leaf", - ["🪶"] = "feather", - ["🍄"] = "mushroom", - ["🐚"] = "shell", - ["🪨"] = "rock", - ["🪵"] = "wood", - ["🌾"] = "ear_of_rice", - ["🪴"] = "potted_plant", - ["💐"] = "bouquet", - ["🌷"] = "tulip", - ["🌹"] = "rose", - ["🥀"] = "wilted_rose", - ["🌺"] = "hibiscus", - ["🌸"] = "cherry_blossom", - ["🌼"] = "blossom", - ["🌻"] = "sunflower", - ["🌞"] = "sun_with_face", - ["🌝"] = "full_moon_with_face", - ["🌛"] = "first_quarter_moon_with_face", - ["🌜"] = "last_quarter_moon_with_face", - ["🌚"] = "new_moon_with_face", - ["🌕"] = "full_moon", - ["🌖"] = "waning_gibbous_moon", - ["🌗"] = "last_quarter_moon", - ["🌘"] = "waning_crescent_moon", - ["🌑"] = "new_moon", - ["🌒"] = "waxing_crescent_moon", - ["🌓"] = "first_quarter_moon", - ["🌔"] = "waxing_gibbous_moon", - ["🌙"] = "crescent_moon", - ["🌎"] = "earth_americas", - ["🌍"] = "earth_africa", - ["🌏"] = "earth_asia", - ["🪐"] = "ringed_planet", - ["💫"] = "dizzy", - ["⭐"] = "star", - ["🌟"] = "star2", - ["✨"] = "sparkles", - ["⚡"] = "zap", - ["☄️"] = "comet", - ["💥"] = "boom", - ["🔥"] = "fire", - ["🌪️"] = "cloud_tornado", - ["🌈"] = "rainbow", - ["☀️"] = "sunny", - ["🌤️"] = "white_sun_small_cloud", - ["⛅"] = "partly_sunny", - ["🌥️"] = "white_sun_cloud", - ["☁️"] = "cloud", - ["🌦️"] = "white_sun_rain_cloud", - ["🌧️"] = "cloud_rain", - ["⛈️"] = "thunder_cloud_rain", - ["🌩️"] = "cloud_lightning", - ["🌨️"] = "cloud_snow", - ["❄️"] = "snowflake", - ["☃️"] = "snowman2", - ["⛄"] = "snowman", - ["🌬️"] = "wind_blowing_face", - ["💨"] = "dash", - ["💧"] = "droplet", - ["💦"] = "sweat_drops", - ["☔"] = "umbrella", - ["☂️"] = "umbrella2", - ["🌊"] = "ocean", - ["🌫️"] = "fog", - ["🍏"] = "green_apple", - ["🍎"] = "apple", - ["🍐"] = "pear", - ["🍊"] = "tangerine", - ["🍋"] = "lemon", - ["🍌"] = "banana", - ["🍉"] = "watermelon", - ["🍇"] = "grapes", - ["🫐"] = "blueberries", - ["🍓"] = "strawberry", - ["🍈"] = "melon", - ["🍒"] = "cherries", - ["🍑"] = "peach", - ["🥭"] = "mango", - ["🍍"] = "pineapple", - ["🥥"] = "coconut", - ["🥝"] = "kiwi", - ["🍅"] = "tomato", - ["🍆"] = "eggplant", - ["🥑"] = "avocado", - ["🫒"] = "olive", - ["🥦"] = "broccoli", - ["🥬"] = "leafy_green", - ["🫑"] = "bell_pepper", - ["🥒"] = "cucumber", - ["🌶️"] = "hot_pepper", - ["🌽"] = "corn", - ["🥕"] = "carrot", - ["🧄"] = "garlic", - ["🧅"] = "onion", - ["🥔"] = "potato", - ["🍠"] = "sweet_potato", - ["🥐"] = "croissant", - ["🥯"] = "bagel", - ["🍞"] = "bread", - ["🥖"] = "french_bread", - ["🫓"] = "flatbread", - ["🥨"] = "pretzel", - ["🧀"] = "cheese", - ["🥚"] = "egg", - ["🍳"] = "cooking", - ["🧈"] = "butter", - ["🥞"] = "pancakes", - ["🧇"] = "waffle", - ["🥓"] = "bacon", - ["🥩"] = "cut_of_meat", - ["🍗"] = "poultry_leg", - ["🍖"] = "meat_on_bone", - ["🌭"] = "hotdog", - ["🍔"] = "hamburger", - ["🍟"] = "fries", - ["🍕"] = "pizza", - ["🥪"] = "sandwich", - ["🥙"] = "stuffed_flatbread", - ["🧆"] = "falafel", - ["🌮"] = "taco", - ["🌯"] = "burrito", - ["🫔"] = "tamale", - ["🥗"] = "salad", - ["🥘"] = "shallow_pan_of_food", - ["🫕"] = "fondue", - ["🥫"] = "canned_food", - ["🍝"] = "spaghetti", - ["🍜"] = "ramen", - ["🍲"] = "stew", - ["🍛"] = "curry", - ["🍣"] = "sushi", - ["🍱"] = "bento", - ["🥟"] = "dumpling", - ["🦪"] = "oyster", - ["🍤"] = "fried_shrimp", - ["🍙"] = "rice_ball", - ["🍚"] = "rice", - ["🍘"] = "rice_cracker", - ["🍥"] = "fish_cake", - ["🥠"] = "fortune_cookie", - ["🥮"] = "moon_cake", - ["🍢"] = "oden", - ["🍡"] = "dango", - ["🍧"] = "shaved_ice", - ["🍨"] = "ice_cream", - ["🍦"] = "icecream", - ["🥧"] = "pie", - ["🧁"] = "cupcake", - ["🍰"] = "cake", - ["🎂"] = "birthday", - ["🍮"] = "custard", - ["🍭"] = "lollipop", - ["🍬"] = "candy", - ["🍫"] = "chocolate_bar", - ["🍿"] = "popcorn", - ["🍩"] = "doughnut", - ["🍪"] = "cookie", - ["🌰"] = "chestnut", - ["🥜"] = "peanuts", - ["🍯"] = "honey_pot", - ["🥛"] = "milk", - ["🍼"] = "baby_bottle", - ["☕"] = "coffee", - ["🍵"] = "tea", - ["🫖"] = "teapot", - ["🧉"] = "mate", - ["🧋"] = "bubble_tea", - ["🧃"] = "beverage_box", - ["🥤"] = "cup_with_straw", - ["🍶"] = "sake", - ["🍺"] = "beer", - ["🍻"] = "beers", - ["🥂"] = "champagne_glass", - ["🍷"] = "wine_glass", - ["🥃"] = "tumbler_glass", - ["🍸"] = "cocktail", - ["🍹"] = "tropical_drink", - ["🍾"] = "champagne", - ["🧊"] = "ice_cube", - ["🥄"] = "spoon", - ["🍴"] = "fork_and_knife", - ["🍽️"] = "fork_knife_plate", - ["🥣"] = "bowl_with_spoon", - ["🥡"] = "takeout_box", - ["🥢"] = "chopsticks", - ["🧂"] = "salt", - ["⚽"] = "soccer", - ["🏀"] = "basketball", - ["🏈"] = "football", - ["⚾"] = "baseball", - ["🥎"] = "softball", - ["🎾"] = "tennis", - ["🏐"] = "volleyball", - ["🏉"] = "rugby_football", - ["🥏"] = "flying_disc", - ["🪃"] = "boomerang", - ["🎱"] = "8ball", - ["🪀"] = "yo_yo", - ["🏓"] = "ping_pong", - ["🏸"] = "badminton", - ["🏒"] = "hockey", - ["🏑"] = "field_hockey", - ["🥍"] = "lacrosse", - ["🏏"] = "cricket_game", - ["🥅"] = "goal", - ["⛳"] = "golf", - ["🪁"] = "kite", - ["🏹"] = "bow_and_arrow", - ["🎣"] = "fishing_pole_and_fish", - ["🤿"] = "diving_mask", - ["🥊"] = "boxing_glove", - ["🥋"] = "martial_arts_uniform", - ["🎽"] = "running_shirt_with_sash", - ["🛹"] = "skateboard", - ["🛼"] = "roller_skate", - ["🛷"] = "sled", - ["⛸️"] = "ice_skate", - ["🥌"] = "curling_stone", - ["🎿"] = "ski", - ["⛷️"] = "skier", - ["🏂"] = "snowboarder", - ["🏂🏻"] = "snowboarder_tone1", - ["🏂🏼"] = "snowboarder_tone2", - ["🏂🏽"] = "snowboarder_tone3", - ["🏂🏾"] = "snowboarder_tone4", - ["🏂🏿"] = "snowboarder_tone5", - ["🪂"] = "parachute", - ["🏋️"] = "person_lifting_weights", - ["🏋🏻"] = "person_lifting_weights_tone1", - ["🏋🏼"] = "person_lifting_weights_tone2", - ["🏋🏽"] = "person_lifting_weights_tone3", - ["🏋🏾"] = "person_lifting_weights_tone4", - ["🏋🏿"] = "person_lifting_weights_tone5", - ["🏋️‍♀️"] = "woman_lifting_weights", - ["🏋🏻‍♀️"] = "woman_lifting_weights_tone1", - ["🏋🏼‍♀️"] = "woman_lifting_weights_tone2", - ["🏋🏽‍♀️"] = "woman_lifting_weights_tone3", - ["🏋🏾‍♀️"] = "woman_lifting_weights_tone4", - ["🏋🏿‍♀️"] = "woman_lifting_weights_tone5", - ["🏋️‍♂️"] = "man_lifting_weights", - ["🏋🏻‍♂️"] = "man_lifting_weights_tone1", - ["🏋🏼‍♂️"] = "man_lifting_weights_tone2", - ["🏋🏽‍♂️"] = "man_lifting_weights_tone3", - ["🏋🏾‍♂️"] = "man_lifting_weights_tone4", - ["🏋🏿‍♂️"] = "man_lifting_weights_tone5", - ["🤼"] = "people_wrestling", - ["🤼‍♀️"] = "women_wrestling", - ["🤼‍♂️"] = "men_wrestling", - ["🤸"] = "person_doing_cartwheel", - ["🤸🏻"] = "person_doing_cartwheel_tone1", - ["🤸🏼"] = "person_doing_cartwheel_tone2", - ["🤸🏽"] = "person_doing_cartwheel_tone3", - ["🤸🏾"] = "person_doing_cartwheel_tone4", - ["🤸🏿"] = "person_doing_cartwheel_tone5", - ["🤸‍♀️"] = "woman_cartwheeling", - ["🤸🏻‍♀️"] = "woman_cartwheeling_tone1", - ["🤸🏼‍♀️"] = "woman_cartwheeling_tone2", - ["🤸🏽‍♀️"] = "woman_cartwheeling_tone3", - ["🤸🏾‍♀️"] = "woman_cartwheeling_tone4", - ["🤸🏿‍♀️"] = "woman_cartwheeling_tone5", - ["🤸‍♂️"] = "man_cartwheeling", - ["🤸🏻‍♂️"] = "man_cartwheeling_tone1", - ["🤸🏼‍♂️"] = "man_cartwheeling_tone2", - ["🤸🏽‍♂️"] = "man_cartwheeling_tone3", - ["🤸🏾‍♂️"] = "man_cartwheeling_tone4", - ["🤸🏿‍♂️"] = "man_cartwheeling_tone5", - ["⛹️"] = "person_bouncing_ball", - ["⛹🏻"] = "person_bouncing_ball_tone1", - ["⛹🏼"] = "person_bouncing_ball_tone2", - ["⛹🏽"] = "person_bouncing_ball_tone3", - ["⛹🏾"] = "person_bouncing_ball_tone4", - ["⛹🏿"] = "person_bouncing_ball_tone5", - ["⛹️‍♀️"] = "woman_bouncing_ball", - ["⛹🏻‍♀️"] = "woman_bouncing_ball_tone1", - ["⛹🏼‍♀️"] = "woman_bouncing_ball_tone2", - ["⛹🏽‍♀️"] = "woman_bouncing_ball_tone3", - ["⛹🏾‍♀️"] = "woman_bouncing_ball_tone4", - ["⛹🏿‍♀️"] = "woman_bouncing_ball_tone5", - ["⛹️‍♂️"] = "man_bouncing_ball", - ["⛹🏻‍♂️"] = "man_bouncing_ball_tone1", - ["⛹🏼‍♂️"] = "man_bouncing_ball_tone2", - ["⛹🏽‍♂️"] = "man_bouncing_ball_tone3", - ["⛹🏾‍♂️"] = "man_bouncing_ball_tone4", - ["⛹🏿‍♂️"] = "man_bouncing_ball_tone5", - ["🤺"] = "person_fencing", - ["🤾"] = "person_playing_handball", - ["🤾🏻"] = "person_playing_handball_tone1", - ["🤾🏼"] = "person_playing_handball_tone2", - ["🤾🏽"] = "person_playing_handball_tone3", - ["🤾🏾"] = "person_playing_handball_tone4", - ["🤾🏿"] = "person_playing_handball_tone5", - ["🤾‍♀️"] = "woman_playing_handball", - ["🤾🏻‍♀️"] = "woman_playing_handball_tone1", - ["🤾🏼‍♀️"] = "woman_playing_handball_tone2", - ["🤾🏽‍♀️"] = "woman_playing_handball_tone3", - ["🤾🏾‍♀️"] = "woman_playing_handball_tone4", - ["🤾🏿‍♀️"] = "woman_playing_handball_tone5", - ["🤾‍♂️"] = "man_playing_handball", - ["🤾🏻‍♂️"] = "man_playing_handball_tone1", - ["🤾🏼‍♂️"] = "man_playing_handball_tone2", - ["🤾🏽‍♂️"] = "man_playing_handball_tone3", - ["🤾🏾‍♂️"] = "man_playing_handball_tone4", - ["🤾🏿‍♂️"] = "man_playing_handball_tone5", - ["🏌️"] = "person_golfing", - ["🏌🏻"] = "person_golfing_tone1", - ["🏌🏼"] = "person_golfing_tone2", - ["🏌🏽"] = "person_golfing_tone3", - ["🏌🏾"] = "person_golfing_tone4", - ["🏌🏿"] = "person_golfing_tone5", - ["🏌️‍♀️"] = "woman_golfing", - ["🏌🏻‍♀️"] = "woman_golfing_tone1", - ["🏌🏼‍♀️"] = "woman_golfing_tone2", - ["🏌🏽‍♀️"] = "woman_golfing_tone3", - ["🏌🏾‍♀️"] = "woman_golfing_tone4", - ["🏌🏿‍♀️"] = "woman_golfing_tone5", - ["🏌️‍♂️"] = "man_golfing", - ["🏌🏻‍♂️"] = "man_golfing_tone1", - ["🏌🏼‍♂️"] = "man_golfing_tone2", - ["🏌🏽‍♂️"] = "man_golfing_tone3", - ["🏌🏾‍♂️"] = "man_golfing_tone4", - ["🏌🏿‍♂️"] = "man_golfing_tone5", - ["🏇"] = "horse_racing", - ["🏇🏻"] = "horse_racing_tone1", - ["🏇🏼"] = "horse_racing_tone2", - ["🏇🏽"] = "horse_racing_tone3", - ["🏇🏾"] = "horse_racing_tone4", - ["🏇🏿"] = "horse_racing_tone5", - ["🧘"] = "person_in_lotus_position", - ["🧘🏻"] = "person_in_lotus_position_tone1", - ["🧘🏼"] = "person_in_lotus_position_tone2", - ["🧘🏽"] = "person_in_lotus_position_tone3", - ["🧘🏾"] = "person_in_lotus_position_tone4", - ["🧘🏿"] = "person_in_lotus_position_tone5", - ["🧘‍♀️"] = "woman_in_lotus_position", - ["🧘🏻‍♀️"] = "woman_in_lotus_position_tone1", - ["🧘🏼‍♀️"] = "woman_in_lotus_position_tone2", - ["🧘🏽‍♀️"] = "woman_in_lotus_position_tone3", - ["🧘🏾‍♀️"] = "woman_in_lotus_position_tone4", - ["🧘🏿‍♀️"] = "woman_in_lotus_position_tone5", - ["🧘‍♂️"] = "man_in_lotus_position", - ["🧘🏻‍♂️"] = "man_in_lotus_position_tone1", - ["🧘🏼‍♂️"] = "man_in_lotus_position_tone2", - ["🧘🏽‍♂️"] = "man_in_lotus_position_tone3", - ["🧘🏾‍♂️"] = "man_in_lotus_position_tone4", - ["🧘🏿‍♂️"] = "man_in_lotus_position_tone5", - ["🏄"] = "person_surfing", - ["🏄🏻"] = "person_surfing_tone1", - ["🏄🏼"] = "person_surfing_tone2", - ["🏄🏽"] = "person_surfing_tone3", - ["🏄🏾"] = "person_surfing_tone4", - ["🏄🏿"] = "person_surfing_tone5", - ["🏄‍♀️"] = "woman_surfing", - ["🏄🏻‍♀️"] = "woman_surfing_tone1", - ["🏄🏼‍♀️"] = "woman_surfing_tone2", - ["🏄🏽‍♀️"] = "woman_surfing_tone3", - ["🏄🏾‍♀️"] = "woman_surfing_tone4", - ["🏄🏿‍♀️"] = "woman_surfing_tone5", - ["🏄‍♂️"] = "man_surfing", - ["🏄🏻‍♂️"] = "man_surfing_tone1", - ["🏄🏼‍♂️"] = "man_surfing_tone2", - ["🏄🏽‍♂️"] = "man_surfing_tone3", - ["🏄🏾‍♂️"] = "man_surfing_tone4", - ["🏄🏿‍♂️"] = "man_surfing_tone5", - ["🏊"] = "person_swimming", - ["🏊🏻"] = "person_swimming_tone1", - ["🏊🏼"] = "person_swimming_tone2", - ["🏊🏽"] = "person_swimming_tone3", - ["🏊🏾"] = "person_swimming_tone4", - ["🏊🏿"] = "person_swimming_tone5", - ["🏊‍♀️"] = "woman_swimming", - ["🏊🏻‍♀️"] = "woman_swimming_tone1", - ["🏊🏼‍♀️"] = "woman_swimming_tone2", - ["🏊🏽‍♀️"] = "woman_swimming_tone3", - ["🏊🏾‍♀️"] = "woman_swimming_tone4", - ["🏊🏿‍♀️"] = "woman_swimming_tone5", - ["🏊‍♂️"] = "man_swimming", - ["🏊🏻‍♂️"] = "man_swimming_tone1", - ["🏊🏼‍♂️"] = "man_swimming_tone2", - ["🏊🏽‍♂️"] = "man_swimming_tone3", - ["🏊🏾‍♂️"] = "man_swimming_tone4", - ["🏊🏿‍♂️"] = "man_swimming_tone5", - ["🤽"] = "person_playing_water_polo", - ["🤽🏻"] = "person_playing_water_polo_tone1", - ["🤽🏼"] = "person_playing_water_polo_tone2", - ["🤽🏽"] = "person_playing_water_polo_tone3", - ["🤽🏾"] = "person_playing_water_polo_tone4", - ["🤽🏿"] = "person_playing_water_polo_tone5", - ["🤽‍♀️"] = "woman_playing_water_polo", - ["🤽🏻‍♀️"] = "woman_playing_water_polo_tone1", - ["🤽🏼‍♀️"] = "woman_playing_water_polo_tone2", - ["🤽🏽‍♀️"] = "woman_playing_water_polo_tone3", - ["🤽🏾‍♀️"] = "woman_playing_water_polo_tone4", - ["🤽🏿‍♀️"] = "woman_playing_water_polo_tone5", - ["🤽‍♂️"] = "man_playing_water_polo", - ["🤽🏻‍♂️"] = "man_playing_water_polo_tone1", - ["🤽🏼‍♂️"] = "man_playing_water_polo_tone2", - ["🤽🏽‍♂️"] = "man_playing_water_polo_tone3", - ["🤽🏾‍♂️"] = "man_playing_water_polo_tone4", - ["🤽🏿‍♂️"] = "man_playing_water_polo_tone5", - ["🚣"] = "person_rowing_boat", - ["🚣🏻"] = "person_rowing_boat_tone1", - ["🚣🏼"] = "person_rowing_boat_tone2", - ["🚣🏽"] = "person_rowing_boat_tone3", - ["🚣🏾"] = "person_rowing_boat_tone4", - ["🚣🏿"] = "person_rowing_boat_tone5", - ["🚣‍♀️"] = "woman_rowing_boat", - ["🚣🏻‍♀️"] = "woman_rowing_boat_tone1", - ["🚣🏼‍♀️"] = "woman_rowing_boat_tone2", - ["🚣🏽‍♀️"] = "woman_rowing_boat_tone3", - ["🚣🏾‍♀️"] = "woman_rowing_boat_tone4", - ["🚣🏿‍♀️"] = "woman_rowing_boat_tone5", - ["🚣‍♂️"] = "man_rowing_boat", - ["🚣🏻‍♂️"] = "man_rowing_boat_tone1", - ["🚣🏼‍♂️"] = "man_rowing_boat_tone2", - ["🚣🏽‍♂️"] = "man_rowing_boat_tone3", - ["🚣🏾‍♂️"] = "man_rowing_boat_tone4", - ["🚣🏿‍♂️"] = "man_rowing_boat_tone5", - ["🧗"] = "person_climbing", - ["🧗🏻"] = "person_climbing_tone1", - ["🧗🏼"] = "person_climbing_tone2", - ["🧗🏽"] = "person_climbing_tone3", - ["🧗🏾"] = "person_climbing_tone4", - ["🧗🏿"] = "person_climbing_tone5", - ["🧗‍♀️"] = "woman_climbing", - ["🧗🏻‍♀️"] = "woman_climbing_tone1", - ["🧗🏼‍♀️"] = "woman_climbing_tone2", - ["🧗🏽‍♀️"] = "woman_climbing_tone3", - ["🧗🏾‍♀️"] = "woman_climbing_tone4", - ["🧗🏿‍♀️"] = "woman_climbing_tone5", - ["🧗‍♂️"] = "man_climbing", - ["🧗🏻‍♂️"] = "man_climbing_tone1", - ["🧗🏼‍♂️"] = "man_climbing_tone2", - ["🧗🏽‍♂️"] = "man_climbing_tone3", - ["🧗🏾‍♂️"] = "man_climbing_tone4", - ["🧗🏿‍♂️"] = "man_climbing_tone5", - ["🚵"] = "person_mountain_biking", - ["🚵🏻"] = "person_mountain_biking_tone1", - ["🚵🏼"] = "person_mountain_biking_tone2", - ["🚵🏽"] = "person_mountain_biking_tone3", - ["🚵🏾"] = "person_mountain_biking_tone4", - ["🚵🏿"] = "person_mountain_biking_tone5", - ["🚵‍♀️"] = "woman_mountain_biking", - ["🚵🏻‍♀️"] = "woman_mountain_biking_tone1", - ["🚵🏼‍♀️"] = "woman_mountain_biking_tone2", - ["🚵🏽‍♀️"] = "woman_mountain_biking_tone3", - ["🚵🏾‍♀️"] = "woman_mountain_biking_tone4", - ["🚵🏿‍♀️"] = "woman_mountain_biking_tone5", - ["🚵‍♂️"] = "man_mountain_biking", - ["🚵🏻‍♂️"] = "man_mountain_biking_tone1", - ["🚵🏼‍♂️"] = "man_mountain_biking_tone2", - ["🚵🏽‍♂️"] = "man_mountain_biking_tone3", - ["🚵🏾‍♂️"] = "man_mountain_biking_tone4", - ["🚵🏿‍♂️"] = "man_mountain_biking_tone5", - ["🚴"] = "person_biking", - ["🚴🏻"] = "person_biking_tone1", - ["🚴🏼"] = "person_biking_tone2", - ["🚴🏽"] = "person_biking_tone3", - ["🚴🏾"] = "person_biking_tone4", - ["🚴🏿"] = "person_biking_tone5", - ["🚴‍♀️"] = "woman_biking", - ["🚴🏻‍♀️"] = "woman_biking_tone1", - ["🚴🏼‍♀️"] = "woman_biking_tone2", - ["🚴🏽‍♀️"] = "woman_biking_tone3", - ["🚴🏾‍♀️"] = "woman_biking_tone4", - ["🚴🏿‍♀️"] = "woman_biking_tone5", - ["🚴‍♂️"] = "man_biking", - ["🚴🏻‍♂️"] = "man_biking_tone1", - ["🚴🏼‍♂️"] = "man_biking_tone2", - ["🚴🏽‍♂️"] = "man_biking_tone3", - ["🚴🏾‍♂️"] = "man_biking_tone4", - ["🚴🏿‍♂️"] = "man_biking_tone5", - ["🏆"] = "trophy", - ["🥇"] = "first_place", - ["🥈"] = "second_place", - ["🥉"] = "third_place", - ["🏅"] = "medal", - ["🎖️"] = "military_medal", - ["🏵️"] = "rosette", - ["🎗️"] = "reminder_ribbon", - ["🎫"] = "ticket", - ["🎟️"] = "tickets", - ["🎪"] = "circus_tent", - ["🤹"] = "person_juggling", - ["🤹🏻"] = "person_juggling_tone1", - ["🤹🏼"] = "person_juggling_tone2", - ["🤹🏽"] = "person_juggling_tone3", - ["🤹🏾"] = "person_juggling_tone4", - ["🤹🏿"] = "person_juggling_tone5", - ["🤹‍♀️"] = "woman_juggling", - ["🤹🏻‍♀️"] = "woman_juggling_tone1", - ["🤹🏼‍♀️"] = "woman_juggling_tone2", - ["🤹🏽‍♀️"] = "woman_juggling_tone3", - ["🤹🏾‍♀️"] = "woman_juggling_tone4", - ["🤹🏿‍♀️"] = "woman_juggling_tone5", - ["🤹‍♂️"] = "man_juggling", - ["🤹🏻‍♂️"] = "man_juggling_tone1", - ["🤹🏼‍♂️"] = "man_juggling_tone2", - ["🤹🏽‍♂️"] = "man_juggling_tone3", - ["🤹🏾‍♂️"] = "man_juggling_tone4", - ["🤹🏿‍♂️"] = "man_juggling_tone5", - ["🎭"] = "performing_arts", - ["🩰"] = "ballet_shoes", - ["🎨"] = "art", - ["🎬"] = "clapper", - ["🎤"] = "microphone", - ["🎧"] = "headphones", - ["🎼"] = "musical_score", - ["🎹"] = "musical_keyboard", - ["🥁"] = "drum", - ["🪘"] = "long_drum", - ["🎷"] = "saxophone", - ["🎺"] = "trumpet", - ["🎸"] = "guitar", - ["🪕"] = "banjo", - ["🎻"] = "violin", - ["🪗"] = "accordion", - ["🎲"] = "game_die", - ["♟️"] = "chess_pawn", - ["🎯"] = "dart", - ["🎳"] = "bowling", - ["🎮"] = "video_game", - ["🎰"] = "slot_machine", - ["🧩"] = "jigsaw", - ["🚗"] = "red_car", - ["🚕"] = "taxi", - ["🚙"] = "blue_car", - ["🛻"] = "pickup_truck", - ["🚌"] = "bus", - ["🚎"] = "trolleybus", - ["🏎️"] = "race_car", - ["🚓"] = "police_car", - ["🚑"] = "ambulance", - ["🚒"] = "fire_engine", - ["🚐"] = "minibus", - ["🚚"] = "truck", - ["🚛"] = "articulated_lorry", - ["🚜"] = "tractor", - ["🦯"] = "probing_cane", - ["🦽"] = "manual_wheelchair", - ["🦼"] = "motorized_wheelchair", - ["🛴"] = "scooter", - ["🚲"] = "bike", - ["🛵"] = "motor_scooter", - ["🏍️"] = "motorcycle", - ["🛺"] = "auto_rickshaw", - ["🚨"] = "rotating_light", - ["🚔"] = "oncoming_police_car", - ["🚍"] = "oncoming_bus", - ["🚘"] = "oncoming_automobile", - ["🚖"] = "oncoming_taxi", - ["🚡"] = "aerial_tramway", - ["🚠"] = "mountain_cableway", - ["🚟"] = "suspension_railway", - ["🚃"] = "railway_car", - ["🚋"] = "train", - ["🚞"] = "mountain_railway", - ["🚝"] = "monorail", - ["🚄"] = "bullettrain_side", - ["🚅"] = "bullettrain_front", - ["🚈"] = "light_rail", - ["🚂"] = "steam_locomotive", - ["🚆"] = "train2", - ["🚇"] = "metro", - ["🚊"] = "tram", - ["🚉"] = "station", - ["✈️"] = "airplane", - ["🛫"] = "airplane_departure", - ["🛬"] = "airplane_arriving", - ["🛩️"] = "airplane_small", - ["💺"] = "seat", - ["🛰️"] = "satellite_orbital", - ["🚀"] = "rocket", - ["🛸"] = "flying_saucer", - ["🚁"] = "helicopter", - ["🛶"] = "canoe", - ["⛵"] = "sailboat", - ["🚤"] = "speedboat", - ["🛥️"] = "motorboat", - ["🛳️"] = "cruise_ship", - ["⛴️"] = "ferry", - ["🚢"] = "ship", - ["⚓"] = "anchor", - ["⛽"] = "fuelpump", - ["🚧"] = "construction", - ["🚦"] = "vertical_traffic_light", - ["🚥"] = "traffic_light", - ["🚏"] = "busstop", - ["🗺️"] = "map", - ["🗿"] = "moyai", - ["🗽"] = "statue_of_liberty", - ["🗼"] = "tokyo_tower", - ["🏰"] = "european_castle", - ["🏯"] = "japanese_castle", - ["🏟️"] = "stadium", - ["🎡"] = "ferris_wheel", - ["🎢"] = "roller_coaster", - ["🎠"] = "carousel_horse", - ["⛲"] = "fountain", - ["⛱️"] = "beach_umbrella", - ["🏖️"] = "beach", - ["🏝️"] = "island", - ["🏜️"] = "desert", - ["🌋"] = "volcano", - ["⛰️"] = "mountain", - ["🏔️"] = "mountain_snow", - ["🗻"] = "mount_fuji", - ["🏕️"] = "camping", - ["⛺"] = "tent", - ["🏠"] = "house", - ["🏡"] = "house_with_garden", - ["🏘️"] = "homes", - ["🏚️"] = "house_abandoned", - ["🛖"] = "hut", - ["🏗️"] = "construction_site", - ["🏭"] = "factory", - ["🏢"] = "office", - ["🏬"] = "department_store", - ["🏣"] = "post_office", - ["🏤"] = "european_post_office", - ["🏥"] = "hospital", - ["🏦"] = "bank", - ["🏨"] = "hotel", - ["🏪"] = "convenience_store", - ["🏫"] = "school", - ["🏩"] = "love_hotel", - ["💒"] = "wedding", - ["🏛️"] = "classical_building", - ["⛪"] = "church", - ["🕌"] = "mosque", - ["🕍"] = "synagogue", - ["🛕"] = "hindu_temple", - ["🕋"] = "kaaba", - ["⛩️"] = "shinto_shrine", - ["🛤️"] = "railway_track", - ["🛣️"] = "motorway", - ["🗾"] = "japan", - ["🎑"] = "rice_scene", - ["🏞️"] = "park", - ["🌅"] = "sunrise", - ["🌄"] = "sunrise_over_mountains", - ["🌠"] = "stars", - ["🎇"] = "sparkler", - ["🎆"] = "fireworks", - ["🌇"] = "city_sunset", - ["🌆"] = "city_dusk", - ["🏙️"] = "cityscape", - ["🌃"] = "night_with_stars", - ["🌌"] = "milky_way", - ["🌉"] = "bridge_at_night", - ["🌁"] = "foggy", - ["⌚"] = "watch", - ["📱"] = "mobile_phone", - ["📲"] = "calling", - ["💻"] = "computer", - ["⌨️"] = "keyboard", - ["🖥️"] = "desktop", - ["🖨️"] = "printer", - ["🖱️"] = "mouse_three_button", - ["🖲️"] = "trackball", - ["🕹️"] = "joystick", - ["🗜️"] = "compression", - ["💽"] = "minidisc", - ["💾"] = "floppy_disk", - ["💿"] = "cd", - ["📀"] = "dvd", - ["📼"] = "vhs", - ["📷"] = "camera", - ["📸"] = "camera_with_flash", - ["📹"] = "video_camera", - ["🎥"] = "movie_camera", - ["📽️"] = "projector", - ["🎞️"] = "film_frames", - ["📞"] = "telephone_receiver", - ["☎️"] = "telephone", - ["📟"] = "pager", - ["📠"] = "fax", - ["📺"] = "tv", - ["📻"] = "radio", - ["🎙️"] = "microphone2", - ["🎚️"] = "level_slider", - ["🎛️"] = "control_knobs", - ["🧭"] = "compass", - ["⏱️"] = "stopwatch", - ["⏲️"] = "timer", - ["⏰"] = "alarm_clock", - ["🕰️"] = "clock", - ["⌛"] = "hourglass", - ["⏳"] = "hourglass_flowing_sand", - ["📡"] = "satellite", - ["🔋"] = "battery", - ["🔌"] = "electric_plug", - ["💡"] = "bulb", - ["🔦"] = "flashlight", - ["🕯️"] = "candle", - ["🪔"] = "diya_lamp", - ["🧯"] = "fire_extinguisher", - ["🛢️"] = "oil", - ["💸"] = "money_with_wings", - ["💵"] = "dollar", - ["💴"] = "yen", - ["💶"] = "euro", - ["💷"] = "pound", - ["🪙"] = "coin", - ["💰"] = "moneybag", - ["💳"] = "credit_card", - ["💎"] = "gem", - ["⚖️"] = "scales", - ["🪜"] = "ladder", - ["🧰"] = "toolbox", - ["🪛"] = "screwdriver", - ["🔧"] = "wrench", - ["🔨"] = "hammer", - ["⚒️"] = "hammer_pick", - ["🛠️"] = "tools", - ["⛏️"] = "pick", - ["🔩"] = "nut_and_bolt", - ["⚙️"] = "gear", - ["🧱"] = "bricks", - ["⛓️"] = "chains", - ["🪝"] = "hook", - ["🪢"] = "knot", - ["🧲"] = "magnet", - ["🔫"] = "gun", - ["💣"] = "bomb", - ["🧨"] = "firecracker", - ["🪓"] = "axe", - ["🪚"] = "carpentry_saw", - ["🔪"] = "knife", - ["🗡️"] = "dagger", - ["⚔️"] = "crossed_swords", - ["🛡️"] = "shield", - ["🚬"] = "smoking", - ["⚰️"] = "coffin", - ["🪦"] = "headstone", - ["⚱️"] = "urn", - ["🏺"] = "amphora", - ["🪄"] = "magic_wand", - ["🔮"] = "crystal_ball", - ["📿"] = "prayer_beads", - ["🧿"] = "nazar_amulet", - ["💈"] = "barber", - ["⚗️"] = "alembic", - ["🔭"] = "telescope", - ["🔬"] = "microscope", - ["🕳️"] = "hole", - ["🪟"] = "window", - ["🩹"] = "adhesive_bandage", - ["🩺"] = "stethoscope", - ["💊"] = "pill", - ["💉"] = "syringe", - ["🩸"] = "drop_of_blood", - ["🧬"] = "dna", - ["🦠"] = "microbe", - ["🧫"] = "petri_dish", - ["🧪"] = "test_tube", - ["🌡️"] = "thermometer", - ["🪤"] = "mouse_trap", - ["🧹"] = "broom", - ["🧺"] = "basket", - ["🪡"] = "sewing_needle", - ["🧻"] = "roll_of_paper", - ["🚽"] = "toilet", - ["🪠"] = "plunger", - ["🪣"] = "bucket", - ["🚰"] = "potable_water", - ["🚿"] = "shower", - ["🛁"] = "bathtub", - ["🛀"] = "bath", - ["🛀🏻"] = "bath_tone1", - ["🛀🏼"] = "bath_tone2", - ["🛀🏽"] = "bath_tone3", - ["🛀🏾"] = "bath_tone4", - ["🛀🏿"] = "bath_tone5", - ["🪥"] = "toothbrush", - ["🧼"] = "soap", - ["🪒"] = "razor", - ["🧽"] = "sponge", - ["🧴"] = "squeeze_bottle", - ["🛎️"] = "bellhop", - ["🔑"] = "key", - ["🗝️"] = "key2", - ["🚪"] = "door", - ["🪑"] = "chair", - ["🪞"] = "mirror", - ["🛋️"] = "couch", - ["🛏️"] = "bed", - ["🛌"] = "sleeping_accommodation", - ["🛌🏻"] = "person_in_bed_tone1", - ["🛌🏼"] = "person_in_bed_tone2", - ["🛌🏽"] = "person_in_bed_tone3", - ["🛌🏾"] = "person_in_bed_tone4", - ["🛌🏿"] = "person_in_bed_tone5", - ["🧸"] = "teddy_bear", - ["🖼️"] = "frame_photo", - ["🛍️"] = "shopping_bags", - ["🛒"] = "shopping_cart", - ["🎁"] = "gift", - ["🎈"] = "balloon", - ["🎏"] = "flags", - ["🎀"] = "ribbon", - ["🎊"] = "confetti_ball", - ["🎉"] = "tada", - ["🪅"] = "piñata", - ["🪆"] = "nesting_dolls", - ["🎎"] = "dolls", - ["🏮"] = "izakaya_lantern", - ["🎐"] = "wind_chime", - ["🧧"] = "red_envelope", - ["✉️"] = "envelope", - ["📩"] = "envelope_with_arrow", - ["📨"] = "incoming_envelope", - ["📧"] = "e_mail", - ["💌"] = "love_letter", - ["📥"] = "inbox_tray", - ["📤"] = "outbox_tray", - ["📦"] = "package", - ["🏷️"] = "label", - ["📪"] = "mailbox_closed", - ["📫"] = "mailbox", - ["📬"] = "mailbox_with_mail", - ["📭"] = "mailbox_with_no_mail", - ["📮"] = "postbox", - ["📯"] = "postal_horn", - ["🪧"] = "placard", - ["📜"] = "scroll", - ["📃"] = "page_with_curl", - ["📄"] = "page_facing_up", - ["📑"] = "bookmark_tabs", - ["🧾"] = "receipt", - ["📊"] = "bar_chart", - ["📈"] = "chart_with_upwards_trend", - ["📉"] = "chart_with_downwards_trend", - ["🗒️"] = "notepad_spiral", - ["🗓️"] = "calendar_spiral", - ["📆"] = "calendar", - ["📅"] = "date", - ["🗑️"] = "wastebasket", - ["📇"] = "card_index", - ["🗃️"] = "card_box", - ["🗳️"] = "ballot_box", - ["🗄️"] = "file_cabinet", - ["📋"] = "clipboard", - ["📁"] = "file_folder", - ["📂"] = "open_file_folder", - ["🗂️"] = "dividers", - ["🗞️"] = "newspaper2", - ["📰"] = "newspaper", - ["📓"] = "notebook", - ["📔"] = "notebook_with_decorative_cover", - ["📒"] = "ledger", - ["📕"] = "closed_book", - ["📗"] = "green_book", - ["📘"] = "blue_book", - ["📙"] = "orange_book", - ["📚"] = "books", - ["📖"] = "book", - ["🔖"] = "bookmark", - ["🧷"] = "safety_pin", - ["🔗"] = "link", - ["📎"] = "paperclip", - ["🖇️"] = "paperclips", - ["📐"] = "triangular_ruler", - ["📏"] = "straight_ruler", - ["🧮"] = "abacus", - ["📌"] = "pushpin", - ["📍"] = "round_pushpin", - ["✂️"] = "scissors", - ["🖊️"] = "pen_ballpoint", - ["🖋️"] = "pen_fountain", - ["✒️"] = "black_nib", - ["🖌️"] = "paintbrush", - ["🖍️"] = "crayon", - ["📝"] = "pencil", - ["✏️"] = "pencil2", - ["🔍"] = "mag", - ["🔎"] = "mag_right", - ["🔏"] = "lock_with_ink_pen", - ["🔐"] = "closed_lock_with_key", - ["🔒"] = "lock", - ["🔓"] = "unlock", - ["❤️"] = "heart", - ["🧡"] = "orange_heart", - ["💛"] = "yellow_heart", - ["💚"] = "green_heart", - ["💙"] = "blue_heart", - ["💜"] = "purple_heart", - ["🖤"] = "black_heart", - ["🤎"] = "brown_heart", - ["🤍"] = "white_heart", - ["💔"] = "broken_heart", - ["❣️"] = "heart_exclamation", - ["💕"] = "two_hearts", - ["💞"] = "revolving_hearts", - ["💓"] = "heartbeat", - ["💗"] = "heartpulse", - ["💖"] = "sparkling_heart", - ["💘"] = "cupid", - ["💝"] = "gift_heart", - ["❤️‍🩹"] = "mending_heart", - ["❤️‍🔥"] = "heart_on_fire", - ["💟"] = "heart_decoration", - ["☮️"] = "peace", - ["✝️"] = "cross", - ["☪️"] = "star_and_crescent", - ["🕉️"] = "om_symbol", - ["☸️"] = "wheel_of_dharma", - ["✡️"] = "star_of_david", - ["🔯"] = "six_pointed_star", - ["🕎"] = "menorah", - ["☯️"] = "yin_yang", - ["☦️"] = "orthodox_cross", - ["🛐"] = "place_of_worship", - ["⛎"] = "ophiuchus", - ["♈"] = "aries", - ["♉"] = "taurus", - ["♊"] = "gemini", - ["♋"] = "cancer", - ["♌"] = "leo", - ["♍"] = "virgo", - ["♎"] = "libra", - ["♏"] = "scorpius", - ["♐"] = "sagittarius", - ["♑"] = "capricorn", - ["♒"] = "aquarius", - ["♓"] = "pisces", - ["🆔"] = "id", - ["⚛️"] = "atom", - ["🉑"] = "accept", - ["☢️"] = "radioactive", - ["☣️"] = "biohazard", - ["📴"] = "mobile_phone_off", - ["📳"] = "vibration_mode", - ["🈶"] = "u6709", - ["🈚"] = "u7121", - ["🈸"] = "u7533", - ["🈺"] = "u55b6", - ["🈷️"] = "u6708", - ["✴️"] = "eight_pointed_black_star", - ["🆚"] = "vs", - ["💮"] = "white_flower", - ["🉐"] = "ideograph_advantage", - ["㊙️"] = "secret", - ["㊗️"] = "congratulations", - ["🈴"] = "u5408", - ["🈵"] = "u6e80", - ["🈹"] = "u5272", - ["🈲"] = "u7981", - ["🅰️"] = "a", - ["🅱️"] = "b", - ["🆎"] = "ab", - ["🆑"] = "cl", - ["🅾️"] = "o2", - ["🆘"] = "sos", - ["❌"] = "x", - ["⭕"] = "o", - ["🛑"] = "octagonal_sign", - ["⛔"] = "no_entry", - ["📛"] = "name_badge", - ["🚫"] = "no_entry_sign", - ["💯"] = "100", - ["💢"] = "anger", - ["♨️"] = "hotsprings", - ["🚷"] = "no_pedestrians", - ["🚯"] = "do_not_litter", - ["🚳"] = "no_bicycles", - ["🚱"] = "non_potable_water", - ["🔞"] = "underage", - ["📵"] = "no_mobile_phones", - ["🚭"] = "no_smoking", - ["❗"] = "exclamation", - ["❕"] = "grey_exclamation", - ["❓"] = "question", - ["❔"] = "grey_question", - ["‼️"] = "bangbang", - ["⁉️"] = "interrobang", - ["🔅"] = "low_brightness", - ["🔆"] = "high_brightness", - ["〽️"] = "part_alternation_mark", - ["⚠️"] = "warning", - ["🚸"] = "children_crossing", - ["🔱"] = "trident", - ["⚜️"] = "fleur_de_lis", - ["🔰"] = "beginner", - ["♻️"] = "recycle", - ["✅"] = "white_check_mark", - ["🈯"] = "u6307", - ["💹"] = "chart", - ["❇️"] = "sparkle", - ["✳️"] = "eight_spoked_asterisk", - ["❎"] = "negative_squared_cross_mark", - ["🌐"] = "globe_with_meridians", - ["💠"] = "diamond_shape_with_a_dot_inside", - ["Ⓜ️"] = "m", - ["🌀"] = "cyclone", - ["💤"] = "zzz", - ["🏧"] = "atm", - ["🚾"] = "wc", - ["♿"] = "wheelchair", - ["🅿️"] = "parking", - ["🈳"] = "u7a7a", - ["🈂️"] = "sa", - ["🛂"] = "passport_control", - ["🛃"] = "customs", - ["🛄"] = "baggage_claim", - ["🛅"] = "left_luggage", - ["🛗"] = "elevator", - ["🚹"] = "mens", - ["🚺"] = "womens", - ["🚼"] = "baby_symbol", - ["🚻"] = "restroom", - ["🚮"] = "put_litter_in_its_place", - ["🎦"] = "cinema", - ["📶"] = "signal_strength", - ["🈁"] = "koko", - ["🔣"] = "symbols", - ["ℹ️"] = "information_source", - ["🔤"] = "abc", - ["🔡"] = "abcd", - ["🔠"] = "capital_abcd", - ["🆖"] = "ng", - ["🆗"] = "ok", - ["🆙"] = "up", - ["🆒"] = "cool", - ["🆕"] = "new", - ["🆓"] = "free", - ["0️⃣"] = "zero", - ["1️⃣"] = "one", - ["2️⃣"] = "two", - ["3️⃣"] = "three", - ["4️⃣"] = "four", - ["5️⃣"] = "five", - ["6️⃣"] = "six", - ["7️⃣"] = "seven", - ["8️⃣"] = "eight", - ["9️⃣"] = "nine", - ["🔟"] = "keycap_ten", - ["🔢"] = "1234", - ["#️⃣"] = "hash", - ["*️⃣"] = "asterisk", - ["⏏️"] = "eject", - ["▶️"] = "arrow_forward", - ["⏸️"] = "pause_button", - ["⏯️"] = "play_pause", - ["⏹️"] = "stop_button", - ["⏺️"] = "record_button", - ["⏭️"] = "track_next", - ["⏮️"] = "track_previous", - ["⏩"] = "fast_forward", - ["⏪"] = "rewind", - ["⏫"] = "arrow_double_up", - ["⏬"] = "arrow_double_down", - ["◀️"] = "arrow_backward", - ["🔼"] = "arrow_up_small", - ["🔽"] = "arrow_down_small", - ["➡️"] = "arrow_right", - ["⬅️"] = "arrow_left", - ["⬆️"] = "arrow_up", - ["⬇️"] = "arrow_down", - ["↗️"] = "arrow_upper_right", - ["↘️"] = "arrow_lower_right", - ["↙️"] = "arrow_lower_left", - ["↖️"] = "arrow_upper_left", - ["↕️"] = "arrow_up_down", - ["↔️"] = "left_right_arrow", - ["↪️"] = "arrow_right_hook", - ["↩️"] = "leftwards_arrow_with_hook", - ["⤴️"] = "arrow_heading_up", - ["⤵️"] = "arrow_heading_down", - ["🔀"] = "twisted_rightwards_arrows", - ["🔁"] = "repeat", - ["🔂"] = "repeat_one", - ["🔄"] = "arrows_counterclockwise", - ["🔃"] = "arrows_clockwise", - ["🎵"] = "musical_note", - ["🎶"] = "notes", - ["➕"] = "heavy_plus_sign", - ["➖"] = "heavy_minus_sign", - ["➗"] = "heavy_division_sign", - ["✖️"] = "heavy_multiplication_x", - ["♾️"] = "infinity", - ["💲"] = "heavy_dollar_sign", - ["💱"] = "currency_exchange", - ["™️"] = "tm", - ["©️"] = "copyright", - ["®️"] = "registered", - ["〰️"] = "wavy_dash", - ["➰"] = "curly_loop", - ["➿"] = "loop", - ["🔚"] = "end", - ["🔙"] = "back", - ["🔛"] = "on", - ["🔝"] = "top", - ["🔜"] = "soon", - ["✔️"] = "heavy_check_mark", - ["☑️"] = "ballot_box_with_check", - ["🔘"] = "radio_button", - ["⚪"] = "white_circle", - ["⚫"] = "black_circle", - ["🔴"] = "red_circle", - ["🔵"] = "blue_circle", - ["🟤"] = "brown_circle", - ["🟣"] = "purple_circle", - ["🟢"] = "green_circle", - ["🟡"] = "yellow_circle", - ["🟠"] = "orange_circle", - ["🔺"] = "small_red_triangle", - ["🔻"] = "small_red_triangle_down", - ["🔸"] = "small_orange_diamond", - ["🔹"] = "small_blue_diamond", - ["🔶"] = "large_orange_diamond", - ["🔷"] = "large_blue_diamond", - ["🔳"] = "white_square_button", - ["🔲"] = "black_square_button", - ["▪️"] = "black_small_square", - ["▫️"] = "white_small_square", - ["◾"] = "black_medium_small_square", - ["◽"] = "white_medium_small_square", - ["◼️"] = "black_medium_square", - ["◻️"] = "white_medium_square", - ["⬛"] = "black_large_square", - ["⬜"] = "white_large_square", - ["🟧"] = "orange_square", - ["🟦"] = "blue_square", - ["🟥"] = "red_square", - ["🟫"] = "brown_square", - ["🟪"] = "purple_square", - ["🟩"] = "green_square", - ["🟨"] = "yellow_square", - ["🔈"] = "speaker", - ["🔇"] = "mute", - ["🔉"] = "sound", - ["🔊"] = "loud_sound", - ["🔔"] = "bell", - ["🔕"] = "no_bell", - ["📣"] = "mega", - ["📢"] = "loudspeaker", - ["🗨️"] = "speech_left", - ["👁‍🗨"] = "eye_in_speech_bubble", - ["💬"] = "speech_balloon", - ["💭"] = "thought_balloon", - ["🗯️"] = "anger_right", - ["♠️"] = "spades", - ["♣️"] = "clubs", - ["♥️"] = "hearts", - ["♦️"] = "diamonds", - ["🃏"] = "black_joker", - ["🎴"] = "flower_playing_cards", - ["🀄"] = "mahjong", - ["🕐"] = "clock1", - ["🕑"] = "clock2", - ["🕒"] = "clock3", - ["🕓"] = "clock4", - ["🕔"] = "clock5", - ["🕕"] = "clock6", - ["🕖"] = "clock7", - ["🕗"] = "clock8", - ["🕘"] = "clock9", - ["🕙"] = "clock10", - ["🕚"] = "clock11", - ["🕛"] = "clock12", - ["🕜"] = "clock130", - ["🕝"] = "clock230", - ["🕞"] = "clock330", - ["🕟"] = "clock430", - ["🕠"] = "clock530", - ["🕡"] = "clock630", - ["🕢"] = "clock730", - ["🕣"] = "clock830", - ["🕤"] = "clock930", - ["🕥"] = "clock1030", - ["🕦"] = "clock1130", - ["🕧"] = "clock1230", - ["♀️"] = "female_sign", - ["♂️"] = "male_sign", - ["⚧"] = "transgender_symbol", - ["⚕️"] = "medical_symbol", - ["🇿"] = "regional_indicator_z", - ["🇾"] = "regional_indicator_y", - ["🇽"] = "regional_indicator_x", - ["🇼"] = "regional_indicator_w", - ["🇻"] = "regional_indicator_v", - ["🇺"] = "regional_indicator_u", - ["🇹"] = "regional_indicator_t", - ["🇸"] = "regional_indicator_s", - ["🇷"] = "regional_indicator_r", - ["🇶"] = "regional_indicator_q", - ["🇵"] = "regional_indicator_p", - ["🇴"] = "regional_indicator_o", - ["🇳"] = "regional_indicator_n", - ["🇲"] = "regional_indicator_m", - ["🇱"] = "regional_indicator_l", - ["🇰"] = "regional_indicator_k", - ["🇯"] = "regional_indicator_j", - ["🇮"] = "regional_indicator_i", - ["🇭"] = "regional_indicator_h", - ["🇬"] = "regional_indicator_g", - ["🇫"] = "regional_indicator_f", - ["🇪"] = "regional_indicator_e", - ["🇩"] = "regional_indicator_d", - ["🇨"] = "regional_indicator_c", - ["🇧"] = "regional_indicator_b", - ["🇦"] = "regional_indicator_a", - ["🏳️"] = "flag_white", - ["🏴"] = "flag_black", - ["🏁"] = "checkered_flag", - ["🚩"] = "triangular_flag_on_post", - ["🏳️‍🌈"] = "rainbow_flag", - ["🏳️‍⚧️"] = "transgender_flag", - ["🏴‍☠️"] = "pirate_flag", - ["🇦🇫"] = "flag_af", - ["🇦🇽"] = "flag_ax", - ["🇦🇱"] = "flag_al", - ["🇩🇿"] = "flag_dz", - ["🇦🇸"] = "flag_as", - ["🇦🇩"] = "flag_ad", - ["🇦🇴"] = "flag_ao", - ["🇦🇮"] = "flag_ai", - ["🇦🇶"] = "flag_aq", - ["🇦🇬"] = "flag_ag", - ["🇦🇷"] = "flag_ar", - ["🇦🇲"] = "flag_am", - ["🇦🇼"] = "flag_aw", - ["🇦🇺"] = "flag_au", - ["🇦🇹"] = "flag_at", - ["🇦🇿"] = "flag_az", - ["🇧🇸"] = "flag_bs", - ["🇧🇭"] = "flag_bh", - ["🇧🇩"] = "flag_bd", - ["🇧🇧"] = "flag_bb", - ["🇧🇾"] = "flag_by", - ["🇧🇪"] = "flag_be", - ["🇧🇿"] = "flag_bz", - ["🇧🇯"] = "flag_bj", - ["🇧🇲"] = "flag_bm", - ["🇧🇹"] = "flag_bt", - ["🇧🇴"] = "flag_bo", - ["🇧🇦"] = "flag_ba", - ["🇧🇼"] = "flag_bw", - ["🇧🇷"] = "flag_br", - ["🇮🇴"] = "flag_io", - ["🇻🇬"] = "flag_vg", - ["🇧🇳"] = "flag_bn", - ["🇧🇬"] = "flag_bg", - ["🇧🇫"] = "flag_bf", - ["🇧🇮"] = "flag_bi", - ["🇰🇭"] = "flag_kh", - ["🇨🇲"] = "flag_cm", - ["🇨🇦"] = "flag_ca", - ["🇮🇨"] = "flag_ic", - ["🇨🇻"] = "flag_cv", - ["🇧🇶"] = "flag_bq", - ["🇰🇾"] = "flag_ky", - ["🇨🇫"] = "flag_cf", - ["🇹🇩"] = "flag_td", - ["🇨🇱"] = "flag_cl", - ["🇨🇳"] = "flag_cn", - ["🇨🇽"] = "flag_cx", - ["🇨🇨"] = "flag_cc", - ["🇨🇴"] = "flag_co", - ["🇰🇲"] = "flag_km", - ["🇨🇬"] = "flag_cg", - ["🇨🇩"] = "flag_cd", - ["🇨🇰"] = "flag_ck", - ["🇨🇷"] = "flag_cr", - ["🇨🇮"] = "flag_ci", - ["🇭🇷"] = "flag_hr", - ["🇨🇺"] = "flag_cu", - ["🇨🇼"] = "flag_cw", - ["🇨🇾"] = "flag_cy", - ["🇨🇿"] = "flag_cz", - ["🇩🇰"] = "flag_dk", - ["🇩🇯"] = "flag_dj", - ["🇩🇲"] = "flag_dm", - ["🇩🇴"] = "flag_do", - ["🇪🇨"] = "flag_ec", - ["🇪🇬"] = "flag_eg", - ["🇸🇻"] = "flag_sv", - ["🇬🇶"] = "flag_gq", - ["🇪🇷"] = "flag_er", - ["🇪🇪"] = "flag_ee", - ["🇪🇹"] = "flag_et", - ["🇪🇺"] = "flag_eu", - ["🇫🇰"] = "flag_fk", - ["🇫🇴"] = "flag_fo", - ["🇫🇯"] = "flag_fj", - ["🇫🇮"] = "flag_fi", - ["🇫🇷"] = "flag_fr", - ["🇬🇫"] = "flag_gf", - ["🇵🇫"] = "flag_pf", - ["🇹🇫"] = "flag_tf", - ["🇬🇦"] = "flag_ga", - ["🇬🇲"] = "flag_gm", - ["🇬🇪"] = "flag_ge", - ["🇩🇪"] = "flag_de", - ["🇬🇭"] = "flag_gh", - ["🇬🇮"] = "flag_gi", - ["🇬🇷"] = "flag_gr", - ["🇬🇱"] = "flag_gl", - ["🇬🇩"] = "flag_gd", - ["🇬🇵"] = "flag_gp", - ["🇬🇺"] = "flag_gu", - ["🇬🇹"] = "flag_gt", - ["🇬🇬"] = "flag_gg", - ["🇬🇳"] = "flag_gn", - ["🇬🇼"] = "flag_gw", - ["🇬🇾"] = "flag_gy", - ["🇭🇹"] = "flag_ht", - ["🇭🇳"] = "flag_hn", - ["🇭🇰"] = "flag_hk", - ["🇭🇺"] = "flag_hu", - ["🇮🇸"] = "flag_is", - ["🇮🇳"] = "flag_in", - ["🇮🇩"] = "flag_id", - ["🇮🇷"] = "flag_ir", - ["🇮🇶"] = "flag_iq", - ["🇮🇪"] = "flag_ie", - ["🇮🇲"] = "flag_im", - ["🇮🇱"] = "flag_il", - ["🇮🇹"] = "flag_it", - ["🇯🇲"] = "flag_jm", - ["🇯🇵"] = "flag_jp", - ["🎌"] = "crossed_flags", - ["🇯🇪"] = "flag_je", - ["🇯🇴"] = "flag_jo", - ["🇰🇿"] = "flag_kz", - ["🇰🇪"] = "flag_ke", - ["🇰🇮"] = "flag_ki", - ["🇽🇰"] = "flag_xk", - ["🇰🇼"] = "flag_kw", - ["🇰🇬"] = "flag_kg", - ["🇱🇦"] = "flag_la", - ["🇱🇻"] = "flag_lv", - ["🇱🇧"] = "flag_lb", - ["🇱🇸"] = "flag_ls", - ["🇱🇷"] = "flag_lr", - ["🇱🇾"] = "flag_ly", - ["🇱🇮"] = "flag_li", - ["🇱🇹"] = "flag_lt", - ["🇱🇺"] = "flag_lu", - ["🇲🇴"] = "flag_mo", - ["🇲🇰"] = "flag_mk", - ["🇲🇬"] = "flag_mg", - ["🇲🇼"] = "flag_mw", - ["🇲🇾"] = "flag_my", - ["🇲🇻"] = "flag_mv", - ["🇲🇱"] = "flag_ml", - ["🇲🇹"] = "flag_mt", - ["🇲🇭"] = "flag_mh", - ["🇲🇶"] = "flag_mq", - ["🇲🇷"] = "flag_mr", - ["🇲🇺"] = "flag_mu", - ["🇾🇹"] = "flag_yt", - ["🇲🇽"] = "flag_mx", - ["🇫🇲"] = "flag_fm", - ["🇲🇩"] = "flag_md", - ["🇲🇨"] = "flag_mc", - ["🇲🇳"] = "flag_mn", - ["🇲🇪"] = "flag_me", - ["🇲🇸"] = "flag_ms", - ["🇲🇦"] = "flag_ma", - ["🇲🇿"] = "flag_mz", - ["🇲🇲"] = "flag_mm", - ["🇳🇦"] = "flag_na", - ["🇳🇷"] = "flag_nr", - ["🇳🇵"] = "flag_np", - ["🇳🇱"] = "flag_nl", - ["🇳🇨"] = "flag_nc", - ["🇳🇿"] = "flag_nz", - ["🇳🇮"] = "flag_ni", - ["🇳🇪"] = "flag_ne", - ["🇳🇬"] = "flag_ng", - ["🇳🇺"] = "flag_nu", - ["🇳🇫"] = "flag_nf", - ["🇰🇵"] = "flag_kp", - ["🇲🇵"] = "flag_mp", - ["🇳🇴"] = "flag_no", - ["🇴🇲"] = "flag_om", - ["🇵🇰"] = "flag_pk", - ["🇵🇼"] = "flag_pw", - ["🇵🇸"] = "flag_ps", - ["🇵🇦"] = "flag_pa", - ["🇵🇬"] = "flag_pg", - ["🇵🇾"] = "flag_py", - ["🇵🇪"] = "flag_pe", - ["🇵🇭"] = "flag_ph", - ["🇵🇳"] = "flag_pn", - ["🇵🇱"] = "flag_pl", - ["🇵🇹"] = "flag_pt", - ["🇵🇷"] = "flag_pr", - ["🇶🇦"] = "flag_qa", - ["🇷🇪"] = "flag_re", - ["🇷🇴"] = "flag_ro", - ["🇷🇺"] = "flag_ru", - ["🇷🇼"] = "flag_rw", - ["🇼🇸"] = "flag_ws", - ["🇸🇲"] = "flag_sm", - ["🇸🇹"] = "flag_st", - ["🇸🇦"] = "flag_sa", - ["🇸🇳"] = "flag_sn", - ["🇷🇸"] = "flag_rs", - ["🇸🇨"] = "flag_sc", - ["🇸🇱"] = "flag_sl", - ["🇸🇬"] = "flag_sg", - ["🇸🇽"] = "flag_sx", - ["🇸🇰"] = "flag_sk", - ["🇸🇮"] = "flag_si", - ["🇬🇸"] = "flag_gs", - ["🇸🇧"] = "flag_sb", - ["🇸🇴"] = "flag_so", - ["🇿🇦"] = "flag_za", - ["🇰🇷"] = "flag_kr", - ["🇸🇸"] = "flag_ss", - ["🇪🇸"] = "flag_es", - ["🇱🇰"] = "flag_lk", - ["🇧🇱"] = "flag_bl", - ["🇸🇭"] = "flag_sh", - ["🇰🇳"] = "flag_kn", - ["🇱🇨"] = "flag_lc", - ["🇵🇲"] = "flag_pm", - ["🇻🇨"] = "flag_vc", - ["🇸🇩"] = "flag_sd", - ["🇸🇷"] = "flag_sr", - ["🇸🇿"] = "flag_sz", - ["🇸🇪"] = "flag_se", - ["🇨🇭"] = "flag_ch", - ["🇸🇾"] = "flag_sy", - ["🇹🇼"] = "flag_tw", - ["🇹🇯"] = "flag_tj", - ["🇹🇿"] = "flag_tz", - ["🇹🇭"] = "flag_th", - ["🇹🇱"] = "flag_tl", - ["🇹🇬"] = "flag_tg", - ["🇹🇰"] = "flag_tk", - ["🇹🇴"] = "flag_to", - ["🇹🇹"] = "flag_tt", - ["🇹🇳"] = "flag_tn", - ["🇹🇷"] = "flag_tr", - ["🇹🇲"] = "flag_tm", - ["🇹🇨"] = "flag_tc", - ["🇻🇮"] = "flag_vi", - ["🇹🇻"] = "flag_tv", - ["🇺🇬"] = "flag_ug", - ["🇺🇦"] = "flag_ua", - ["🇦🇪"] = "flag_ae", - ["🇬🇧"] = "flag_gb", - ["🏴󠁧󠁢󠁥󠁮󠁧󠁿"] = "england", - ["🏴󠁧󠁢󠁳󠁣󠁴󠁿"] = "scotland", - ["🏴󠁧󠁢󠁷󠁬󠁳󠁿"] = "wales", - ["🇺🇸"] = "flag_us", - ["🇺🇾"] = "flag_uy", - ["🇺🇿"] = "flag_uz", - ["🇻🇺"] = "flag_vu", - ["🇻🇦"] = "flag_va", - ["🇻🇪"] = "flag_ve", - ["🇻🇳"] = "flag_vn", - ["🇼🇫"] = "flag_wf", - ["🇪🇭"] = "flag_eh", - ["🇾🇪"] = "flag_ye", - ["🇿🇲"] = "flag_zm", - ["🇿🇼"] = "flag_zw", - ["🇦🇨"] = "flag_ac", - ["🇧🇻"] = "flag_bv", - ["🇨🇵"] = "flag_cp", - ["🇪🇦"] = "flag_ea", - ["🇩🇬"] = "flag_dg", - ["🇭🇲"] = "flag_hm", - ["🇲🇫"] = "flag_mf", - ["🇸🇯"] = "flag_sj", - ["🇹🇦"] = "flag_ta", - ["🇺🇲"] = "flag_um", - ["🇺🇳"] = "united_nations" - }; + private static Dictionary _toCodes = new(5000, StringComparer.Ordinal) + { + ["😀"] = "grinning", + ["😃"] = "smiley", + ["😄"] = "smile", + ["😁"] = "grin", + ["😆"] = "laughing", + ["😅"] = "sweat_smile", + ["😂"] = "joy", + ["🤣"] = "rofl", + ["☺️"] = "relaxed", + ["😊"] = "blush", + ["😇"] = "innocent", + ["🙂"] = "slight_smile", + ["🙃"] = "upside_down", + ["😉"] = "wink", + ["😌"] = "relieved", + ["🥲"] = "smiling_face_with_tear", + ["😍"] = "heart_eyes", + ["🥰"] = "smiling_face_with_3_hearts", + ["😘"] = "kissing_heart", + ["😗"] = "kissing", + ["😙"] = "kissing_smiling_eyes", + ["😚"] = "kissing_closed_eyes", + ["😋"] = "yum", + ["😛"] = "stuck_out_tongue", + ["😝"] = "stuck_out_tongue_closed_eyes", + ["😜"] = "stuck_out_tongue_winking_eye", + ["🤪"] = "zany_face", + ["🤨"] = "face_with_raised_eyebrow", + ["🧐"] = "face_with_monocle", + ["🤓"] = "nerd", + ["😎"] = "sunglasses", + ["🤩"] = "star_struck", + ["🥳"] = "partying_face", + ["😏"] = "smirk", + ["😒"] = "unamused", + ["😞"] = "disappointed", + ["😔"] = "pensive", + ["😟"] = "worried", + ["😕"] = "confused", + ["🙁"] = "slight_frown", + ["☹️"] = "frowning2", + ["😣"] = "persevere", + ["😖"] = "confounded", + ["😫"] = "tired_face", + ["😩"] = "weary", + ["🥺"] = "pleading_face", + ["😢"] = "cry", + ["😭"] = "sob", + ["😤"] = "triumph", + ["😮‍💨"] = "face_exhaling", + ["😠"] = "angry", + ["😡"] = "rage", + ["🤬"] = "face_with_symbols_over_mouth", + ["🤯"] = "exploding_head", + ["😳"] = "flushed", + ["😶‍🌫️"] = "face_in_clouds", + ["🥵"] = "hot_face", + ["🥶"] = "cold_face", + ["😱"] = "scream", + ["😨"] = "fearful", + ["😰"] = "cold_sweat", + ["😥"] = "disappointed_relieved", + ["😓"] = "sweat", + ["🤗"] = "hugging", + ["🤔"] = "thinking", + ["🤭"] = "face_with_hand_over_mouth", + ["🥱"] = "yawning_face", + ["🤫"] = "shushing_face", + ["🤥"] = "lying_face", + ["😶"] = "no_mouth", + ["😐"] = "neutral_face", + ["😑"] = "expressionless", + ["😬"] = "grimacing", + ["🙄"] = "rolling_eyes", + ["😯"] = "hushed", + ["😦"] = "frowning", + ["😧"] = "anguished", + ["😮"] = "open_mouth", + ["😲"] = "astonished", + ["😴"] = "sleeping", + ["🤤"] = "drooling_face", + ["😪"] = "sleepy", + ["😵"] = "dizzy_face", + ["😵‍💫"] = "face_with_spiral_eyes", + ["🤐"] = "zipper_mouth", + ["🥴"] = "woozy_face", + ["🤢"] = "nauseated_face", + ["🤮"] = "face_vomiting", + ["🤧"] = "sneezing_face", + ["😷"] = "mask", + ["🤒"] = "thermometer_face", + ["🤕"] = "head_bandage", + ["🤑"] = "money_mouth", + ["🤠"] = "cowboy", + ["🥸"] = "disguised_face", + ["😈"] = "smiling_imp", + ["👿"] = "imp", + ["👹"] = "japanese_ogre", + ["👺"] = "japanese_goblin", + ["🤡"] = "clown", + ["💩"] = "poop", + ["👻"] = "ghost", + ["💀"] = "skull", + ["☠️"] = "skull_crossbones", + ["👽"] = "alien", + ["👾"] = "space_invader", + ["🤖"] = "robot", + ["🎃"] = "jack_o_lantern", + ["😺"] = "smiley_cat", + ["😸"] = "smile_cat", + ["😹"] = "joy_cat", + ["😻"] = "heart_eyes_cat", + ["😼"] = "smirk_cat", + ["😽"] = "kissing_cat", + ["🙀"] = "scream_cat", + ["😿"] = "crying_cat_face", + ["😾"] = "pouting_cat", + ["🤲"] = "palms_up_together", + ["🤲🏻"] = "palms_up_together_tone1", + ["🤲🏼"] = "palms_up_together_tone2", + ["🤲🏽"] = "palms_up_together_tone3", + ["🤲🏾"] = "palms_up_together_tone4", + ["🤲🏿"] = "palms_up_together_tone5", + ["👐"] = "open_hands", + ["👐🏻"] = "open_hands_tone1", + ["👐🏼"] = "open_hands_tone2", + ["👐🏽"] = "open_hands_tone3", + ["👐🏾"] = "open_hands_tone4", + ["👐🏿"] = "open_hands_tone5", + ["🙌"] = "raised_hands", + ["🙌🏻"] = "raised_hands_tone1", + ["🙌🏼"] = "raised_hands_tone2", + ["🙌🏽"] = "raised_hands_tone3", + ["🙌🏾"] = "raised_hands_tone4", + ["🙌🏿"] = "raised_hands_tone5", + ["👏"] = "clap", + ["👏🏻"] = "clap_tone1", + ["👏🏼"] = "clap_tone2", + ["👏🏽"] = "clap_tone3", + ["👏🏾"] = "clap_tone4", + ["👏🏿"] = "clap_tone5", + ["🤝"] = "handshake", + ["👍"] = "thumbsup", + ["👍🏻"] = "thumbsup_tone1", + ["👍🏼"] = "thumbsup_tone2", + ["👍🏽"] = "thumbsup_tone3", + ["👍🏾"] = "thumbsup_tone4", + ["👍🏿"] = "thumbsup_tone5", + ["👎"] = "thumbsdown", + ["👎🏻"] = "thumbsdown_tone1", + ["👎🏼"] = "thumbsdown_tone2", + ["👎🏽"] = "thumbsdown_tone3", + ["👎🏾"] = "thumbsdown_tone4", + ["👎🏿"] = "thumbsdown_tone5", + ["👊"] = "punch", + ["👊🏻"] = "punch_tone1", + ["👊🏼"] = "punch_tone2", + ["👊🏽"] = "punch_tone3", + ["👊🏾"] = "punch_tone4", + ["👊🏿"] = "punch_tone5", + ["✊"] = "fist", + ["✊🏻"] = "fist_tone1", + ["✊🏼"] = "fist_tone2", + ["✊🏽"] = "fist_tone3", + ["✊🏾"] = "fist_tone4", + ["✊🏿"] = "fist_tone5", + ["🤛"] = "left_facing_fist", + ["🤛🏻"] = "left_facing_fist_tone1", + ["🤛🏼"] = "left_facing_fist_tone2", + ["🤛🏽"] = "left_facing_fist_tone3", + ["🤛🏾"] = "left_facing_fist_tone4", + ["🤛🏿"] = "left_facing_fist_tone5", + ["🤜"] = "right_facing_fist", + ["🤜🏻"] = "right_facing_fist_tone1", + ["🤜🏼"] = "right_facing_fist_tone2", + ["🤜🏽"] = "right_facing_fist_tone3", + ["🤜🏾"] = "right_facing_fist_tone4", + ["🤜🏿"] = "right_facing_fist_tone5", + ["🤞"] = "fingers_crossed", + ["🤞🏻"] = "fingers_crossed_tone1", + ["🤞🏼"] = "fingers_crossed_tone2", + ["🤞🏽"] = "fingers_crossed_tone3", + ["🤞🏾"] = "fingers_crossed_tone4", + ["🤞🏿"] = "fingers_crossed_tone5", + ["✌️"] = "v", + ["✌🏻"] = "v_tone1", + ["✌🏼"] = "v_tone2", + ["✌🏽"] = "v_tone3", + ["✌🏾"] = "v_tone4", + ["✌🏿"] = "v_tone5", + ["🤟"] = "love_you_gesture", + ["🤟🏻"] = "love_you_gesture_tone1", + ["🤟🏼"] = "love_you_gesture_tone2", + ["🤟🏽"] = "love_you_gesture_tone3", + ["🤟🏾"] = "love_you_gesture_tone4", + ["🤟🏿"] = "love_you_gesture_tone5", + ["🤘"] = "metal", + ["🤘🏻"] = "metal_tone1", + ["🤘🏼"] = "metal_tone2", + ["🤘🏽"] = "metal_tone3", + ["🤘🏾"] = "metal_tone4", + ["🤘🏿"] = "metal_tone5", + ["👌"] = "ok_hand", + ["👌🏻"] = "ok_hand_tone1", + ["👌🏼"] = "ok_hand_tone2", + ["👌🏽"] = "ok_hand_tone3", + ["👌🏾"] = "ok_hand_tone4", + ["👌🏿"] = "ok_hand_tone5", + ["🤏"] = "pinching_hand", + ["🤏🏻"] = "pinching_hand_tone1", + ["🤏🏼"] = "pinching_hand_tone2", + ["🤏🏽"] = "pinching_hand_tone3", + ["🤏🏾"] = "pinching_hand_tone4", + ["🤏🏿"] = "pinching_hand_tone5", + ["🤌"] = "pinched_fingers", + ["🤌🏼"] = "pinched_fingers_tone2", + ["🤌🏻"] = "pinched_fingers_tone1", + ["🤌🏽"] = "pinched_fingers_tone3", + ["🤌🏾"] = "pinched_fingers_tone4", + ["🤌🏿"] = "pinched_fingers_tone5", + ["👈"] = "point_left", + ["👈🏻"] = "point_left_tone1", + ["👈🏼"] = "point_left_tone2", + ["👈🏽"] = "point_left_tone3", + ["👈🏾"] = "point_left_tone4", + ["👈🏿"] = "point_left_tone5", + ["👉"] = "point_right", + ["👉🏻"] = "point_right_tone1", + ["👉🏼"] = "point_right_tone2", + ["👉🏽"] = "point_right_tone3", + ["👉🏾"] = "point_right_tone4", + ["👉🏿"] = "point_right_tone5", + ["👆"] = "point_up_2", + ["👆🏻"] = "point_up_2_tone1", + ["👆🏼"] = "point_up_2_tone2", + ["👆🏽"] = "point_up_2_tone3", + ["👆🏾"] = "point_up_2_tone4", + ["👆🏿"] = "point_up_2_tone5", + ["👇"] = "point_down", + ["👇🏻"] = "point_down_tone1", + ["👇🏼"] = "point_down_tone2", + ["👇🏽"] = "point_down_tone3", + ["👇🏾"] = "point_down_tone4", + ["👇🏿"] = "point_down_tone5", + ["☝️"] = "point_up", + ["☝🏻"] = "point_up_tone1", + ["☝🏼"] = "point_up_tone2", + ["☝🏽"] = "point_up_tone3", + ["☝🏾"] = "point_up_tone4", + ["☝🏿"] = "point_up_tone5", + ["✋"] = "raised_hand", + ["✋🏻"] = "raised_hand_tone1", + ["✋🏼"] = "raised_hand_tone2", + ["✋🏽"] = "raised_hand_tone3", + ["✋🏾"] = "raised_hand_tone4", + ["✋🏿"] = "raised_hand_tone5", + ["🤚"] = "raised_back_of_hand", + ["🤚🏻"] = "raised_back_of_hand_tone1", + ["🤚🏼"] = "raised_back_of_hand_tone2", + ["🤚🏽"] = "raised_back_of_hand_tone3", + ["🤚🏾"] = "raised_back_of_hand_tone4", + ["🤚🏿"] = "raised_back_of_hand_tone5", + ["🖐️"] = "hand_splayed", + ["🖐🏻"] = "hand_splayed_tone1", + ["🖐🏼"] = "hand_splayed_tone2", + ["🖐🏽"] = "hand_splayed_tone3", + ["🖐🏾"] = "hand_splayed_tone4", + ["🖐🏿"] = "hand_splayed_tone5", + ["🖖"] = "vulcan", + ["🖖🏻"] = "vulcan_tone1", + ["🖖🏼"] = "vulcan_tone2", + ["🖖🏽"] = "vulcan_tone3", + ["🖖🏾"] = "vulcan_tone4", + ["🖖🏿"] = "vulcan_tone5", + ["👋"] = "wave", + ["👋🏻"] = "wave_tone1", + ["👋🏼"] = "wave_tone2", + ["👋🏽"] = "wave_tone3", + ["👋🏾"] = "wave_tone4", + ["👋🏿"] = "wave_tone5", + ["🤙"] = "call_me", + ["🤙🏻"] = "call_me_tone1", + ["🤙🏼"] = "call_me_tone2", + ["🤙🏽"] = "call_me_tone3", + ["🤙🏾"] = "call_me_tone4", + ["🤙🏿"] = "call_me_tone5", + ["💪"] = "muscle", + ["💪🏻"] = "muscle_tone1", + ["💪🏼"] = "muscle_tone2", + ["💪🏽"] = "muscle_tone3", + ["💪🏾"] = "muscle_tone4", + ["💪🏿"] = "muscle_tone5", + ["🦾"] = "mechanical_arm", + ["🖕"] = "middle_finger", + ["🖕🏻"] = "middle_finger_tone1", + ["🖕🏼"] = "middle_finger_tone2", + ["🖕🏽"] = "middle_finger_tone3", + ["🖕🏾"] = "middle_finger_tone4", + ["🖕🏿"] = "middle_finger_tone5", + ["✍️"] = "writing_hand", + ["✍🏻"] = "writing_hand_tone1", + ["✍🏼"] = "writing_hand_tone2", + ["✍🏽"] = "writing_hand_tone3", + ["✍🏾"] = "writing_hand_tone4", + ["✍🏿"] = "writing_hand_tone5", + ["🙏"] = "pray", + ["🙏🏻"] = "pray_tone1", + ["🙏🏼"] = "pray_tone2", + ["🙏🏽"] = "pray_tone3", + ["🙏🏾"] = "pray_tone4", + ["🙏🏿"] = "pray_tone5", + ["🦶"] = "foot", + ["🦶🏻"] = "foot_tone1", + ["🦶🏼"] = "foot_tone2", + ["🦶🏽"] = "foot_tone3", + ["🦶🏾"] = "foot_tone4", + ["🦶🏿"] = "foot_tone5", + ["🦵"] = "leg", + ["🦵🏻"] = "leg_tone1", + ["🦵🏼"] = "leg_tone2", + ["🦵🏽"] = "leg_tone3", + ["🦵🏾"] = "leg_tone4", + ["🦵🏿"] = "leg_tone5", + ["🦿"] = "mechanical_leg", + ["💄"] = "lipstick", + ["💋"] = "kiss", + ["👄"] = "lips", + ["🦷"] = "tooth", + ["👅"] = "tongue", + ["👂"] = "ear", + ["👂🏻"] = "ear_tone1", + ["👂🏼"] = "ear_tone2", + ["👂🏽"] = "ear_tone3", + ["👂🏾"] = "ear_tone4", + ["👂🏿"] = "ear_tone5", + ["🦻"] = "ear_with_hearing_aid", + ["🦻🏻"] = "ear_with_hearing_aid_tone1", + ["🦻🏼"] = "ear_with_hearing_aid_tone2", + ["🦻🏽"] = "ear_with_hearing_aid_tone3", + ["🦻🏾"] = "ear_with_hearing_aid_tone4", + ["🦻🏿"] = "ear_with_hearing_aid_tone5", + ["👃"] = "nose", + ["👃🏻"] = "nose_tone1", + ["👃🏼"] = "nose_tone2", + ["👃🏽"] = "nose_tone3", + ["👃🏾"] = "nose_tone4", + ["👃🏿"] = "nose_tone5", + ["👣"] = "footprints", + ["👁️"] = "eye", + ["👀"] = "eyes", + ["🧠"] = "brain", + ["🫀"] = "anatomical_heart", + ["🫁"] = "lungs", + ["🦴"] = "bone", + ["🗣️"] = "speaking_head", + ["👤"] = "bust_in_silhouette", + ["👥"] = "busts_in_silhouette", + ["🫂"] = "people_hugging", + ["👶"] = "baby", + ["👶🏻"] = "baby_tone1", + ["👶🏼"] = "baby_tone2", + ["👶🏽"] = "baby_tone3", + ["👶🏾"] = "baby_tone4", + ["👶🏿"] = "baby_tone5", + ["👧"] = "girl", + ["👧🏻"] = "girl_tone1", + ["👧🏼"] = "girl_tone2", + ["👧🏽"] = "girl_tone3", + ["👧🏾"] = "girl_tone4", + ["👧🏿"] = "girl_tone5", + ["🧒"] = "child", + ["🧒🏻"] = "child_tone1", + ["🧒🏼"] = "child_tone2", + ["🧒🏽"] = "child_tone3", + ["🧒🏾"] = "child_tone4", + ["🧒🏿"] = "child_tone5", + ["👦"] = "boy", + ["👦🏻"] = "boy_tone1", + ["👦🏼"] = "boy_tone2", + ["👦🏽"] = "boy_tone3", + ["👦🏾"] = "boy_tone4", + ["👦🏿"] = "boy_tone5", + ["👩"] = "woman", + ["👩🏻"] = "woman_tone1", + ["👩🏼"] = "woman_tone2", + ["👩🏽"] = "woman_tone3", + ["👩🏾"] = "woman_tone4", + ["👩🏿"] = "woman_tone5", + ["🧑"] = "adult", + ["🧑🏻"] = "adult_tone1", + ["🧑🏼"] = "adult_tone2", + ["🧑🏽"] = "adult_tone3", + ["🧑🏾"] = "adult_tone4", + ["🧑🏿"] = "adult_tone5", + ["👨"] = "man", + ["👨🏻"] = "man_tone1", + ["👨🏼"] = "man_tone2", + ["👨🏽"] = "man_tone3", + ["👨🏾"] = "man_tone4", + ["👨🏿"] = "man_tone5", + ["🧑‍🦱"] = "person_curly_hair", + ["🧑🏻‍🦱"] = "person_tone1_curly_hair", + ["🧑🏼‍🦱"] = "person_tone2_curly_hair", + ["🧑🏽‍🦱"] = "person_tone3_curly_hair", + ["🧑🏾‍🦱"] = "person_tone4_curly_hair", + ["🧑🏿‍🦱"] = "person_tone5_curly_hair", + ["👩‍🦱"] = "woman_curly_haired", + ["👩🏻‍🦱"] = "woman_curly_haired_tone1", + ["👩🏼‍🦱"] = "woman_curly_haired_tone2", + ["👩🏽‍🦱"] = "woman_curly_haired_tone3", + ["👩🏾‍🦱"] = "woman_curly_haired_tone4", + ["👩🏿‍🦱"] = "woman_curly_haired_tone5", + ["👨‍🦱"] = "man_curly_haired", + ["👨🏻‍🦱"] = "man_curly_haired_tone1", + ["👨🏼‍🦱"] = "man_curly_haired_tone2", + ["👨🏽‍🦱"] = "man_curly_haired_tone3", + ["👨🏾‍🦱"] = "man_curly_haired_tone4", + ["👨🏿‍🦱"] = "man_curly_haired_tone5", + ["🧑‍🦰"] = "person_red_hair", + ["🧑🏻‍🦰"] = "person_tone1_red_hair", + ["🧑🏼‍🦰"] = "person_tone2_red_hair", + ["🧑🏽‍🦰"] = "person_tone3_red_hair", + ["🧑🏾‍🦰"] = "person_tone4_red_hair", + ["🧑🏿‍🦰"] = "person_tone5_red_hair", + ["👩‍🦰"] = "woman_red_haired", + ["👩🏻‍🦰"] = "woman_red_haired_tone1", + ["👩🏼‍🦰"] = "woman_red_haired_tone2", + ["👩🏽‍🦰"] = "woman_red_haired_tone3", + ["👩🏾‍🦰"] = "woman_red_haired_tone4", + ["👩🏿‍🦰"] = "woman_red_haired_tone5", + ["👨‍🦰"] = "man_red_haired", + ["👨🏻‍🦰"] = "man_red_haired_tone1", + ["👨🏼‍🦰"] = "man_red_haired_tone2", + ["👨🏽‍🦰"] = "man_red_haired_tone3", + ["👨🏾‍🦰"] = "man_red_haired_tone4", + ["👨🏿‍🦰"] = "man_red_haired_tone5", + ["👱‍♀️"] = "blond_haired_woman", + ["👱🏻‍♀️"] = "blond_haired_woman_tone1", + ["👱🏼‍♀️"] = "blond_haired_woman_tone2", + ["👱🏽‍♀️"] = "blond_haired_woman_tone3", + ["👱🏾‍♀️"] = "blond_haired_woman_tone4", + ["👱🏿‍♀️"] = "blond_haired_woman_tone5", + ["👱"] = "blond_haired_person", + ["👱🏻"] = "blond_haired_person_tone1", + ["👱🏼"] = "blond_haired_person_tone2", + ["👱🏽"] = "blond_haired_person_tone3", + ["👱🏾"] = "blond_haired_person_tone4", + ["👱🏿"] = "blond_haired_person_tone5", + ["👱‍♂️"] = "blond_haired_man", + ["👱🏻‍♂️"] = "blond_haired_man_tone1", + ["👱🏼‍♂️"] = "blond_haired_man_tone2", + ["👱🏽‍♂️"] = "blond_haired_man_tone3", + ["👱🏾‍♂️"] = "blond_haired_man_tone4", + ["👱🏿‍♂️"] = "blond_haired_man_tone5", + ["🧑‍🦳"] = "person_white_hair", + ["🧑🏻‍🦳"] = "person_tone1_white_hair", + ["🧑🏼‍🦳"] = "person_tone2_white_hair", + ["🧑🏽‍🦳"] = "person_tone3_white_hair", + ["🧑🏾‍🦳"] = "person_tone4_white_hair", + ["🧑🏿‍🦳"] = "person_tone5_white_hair", + ["👩‍🦳"] = "woman_white_haired", + ["👩🏻‍🦳"] = "woman_white_haired_tone1", + ["👩🏼‍🦳"] = "woman_white_haired_tone2", + ["👩🏽‍🦳"] = "woman_white_haired_tone3", + ["👩🏾‍🦳"] = "woman_white_haired_tone4", + ["👩🏿‍🦳"] = "woman_white_haired_tone5", + ["👨‍🦳"] = "man_white_haired", + ["👨🏻‍🦳"] = "man_white_haired_tone1", + ["👨🏼‍🦳"] = "man_white_haired_tone2", + ["👨🏽‍🦳"] = "man_white_haired_tone3", + ["👨🏾‍🦳"] = "man_white_haired_tone4", + ["👨🏿‍🦳"] = "man_white_haired_tone5", + ["🧑‍🦲"] = "person_bald", + ["🧑🏻‍🦲"] = "person_tone1_bald", + ["🧑🏼‍🦲"] = "person_tone2_bald", + ["🧑🏽‍🦲"] = "person_tone3_bald", + ["🧑🏾‍🦲"] = "person_tone4_bald", + ["🧑🏿‍🦲"] = "person_tone5_bald", + ["👩‍🦲"] = "woman_bald", + ["👩🏻‍🦲"] = "woman_bald_tone1", + ["👩🏼‍🦲"] = "woman_bald_tone2", + ["👩🏽‍🦲"] = "woman_bald_tone3", + ["👩🏾‍🦲"] = "woman_bald_tone4", + ["👩🏿‍🦲"] = "woman_bald_tone5", + ["👨‍🦲"] = "man_bald", + ["👨🏻‍🦲"] = "man_bald_tone1", + ["👨🏼‍🦲"] = "man_bald_tone2", + ["👨🏽‍🦲"] = "man_bald_tone3", + ["👨🏾‍🦲"] = "man_bald_tone4", + ["👨🏿‍🦲"] = "man_bald_tone5", + ["🧔"] = "bearded_person", + ["🧔🏻"] = "bearded_person_tone1", + ["🧔🏼"] = "bearded_person_tone2", + ["🧔🏽"] = "bearded_person_tone3", + ["🧔🏾"] = "bearded_person_tone4", + ["🧔🏿"] = "bearded_person_tone5", + ["🧔‍♂️"] = "man_beard", + ["🧔🏻‍♂️"] = "man_tone1_beard", + ["🧔🏼‍♂️"] = "man_tone2_beard", + ["🧔🏽‍♂️"] = "man_tone3_beard", + ["🧔🏾‍♂️"] = "man_tone4_beard", + ["🧔🏿‍♂️"] = "man_tone5_beard", + ["🧔‍♀️"] = "woman_beard", + ["🧔🏻‍♀️"] = "woman_tone1_beard", + ["🧔🏼‍♀️"] = "woman_tone2_beard", + ["🧔🏽‍♀️"] = "woman_tone3_beard", + ["🧔🏾‍♀️"] = "woman_tone4_beard", + ["🧔🏿‍♀️"] = "woman_tone5_beard", + ["👵"] = "older_woman", + ["👵🏻"] = "older_woman_tone1", + ["👵🏼"] = "older_woman_tone2", + ["👵🏽"] = "older_woman_tone3", + ["👵🏾"] = "older_woman_tone4", + ["👵🏿"] = "older_woman_tone5", + ["🧓"] = "older_adult", + ["🧓🏻"] = "older_adult_tone1", + ["🧓🏼"] = "older_adult_tone2", + ["🧓🏽"] = "older_adult_tone3", + ["🧓🏾"] = "older_adult_tone4", + ["🧓🏿"] = "older_adult_tone5", + ["👴"] = "older_man", + ["👴🏻"] = "older_man_tone1", + ["👴🏼"] = "older_man_tone2", + ["👴🏽"] = "older_man_tone3", + ["👴🏾"] = "older_man_tone4", + ["👴🏿"] = "older_man_tone5", + ["👲"] = "man_with_chinese_cap", + ["👲🏻"] = "man_with_chinese_cap_tone1", + ["👲🏼"] = "man_with_chinese_cap_tone2", + ["👲🏽"] = "man_with_chinese_cap_tone3", + ["👲🏾"] = "man_with_chinese_cap_tone4", + ["👲🏿"] = "man_with_chinese_cap_tone5", + ["👳"] = "person_wearing_turban", + ["👳🏻"] = "person_wearing_turban_tone1", + ["👳🏼"] = "person_wearing_turban_tone2", + ["👳🏽"] = "person_wearing_turban_tone3", + ["👳🏾"] = "person_wearing_turban_tone4", + ["👳🏿"] = "person_wearing_turban_tone5", + ["👳‍♀️"] = "woman_wearing_turban", + ["👳🏻‍♀️"] = "woman_wearing_turban_tone1", + ["👳🏼‍♀️"] = "woman_wearing_turban_tone2", + ["👳🏽‍♀️"] = "woman_wearing_turban_tone3", + ["👳🏾‍♀️"] = "woman_wearing_turban_tone4", + ["👳🏿‍♀️"] = "woman_wearing_turban_tone5", + ["👳‍♂️"] = "man_wearing_turban", + ["👳🏻‍♂️"] = "man_wearing_turban_tone1", + ["👳🏼‍♂️"] = "man_wearing_turban_tone2", + ["👳🏽‍♂️"] = "man_wearing_turban_tone3", + ["👳🏾‍♂️"] = "man_wearing_turban_tone4", + ["👳🏿‍♂️"] = "man_wearing_turban_tone5", + ["🧕"] = "woman_with_headscarf", + ["🧕🏻"] = "woman_with_headscarf_tone1", + ["🧕🏼"] = "woman_with_headscarf_tone2", + ["🧕🏽"] = "woman_with_headscarf_tone3", + ["🧕🏾"] = "woman_with_headscarf_tone4", + ["🧕🏿"] = "woman_with_headscarf_tone5", + ["👮"] = "police_officer", + ["👮🏻"] = "police_officer_tone1", + ["👮🏼"] = "police_officer_tone2", + ["👮🏽"] = "police_officer_tone3", + ["👮🏾"] = "police_officer_tone4", + ["👮🏿"] = "police_officer_tone5", + ["👮‍♀️"] = "woman_police_officer", + ["👮🏻‍♀️"] = "woman_police_officer_tone1", + ["👮🏼‍♀️"] = "woman_police_officer_tone2", + ["👮🏽‍♀️"] = "woman_police_officer_tone3", + ["👮🏾‍♀️"] = "woman_police_officer_tone4", + ["👮🏿‍♀️"] = "woman_police_officer_tone5", + ["👮‍♂️"] = "man_police_officer", + ["👮🏻‍♂️"] = "man_police_officer_tone1", + ["👮🏼‍♂️"] = "man_police_officer_tone2", + ["👮🏽‍♂️"] = "man_police_officer_tone3", + ["👮🏾‍♂️"] = "man_police_officer_tone4", + ["👮🏿‍♂️"] = "man_police_officer_tone5", + ["👷"] = "construction_worker", + ["👷🏻"] = "construction_worker_tone1", + ["👷🏼"] = "construction_worker_tone2", + ["👷🏽"] = "construction_worker_tone3", + ["👷🏾"] = "construction_worker_tone4", + ["👷🏿"] = "construction_worker_tone5", + ["👷‍♀️"] = "woman_construction_worker", + ["👷🏻‍♀️"] = "woman_construction_worker_tone1", + ["👷🏼‍♀️"] = "woman_construction_worker_tone2", + ["👷🏽‍♀️"] = "woman_construction_worker_tone3", + ["👷🏾‍♀️"] = "woman_construction_worker_tone4", + ["👷🏿‍♀️"] = "woman_construction_worker_tone5", + ["👷‍♂️"] = "man_construction_worker", + ["👷🏻‍♂️"] = "man_construction_worker_tone1", + ["👷🏼‍♂️"] = "man_construction_worker_tone2", + ["👷🏽‍♂️"] = "man_construction_worker_tone3", + ["👷🏾‍♂️"] = "man_construction_worker_tone4", + ["👷🏿‍♂️"] = "man_construction_worker_tone5", + ["💂"] = "guard", + ["💂🏻"] = "guard_tone1", + ["💂🏼"] = "guard_tone2", + ["💂🏽"] = "guard_tone3", + ["💂🏾"] = "guard_tone4", + ["💂🏿"] = "guard_tone5", + ["💂‍♀️"] = "woman_guard", + ["💂🏻‍♀️"] = "woman_guard_tone1", + ["💂🏼‍♀️"] = "woman_guard_tone2", + ["💂🏽‍♀️"] = "woman_guard_tone3", + ["💂🏾‍♀️"] = "woman_guard_tone4", + ["💂🏿‍♀️"] = "woman_guard_tone5", + ["💂‍♂️"] = "man_guard", + ["💂🏻‍♂️"] = "man_guard_tone1", + ["💂🏼‍♂️"] = "man_guard_tone2", + ["💂🏽‍♂️"] = "man_guard_tone3", + ["💂🏾‍♂️"] = "man_guard_tone4", + ["💂🏿‍♂️"] = "man_guard_tone5", + ["🕵️"] = "detective", + ["🕵🏻"] = "detective_tone1", + ["🕵🏼"] = "detective_tone2", + ["🕵🏽"] = "detective_tone3", + ["🕵🏾"] = "detective_tone4", + ["🕵🏿"] = "detective_tone5", + ["🕵️‍♀️"] = "woman_detective", + ["🕵🏻‍♀️"] = "woman_detective_tone1", + ["🕵🏼‍♀️"] = "woman_detective_tone2", + ["🕵🏽‍♀️"] = "woman_detective_tone3", + ["🕵🏾‍♀️"] = "woman_detective_tone4", + ["🕵🏿‍♀️"] = "woman_detective_tone5", + ["🕵️‍♂️"] = "man_detective", + ["🕵🏻‍♂️"] = "man_detective_tone1", + ["🕵🏼‍♂️"] = "man_detective_tone2", + ["🕵🏽‍♂️"] = "man_detective_tone3", + ["🕵🏾‍♂️"] = "man_detective_tone4", + ["🕵🏿‍♂️"] = "man_detective_tone5", + ["🧑‍⚕️"] = "health_worker", + ["🧑🏻‍⚕️"] = "health_worker_tone1", + ["🧑🏼‍⚕️"] = "health_worker_tone2", + ["🧑🏽‍⚕️"] = "health_worker_tone3", + ["🧑🏾‍⚕️"] = "health_worker_tone4", + ["🧑🏿‍⚕️"] = "health_worker_tone5", + ["👩‍⚕️"] = "woman_health_worker", + ["👩🏻‍⚕️"] = "woman_health_worker_tone1", + ["👩🏼‍⚕️"] = "woman_health_worker_tone2", + ["👩🏽‍⚕️"] = "woman_health_worker_tone3", + ["👩🏾‍⚕️"] = "woman_health_worker_tone4", + ["👩🏿‍⚕️"] = "woman_health_worker_tone5", + ["👨‍⚕️"] = "man_health_worker", + ["👨🏻‍⚕️"] = "man_health_worker_tone1", + ["👨🏼‍⚕️"] = "man_health_worker_tone2", + ["👨🏽‍⚕️"] = "man_health_worker_tone3", + ["👨🏾‍⚕️"] = "man_health_worker_tone4", + ["👨🏿‍⚕️"] = "man_health_worker_tone5", + ["🧑‍🌾"] = "farmer", + ["🧑🏻‍🌾"] = "farmer_tone1", + ["🧑🏼‍🌾"] = "farmer_tone2", + ["🧑🏽‍🌾"] = "farmer_tone3", + ["🧑🏾‍🌾"] = "farmer_tone4", + ["🧑🏿‍🌾"] = "farmer_tone5", + ["👩‍🌾"] = "woman_farmer", + ["👩🏻‍🌾"] = "woman_farmer_tone1", + ["👩🏼‍🌾"] = "woman_farmer_tone2", + ["👩🏽‍🌾"] = "woman_farmer_tone3", + ["👩🏾‍🌾"] = "woman_farmer_tone4", + ["👩🏿‍🌾"] = "woman_farmer_tone5", + ["👨‍🌾"] = "man_farmer", + ["👨🏻‍🌾"] = "man_farmer_tone1", + ["👨🏼‍🌾"] = "man_farmer_tone2", + ["👨🏽‍🌾"] = "man_farmer_tone3", + ["👨🏾‍🌾"] = "man_farmer_tone4", + ["👨🏿‍🌾"] = "man_farmer_tone5", + ["🧑‍🍳"] = "cook", + ["🧑🏻‍🍳"] = "cook_tone1", + ["🧑🏼‍🍳"] = "cook_tone2", + ["🧑🏽‍🍳"] = "cook_tone3", + ["🧑🏾‍🍳"] = "cook_tone4", + ["🧑🏿‍🍳"] = "cook_tone5", + ["👩‍🍳"] = "woman_cook", + ["👩🏻‍🍳"] = "woman_cook_tone1", + ["👩🏼‍🍳"] = "woman_cook_tone2", + ["👩🏽‍🍳"] = "woman_cook_tone3", + ["👩🏾‍🍳"] = "woman_cook_tone4", + ["👩🏿‍🍳"] = "woman_cook_tone5", + ["👨‍🍳"] = "man_cook", + ["👨🏻‍🍳"] = "man_cook_tone1", + ["👨🏼‍🍳"] = "man_cook_tone2", + ["👨🏽‍🍳"] = "man_cook_tone3", + ["👨🏾‍🍳"] = "man_cook_tone4", + ["👨🏿‍🍳"] = "man_cook_tone5", + ["🧑‍🎓"] = "student", + ["🧑🏻‍🎓"] = "student_tone1", + ["🧑🏼‍🎓"] = "student_tone2", + ["🧑🏽‍🎓"] = "student_tone3", + ["🧑🏾‍🎓"] = "student_tone4", + ["🧑🏿‍🎓"] = "student_tone5", + ["👩‍🎓"] = "woman_student", + ["👩🏻‍🎓"] = "woman_student_tone1", + ["👩🏼‍🎓"] = "woman_student_tone2", + ["👩🏽‍🎓"] = "woman_student_tone3", + ["👩🏾‍🎓"] = "woman_student_tone4", + ["👩🏿‍🎓"] = "woman_student_tone5", + ["👨‍🎓"] = "man_student", + ["👨🏻‍🎓"] = "man_student_tone1", + ["👨🏼‍🎓"] = "man_student_tone2", + ["👨🏽‍🎓"] = "man_student_tone3", + ["👨🏾‍🎓"] = "man_student_tone4", + ["👨🏿‍🎓"] = "man_student_tone5", + ["🧑‍🎤"] = "singer", + ["🧑🏻‍🎤"] = "singer_tone1", + ["🧑🏼‍🎤"] = "singer_tone2", + ["🧑🏽‍🎤"] = "singer_tone3", + ["🧑🏾‍🎤"] = "singer_tone4", + ["🧑🏿‍🎤"] = "singer_tone5", + ["👩‍🎤"] = "woman_singer", + ["👩🏻‍🎤"] = "woman_singer_tone1", + ["👩🏼‍🎤"] = "woman_singer_tone2", + ["👩🏽‍🎤"] = "woman_singer_tone3", + ["👩🏾‍🎤"] = "woman_singer_tone4", + ["👩🏿‍🎤"] = "woman_singer_tone5", + ["👨‍🎤"] = "man_singer", + ["👨🏻‍🎤"] = "man_singer_tone1", + ["👨🏼‍🎤"] = "man_singer_tone2", + ["👨🏽‍🎤"] = "man_singer_tone3", + ["👨🏾‍🎤"] = "man_singer_tone4", + ["👨🏿‍🎤"] = "man_singer_tone5", + ["🧑‍🏫"] = "teacher", + ["🧑🏻‍🏫"] = "teacher_tone1", + ["🧑🏼‍🏫"] = "teacher_tone2", + ["🧑🏽‍🏫"] = "teacher_tone3", + ["🧑🏾‍🏫"] = "teacher_tone4", + ["🧑🏿‍🏫"] = "teacher_tone5", + ["👩‍🏫"] = "woman_teacher", + ["👩🏻‍🏫"] = "woman_teacher_tone1", + ["👩🏼‍🏫"] = "woman_teacher_tone2", + ["👩🏽‍🏫"] = "woman_teacher_tone3", + ["👩🏾‍🏫"] = "woman_teacher_tone4", + ["👩🏿‍🏫"] = "woman_teacher_tone5", + ["👨‍🏫"] = "man_teacher", + ["👨🏻‍🏫"] = "man_teacher_tone1", + ["👨🏼‍🏫"] = "man_teacher_tone2", + ["👨🏽‍🏫"] = "man_teacher_tone3", + ["👨🏾‍🏫"] = "man_teacher_tone4", + ["👨🏿‍🏫"] = "man_teacher_tone5", + ["🧑‍🏭"] = "factory_worker", + ["🧑🏻‍🏭"] = "factory_worker_tone1", + ["🧑🏼‍🏭"] = "factory_worker_tone2", + ["🧑🏽‍🏭"] = "factory_worker_tone3", + ["🧑🏾‍🏭"] = "factory_worker_tone4", + ["🧑🏿‍🏭"] = "factory_worker_tone5", + ["👩‍🏭"] = "woman_factory_worker", + ["👩🏻‍🏭"] = "woman_factory_worker_tone1", + ["👩🏼‍🏭"] = "woman_factory_worker_tone2", + ["👩🏽‍🏭"] = "woman_factory_worker_tone3", + ["👩🏾‍🏭"] = "woman_factory_worker_tone4", + ["👩🏿‍🏭"] = "woman_factory_worker_tone5", + ["👨‍🏭"] = "man_factory_worker", + ["👨🏻‍🏭"] = "man_factory_worker_tone1", + ["👨🏼‍🏭"] = "man_factory_worker_tone2", + ["👨🏽‍🏭"] = "man_factory_worker_tone3", + ["👨🏾‍🏭"] = "man_factory_worker_tone4", + ["👨🏿‍🏭"] = "man_factory_worker_tone5", + ["🧑‍💻"] = "technologist", + ["🧑🏻‍💻"] = "technologist_tone1", + ["🧑🏼‍💻"] = "technologist_tone2", + ["🧑🏽‍💻"] = "technologist_tone3", + ["🧑🏾‍💻"] = "technologist_tone4", + ["🧑🏿‍💻"] = "technologist_tone5", + ["👩‍💻"] = "woman_technologist", + ["👩🏻‍💻"] = "woman_technologist_tone1", + ["👩🏼‍💻"] = "woman_technologist_tone2", + ["👩🏽‍💻"] = "woman_technologist_tone3", + ["👩🏾‍💻"] = "woman_technologist_tone4", + ["👩🏿‍💻"] = "woman_technologist_tone5", + ["👨‍💻"] = "man_technologist", + ["👨🏻‍💻"] = "man_technologist_tone1", + ["👨🏼‍💻"] = "man_technologist_tone2", + ["👨🏽‍💻"] = "man_technologist_tone3", + ["👨🏾‍💻"] = "man_technologist_tone4", + ["👨🏿‍💻"] = "man_technologist_tone5", + ["🧑‍💼"] = "office_worker", + ["🧑🏻‍💼"] = "office_worker_tone1", + ["🧑🏼‍💼"] = "office_worker_tone2", + ["🧑🏽‍💼"] = "office_worker_tone3", + ["🧑🏾‍💼"] = "office_worker_tone4", + ["🧑🏿‍💼"] = "office_worker_tone5", + ["👩‍💼"] = "woman_office_worker", + ["👩🏻‍💼"] = "woman_office_worker_tone1", + ["👩🏼‍💼"] = "woman_office_worker_tone2", + ["👩🏽‍💼"] = "woman_office_worker_tone3", + ["👩🏾‍💼"] = "woman_office_worker_tone4", + ["👩🏿‍💼"] = "woman_office_worker_tone5", + ["👨‍💼"] = "man_office_worker", + ["👨🏻‍💼"] = "man_office_worker_tone1", + ["👨🏼‍💼"] = "man_office_worker_tone2", + ["👨🏽‍💼"] = "man_office_worker_tone3", + ["👨🏾‍💼"] = "man_office_worker_tone4", + ["👨🏿‍💼"] = "man_office_worker_tone5", + ["🧑‍🔧"] = "mechanic", + ["🧑🏻‍🔧"] = "mechanic_tone1", + ["🧑🏼‍🔧"] = "mechanic_tone2", + ["🧑🏽‍🔧"] = "mechanic_tone3", + ["🧑🏾‍🔧"] = "mechanic_tone4", + ["🧑🏿‍🔧"] = "mechanic_tone5", + ["👩‍🔧"] = "woman_mechanic", + ["👩🏻‍🔧"] = "woman_mechanic_tone1", + ["👩🏼‍🔧"] = "woman_mechanic_tone2", + ["👩🏽‍🔧"] = "woman_mechanic_tone3", + ["👩🏾‍🔧"] = "woman_mechanic_tone4", + ["👩🏿‍🔧"] = "woman_mechanic_tone5", + ["👨‍🔧"] = "man_mechanic", + ["👨🏻‍🔧"] = "man_mechanic_tone1", + ["👨🏼‍🔧"] = "man_mechanic_tone2", + ["👨🏽‍🔧"] = "man_mechanic_tone3", + ["👨🏾‍🔧"] = "man_mechanic_tone4", + ["👨🏿‍🔧"] = "man_mechanic_tone5", + ["🧑‍🔬"] = "scientist", + ["🧑🏻‍🔬"] = "scientist_tone1", + ["🧑🏼‍🔬"] = "scientist_tone2", + ["🧑🏽‍🔬"] = "scientist_tone3", + ["🧑🏾‍🔬"] = "scientist_tone4", + ["🧑🏿‍🔬"] = "scientist_tone5", + ["👩‍🔬"] = "woman_scientist", + ["👩🏻‍🔬"] = "woman_scientist_tone1", + ["👩🏼‍🔬"] = "woman_scientist_tone2", + ["👩🏽‍🔬"] = "woman_scientist_tone3", + ["👩🏾‍🔬"] = "woman_scientist_tone4", + ["👩🏿‍🔬"] = "woman_scientist_tone5", + ["👨‍🔬"] = "man_scientist", + ["👨🏻‍🔬"] = "man_scientist_tone1", + ["👨🏼‍🔬"] = "man_scientist_tone2", + ["👨🏽‍🔬"] = "man_scientist_tone3", + ["👨🏾‍🔬"] = "man_scientist_tone4", + ["👨🏿‍🔬"] = "man_scientist_tone5", + ["🧑‍🎨"] = "artist", + ["🧑🏻‍🎨"] = "artist_tone1", + ["🧑🏼‍🎨"] = "artist_tone2", + ["🧑🏽‍🎨"] = "artist_tone3", + ["🧑🏾‍🎨"] = "artist_tone4", + ["🧑🏿‍🎨"] = "artist_tone5", + ["👩‍🎨"] = "woman_artist", + ["👩🏻‍🎨"] = "woman_artist_tone1", + ["👩🏼‍🎨"] = "woman_artist_tone2", + ["👩🏽‍🎨"] = "woman_artist_tone3", + ["👩🏾‍🎨"] = "woman_artist_tone4", + ["👩🏿‍🎨"] = "woman_artist_tone5", + ["👨‍🎨"] = "man_artist", + ["👨🏻‍🎨"] = "man_artist_tone1", + ["👨🏼‍🎨"] = "man_artist_tone2", + ["👨🏽‍🎨"] = "man_artist_tone3", + ["👨🏾‍🎨"] = "man_artist_tone4", + ["👨🏿‍🎨"] = "man_artist_tone5", + ["🧑‍🚒"] = "firefighter", + ["🧑🏻‍🚒"] = "firefighter_tone1", + ["🧑🏼‍🚒"] = "firefighter_tone2", + ["🧑🏽‍🚒"] = "firefighter_tone3", + ["🧑🏾‍🚒"] = "firefighter_tone4", + ["🧑🏿‍🚒"] = "firefighter_tone5", + ["👩‍🚒"] = "woman_firefighter", + ["👩🏻‍🚒"] = "woman_firefighter_tone1", + ["👩🏼‍🚒"] = "woman_firefighter_tone2", + ["👩🏽‍🚒"] = "woman_firefighter_tone3", + ["👩🏾‍🚒"] = "woman_firefighter_tone4", + ["👩🏿‍🚒"] = "woman_firefighter_tone5", + ["👨‍🚒"] = "man_firefighter", + ["👨🏻‍🚒"] = "man_firefighter_tone1", + ["👨🏼‍🚒"] = "man_firefighter_tone2", + ["👨🏽‍🚒"] = "man_firefighter_tone3", + ["👨🏾‍🚒"] = "man_firefighter_tone4", + ["👨🏿‍🚒"] = "man_firefighter_tone5", + ["🧑‍✈️"] = "pilot", + ["🧑🏻‍✈️"] = "pilot_tone1", + ["🧑🏼‍✈️"] = "pilot_tone2", + ["🧑🏽‍✈️"] = "pilot_tone3", + ["🧑🏾‍✈️"] = "pilot_tone4", + ["🧑🏿‍✈️"] = "pilot_tone5", + ["👩‍✈️"] = "woman_pilot", + ["👩🏻‍✈️"] = "woman_pilot_tone1", + ["👩🏼‍✈️"] = "woman_pilot_tone2", + ["👩🏽‍✈️"] = "woman_pilot_tone3", + ["👩🏾‍✈️"] = "woman_pilot_tone4", + ["👩🏿‍✈️"] = "woman_pilot_tone5", + ["👨‍✈️"] = "man_pilot", + ["👨🏻‍✈️"] = "man_pilot_tone1", + ["👨🏼‍✈️"] = "man_pilot_tone2", + ["👨🏽‍✈️"] = "man_pilot_tone3", + ["👨🏾‍✈️"] = "man_pilot_tone4", + ["👨🏿‍✈️"] = "man_pilot_tone5", + ["🧑‍🚀"] = "astronaut", + ["🧑🏻‍🚀"] = "astronaut_tone1", + ["🧑🏼‍🚀"] = "astronaut_tone2", + ["🧑🏽‍🚀"] = "astronaut_tone3", + ["🧑🏾‍🚀"] = "astronaut_tone4", + ["🧑🏿‍🚀"] = "astronaut_tone5", + ["👩‍🚀"] = "woman_astronaut", + ["👩🏻‍🚀"] = "woman_astronaut_tone1", + ["👩🏼‍🚀"] = "woman_astronaut_tone2", + ["👩🏽‍🚀"] = "woman_astronaut_tone3", + ["👩🏾‍🚀"] = "woman_astronaut_tone4", + ["👩🏿‍🚀"] = "woman_astronaut_tone5", + ["👨‍🚀"] = "man_astronaut", + ["👨🏻‍🚀"] = "man_astronaut_tone1", + ["👨🏼‍🚀"] = "man_astronaut_tone2", + ["👨🏽‍🚀"] = "man_astronaut_tone3", + ["👨🏾‍🚀"] = "man_astronaut_tone4", + ["👨🏿‍🚀"] = "man_astronaut_tone5", + ["🧑‍⚖️"] = "judge", + ["🧑🏻‍⚖️"] = "judge_tone1", + ["🧑🏼‍⚖️"] = "judge_tone2", + ["🧑🏽‍⚖️"] = "judge_tone3", + ["🧑🏾‍⚖️"] = "judge_tone4", + ["🧑🏿‍⚖️"] = "judge_tone5", + ["👩‍⚖️"] = "woman_judge", + ["👩🏻‍⚖️"] = "woman_judge_tone1", + ["👩🏼‍⚖️"] = "woman_judge_tone2", + ["👩🏽‍⚖️"] = "woman_judge_tone3", + ["👩🏾‍⚖️"] = "woman_judge_tone4", + ["👩🏿‍⚖️"] = "woman_judge_tone5", + ["👨‍⚖️"] = "man_judge", + ["👨🏻‍⚖️"] = "man_judge_tone1", + ["👨🏼‍⚖️"] = "man_judge_tone2", + ["👨🏽‍⚖️"] = "man_judge_tone3", + ["👨🏾‍⚖️"] = "man_judge_tone4", + ["👨🏿‍⚖️"] = "man_judge_tone5", + ["👰"] = "person_with_veil", + ["👰🏻"] = "person_with_veil_tone1", + ["👰🏼"] = "person_with_veil_tone2", + ["👰🏽"] = "person_with_veil_tone3", + ["👰🏾"] = "person_with_veil_tone4", + ["👰🏿"] = "person_with_veil_tone5", + ["👰‍♀️"] = "woman_with_veil", + ["👰🏻‍♀️"] = "woman_with_veil_tone1", + ["👰🏼‍♀️"] = "woman_with_veil_tone2", + ["👰🏽‍♀️"] = "woman_with_veil_tone3", + ["👰🏾‍♀️"] = "woman_with_veil_tone4", + ["👰🏿‍♀️"] = "woman_with_veil_tone5", + ["👰‍♂️"] = "man_with_veil", + ["👰🏻‍♂️"] = "man_with_veil_tone1", + ["👰🏼‍♂️"] = "man_with_veil_tone2", + ["👰🏽‍♂️"] = "man_with_veil_tone3", + ["👰🏾‍♂️"] = "man_with_veil_tone4", + ["👰🏿‍♂️"] = "man_with_veil_tone5", + ["🤵"] = "person_in_tuxedo", + ["🤵🏻"] = "person_in_tuxedo_tone1", + ["🤵🏼"] = "person_in_tuxedo_tone2", + ["🤵🏽"] = "person_in_tuxedo_tone3", + ["🤵🏾"] = "person_in_tuxedo_tone4", + ["🤵🏿"] = "person_in_tuxedo_tone5", + ["🤵‍♀️"] = "woman_in_tuxedo", + ["🤵🏻‍♀️"] = "woman_in_tuxedo_tone1", + ["🤵🏼‍♀️"] = "woman_in_tuxedo_tone2", + ["🤵🏽‍♀️"] = "woman_in_tuxedo_tone3", + ["🤵🏾‍♀️"] = "woman_in_tuxedo_tone4", + ["🤵🏿‍♀️"] = "woman_in_tuxedo_tone5", + ["🤵‍♂️"] = "man_in_tuxedo", + ["🤵🏻‍♂️"] = "man_in_tuxedo_tone1", + ["🤵🏼‍♂️"] = "man_in_tuxedo_tone2", + ["🤵🏽‍♂️"] = "man_in_tuxedo_tone3", + ["🤵🏾‍♂️"] = "man_in_tuxedo_tone4", + ["🤵🏿‍♂️"] = "man_in_tuxedo_tone5", + ["👸"] = "princess", + ["👸🏻"] = "princess_tone1", + ["👸🏼"] = "princess_tone2", + ["👸🏽"] = "princess_tone3", + ["👸🏾"] = "princess_tone4", + ["👸🏿"] = "princess_tone5", + ["🤴"] = "prince", + ["🤴🏻"] = "prince_tone1", + ["🤴🏼"] = "prince_tone2", + ["🤴🏽"] = "prince_tone3", + ["🤴🏾"] = "prince_tone4", + ["🤴🏿"] = "prince_tone5", + ["🦸"] = "superhero", + ["🦸🏻"] = "superhero_tone1", + ["🦸🏼"] = "superhero_tone2", + ["🦸🏽"] = "superhero_tone3", + ["🦸🏾"] = "superhero_tone4", + ["🦸🏿"] = "superhero_tone5", + ["🦸‍♀️"] = "woman_superhero", + ["🦸🏻‍♀️"] = "woman_superhero_tone1", + ["🦸🏼‍♀️"] = "woman_superhero_tone2", + ["🦸🏽‍♀️"] = "woman_superhero_tone3", + ["🦸🏾‍♀️"] = "woman_superhero_tone4", + ["🦸🏿‍♀️"] = "woman_superhero_tone5", + ["🦸‍♂️"] = "man_superhero", + ["🦸🏻‍♂️"] = "man_superhero_tone1", + ["🦸🏼‍♂️"] = "man_superhero_tone2", + ["🦸🏽‍♂️"] = "man_superhero_tone3", + ["🦸🏾‍♂️"] = "man_superhero_tone4", + ["🦸🏿‍♂️"] = "man_superhero_tone5", + ["🦹"] = "supervillain", + ["🦹🏻"] = "supervillain_tone1", + ["🦹🏼"] = "supervillain_tone2", + ["🦹🏽"] = "supervillain_tone3", + ["🦹🏾"] = "supervillain_tone4", + ["🦹🏿"] = "supervillain_tone5", + ["🦹‍♀️"] = "woman_supervillain", + ["🦹🏻‍♀️"] = "woman_supervillain_tone1", + ["🦹🏼‍♀️"] = "woman_supervillain_tone2", + ["🦹🏽‍♀️"] = "woman_supervillain_tone3", + ["🦹🏾‍♀️"] = "woman_supervillain_tone4", + ["🦹🏿‍♀️"] = "woman_supervillain_tone5", + ["🦹‍♂️"] = "man_supervillain", + ["🦹🏻‍♂️"] = "man_supervillain_tone1", + ["🦹🏼‍♂️"] = "man_supervillain_tone2", + ["🦹🏽‍♂️"] = "man_supervillain_tone3", + ["🦹🏾‍♂️"] = "man_supervillain_tone4", + ["🦹🏿‍♂️"] = "man_supervillain_tone5", + ["🥷"] = "ninja", + ["🥷🏻"] = "ninja_tone1", + ["🥷🏼"] = "ninja_tone2", + ["🥷🏽"] = "ninja_tone3", + ["🥷🏾"] = "ninja_tone4", + ["🥷🏿"] = "ninja_tone5", + ["🧑‍🎄"] = "mx_claus", + ["🧑🏻‍🎄"] = "mx_claus_tone1", + ["🧑🏼‍🎄"] = "mx_claus_tone2", + ["🧑🏽‍🎄"] = "mx_claus_tone3", + ["🧑🏾‍🎄"] = "mx_claus_tone4", + ["🧑🏿‍🎄"] = "mx_claus_tone5", + ["🤶"] = "mrs_claus", + ["🤶🏻"] = "mrs_claus_tone1", + ["🤶🏼"] = "mrs_claus_tone2", + ["🤶🏽"] = "mrs_claus_tone3", + ["🤶🏾"] = "mrs_claus_tone4", + ["🤶🏿"] = "mrs_claus_tone5", + ["🎅"] = "santa", + ["🎅🏻"] = "santa_tone1", + ["🎅🏼"] = "santa_tone2", + ["🎅🏽"] = "santa_tone3", + ["🎅🏾"] = "santa_tone4", + ["🎅🏿"] = "santa_tone5", + ["🧙"] = "mage", + ["🧙🏻"] = "mage_tone1", + ["🧙🏼"] = "mage_tone2", + ["🧙🏽"] = "mage_tone3", + ["🧙🏾"] = "mage_tone4", + ["🧙🏿"] = "mage_tone5", + ["🧙‍♀️"] = "woman_mage", + ["🧙🏻‍♀️"] = "woman_mage_tone1", + ["🧙🏼‍♀️"] = "woman_mage_tone2", + ["🧙🏽‍♀️"] = "woman_mage_tone3", + ["🧙🏾‍♀️"] = "woman_mage_tone4", + ["🧙🏿‍♀️"] = "woman_mage_tone5", + ["🧙‍♂️"] = "man_mage", + ["🧙🏻‍♂️"] = "man_mage_tone1", + ["🧙🏼‍♂️"] = "man_mage_tone2", + ["🧙🏽‍♂️"] = "man_mage_tone3", + ["🧙🏾‍♂️"] = "man_mage_tone4", + ["🧙🏿‍♂️"] = "man_mage_tone5", + ["🧝"] = "elf", + ["🧝🏻"] = "elf_tone1", + ["🧝🏼"] = "elf_tone2", + ["🧝🏽"] = "elf_tone3", + ["🧝🏾"] = "elf_tone4", + ["🧝🏿"] = "elf_tone5", + ["🧝‍♀️"] = "woman_elf", + ["🧝🏻‍♀️"] = "woman_elf_tone1", + ["🧝🏼‍♀️"] = "woman_elf_tone2", + ["🧝🏽‍♀️"] = "woman_elf_tone3", + ["🧝🏾‍♀️"] = "woman_elf_tone4", + ["🧝🏿‍♀️"] = "woman_elf_tone5", + ["🧝‍♂️"] = "man_elf", + ["🧝🏻‍♂️"] = "man_elf_tone1", + ["🧝🏼‍♂️"] = "man_elf_tone2", + ["🧝🏽‍♂️"] = "man_elf_tone3", + ["🧝🏾‍♂️"] = "man_elf_tone4", + ["🧝🏿‍♂️"] = "man_elf_tone5", + ["🧛"] = "vampire", + ["🧛🏻"] = "vampire_tone1", + ["🧛🏼"] = "vampire_tone2", + ["🧛🏽"] = "vampire_tone3", + ["🧛🏾"] = "vampire_tone4", + ["🧛🏿"] = "vampire_tone5", + ["🧛‍♀️"] = "woman_vampire", + ["🧛🏻‍♀️"] = "woman_vampire_tone1", + ["🧛🏼‍♀️"] = "woman_vampire_tone2", + ["🧛🏽‍♀️"] = "woman_vampire_tone3", + ["🧛🏾‍♀️"] = "woman_vampire_tone4", + ["🧛🏿‍♀️"] = "woman_vampire_tone5", + ["🧛‍♂️"] = "man_vampire", + ["🧛🏻‍♂️"] = "man_vampire_tone1", + ["🧛🏼‍♂️"] = "man_vampire_tone2", + ["🧛🏽‍♂️"] = "man_vampire_tone3", + ["🧛🏾‍♂️"] = "man_vampire_tone4", + ["🧛🏿‍♂️"] = "man_vampire_tone5", + ["🧟"] = "zombie", + ["🧟‍♀️"] = "woman_zombie", + ["🧟‍♂️"] = "man_zombie", + ["🧞"] = "genie", + ["🧞‍♀️"] = "woman_genie", + ["🧞‍♂️"] = "man_genie", + ["🧜"] = "merperson", + ["🧜🏻"] = "merperson_tone1", + ["🧜🏼"] = "merperson_tone2", + ["🧜🏽"] = "merperson_tone3", + ["🧜🏾"] = "merperson_tone4", + ["🧜🏿"] = "merperson_tone5", + ["🧜‍♀️"] = "mermaid", + ["🧜🏻‍♀️"] = "mermaid_tone1", + ["🧜🏼‍♀️"] = "mermaid_tone2", + ["🧜🏽‍♀️"] = "mermaid_tone3", + ["🧜🏾‍♀️"] = "mermaid_tone4", + ["🧜🏿‍♀️"] = "mermaid_tone5", + ["🧜‍♂️"] = "merman", + ["🧜🏻‍♂️"] = "merman_tone1", + ["🧜🏼‍♂️"] = "merman_tone2", + ["🧜🏽‍♂️"] = "merman_tone3", + ["🧜🏾‍♂️"] = "merman_tone4", + ["🧜🏿‍♂️"] = "merman_tone5", + ["🧚"] = "fairy", + ["🧚🏻"] = "fairy_tone1", + ["🧚🏼"] = "fairy_tone2", + ["🧚🏽"] = "fairy_tone3", + ["🧚🏾"] = "fairy_tone4", + ["🧚🏿"] = "fairy_tone5", + ["🧚‍♀️"] = "woman_fairy", + ["🧚🏻‍♀️"] = "woman_fairy_tone1", + ["🧚🏼‍♀️"] = "woman_fairy_tone2", + ["🧚🏽‍♀️"] = "woman_fairy_tone3", + ["🧚🏾‍♀️"] = "woman_fairy_tone4", + ["🧚🏿‍♀️"] = "woman_fairy_tone5", + ["🧚‍♂️"] = "man_fairy", + ["🧚🏻‍♂️"] = "man_fairy_tone1", + ["🧚🏼‍♂️"] = "man_fairy_tone2", + ["🧚🏽‍♂️"] = "man_fairy_tone3", + ["🧚🏾‍♂️"] = "man_fairy_tone4", + ["🧚🏿‍♂️"] = "man_fairy_tone5", + ["👼"] = "angel", + ["👼🏻"] = "angel_tone1", + ["👼🏼"] = "angel_tone2", + ["👼🏽"] = "angel_tone3", + ["👼🏾"] = "angel_tone4", + ["👼🏿"] = "angel_tone5", + ["🤰"] = "pregnant_woman", + ["🤰🏻"] = "pregnant_woman_tone1", + ["🤰🏼"] = "pregnant_woman_tone2", + ["🤰🏽"] = "pregnant_woman_tone3", + ["🤰🏾"] = "pregnant_woman_tone4", + ["🤰🏿"] = "pregnant_woman_tone5", + ["🤱"] = "breast_feeding", + ["🤱🏻"] = "breast_feeding_tone1", + ["🤱🏼"] = "breast_feeding_tone2", + ["🤱🏽"] = "breast_feeding_tone3", + ["🤱🏾"] = "breast_feeding_tone4", + ["🤱🏿"] = "breast_feeding_tone5", + ["🧑‍🍼"] = "person_feeding_baby", + ["🧑🏻‍🍼"] = "person_feeding_baby_tone1", + ["🧑🏼‍🍼"] = "person_feeding_baby_tone2", + ["🧑🏽‍🍼"] = "person_feeding_baby_tone3", + ["🧑🏾‍🍼"] = "person_feeding_baby_tone4", + ["🧑🏿‍🍼"] = "person_feeding_baby_tone5", + ["👩‍🍼"] = "woman_feeding_baby", + ["👩🏻‍🍼"] = "woman_feeding_baby_tone1", + ["👩🏼‍🍼"] = "woman_feeding_baby_tone2", + ["👩🏽‍🍼"] = "woman_feeding_baby_tone3", + ["👩🏾‍🍼"] = "woman_feeding_baby_tone4", + ["👩🏿‍🍼"] = "woman_feeding_baby_tone5", + ["👨‍🍼"] = "man_feeding_baby", + ["👨🏻‍🍼"] = "man_feeding_baby_tone1", + ["👨🏼‍🍼"] = "man_feeding_baby_tone2", + ["👨🏽‍🍼"] = "man_feeding_baby_tone3", + ["👨🏾‍🍼"] = "man_feeding_baby_tone4", + ["👨🏿‍🍼"] = "man_feeding_baby_tone5", + ["🙇"] = "person_bowing", + ["🙇🏻"] = "person_bowing_tone1", + ["🙇🏼"] = "person_bowing_tone2", + ["🙇🏽"] = "person_bowing_tone3", + ["🙇🏾"] = "person_bowing_tone4", + ["🙇🏿"] = "person_bowing_tone5", + ["🙇‍♀️"] = "woman_bowing", + ["🙇🏻‍♀️"] = "woman_bowing_tone1", + ["🙇🏼‍♀️"] = "woman_bowing_tone2", + ["🙇🏽‍♀️"] = "woman_bowing_tone3", + ["🙇🏾‍♀️"] = "woman_bowing_tone4", + ["🙇🏿‍♀️"] = "woman_bowing_tone5", + ["🙇‍♂️"] = "man_bowing", + ["🙇🏻‍♂️"] = "man_bowing_tone1", + ["🙇🏼‍♂️"] = "man_bowing_tone2", + ["🙇🏽‍♂️"] = "man_bowing_tone3", + ["🙇🏾‍♂️"] = "man_bowing_tone4", + ["🙇🏿‍♂️"] = "man_bowing_tone5", + ["💁"] = "person_tipping_hand", + ["💁🏻"] = "person_tipping_hand_tone1", + ["💁🏼"] = "person_tipping_hand_tone2", + ["💁🏽"] = "person_tipping_hand_tone3", + ["💁🏾"] = "person_tipping_hand_tone4", + ["💁🏿"] = "person_tipping_hand_tone5", + ["💁‍♀️"] = "woman_tipping_hand", + ["💁🏻‍♀️"] = "woman_tipping_hand_tone1", + ["💁🏼‍♀️"] = "woman_tipping_hand_tone2", + ["💁🏽‍♀️"] = "woman_tipping_hand_tone3", + ["💁🏾‍♀️"] = "woman_tipping_hand_tone4", + ["💁🏿‍♀️"] = "woman_tipping_hand_tone5", + ["💁‍♂️"] = "man_tipping_hand", + ["💁🏻‍♂️"] = "man_tipping_hand_tone1", + ["💁🏼‍♂️"] = "man_tipping_hand_tone2", + ["💁🏽‍♂️"] = "man_tipping_hand_tone3", + ["💁🏾‍♂️"] = "man_tipping_hand_tone4", + ["💁🏿‍♂️"] = "man_tipping_hand_tone5", + ["🙅"] = "person_gesturing_no", + ["🙅🏻"] = "person_gesturing_no_tone1", + ["🙅🏼"] = "person_gesturing_no_tone2", + ["🙅🏽"] = "person_gesturing_no_tone3", + ["🙅🏾"] = "person_gesturing_no_tone4", + ["🙅🏿"] = "person_gesturing_no_tone5", + ["🙅‍♀️"] = "woman_gesturing_no", + ["🙅🏻‍♀️"] = "woman_gesturing_no_tone1", + ["🙅🏼‍♀️"] = "woman_gesturing_no_tone2", + ["🙅🏽‍♀️"] = "woman_gesturing_no_tone3", + ["🙅🏾‍♀️"] = "woman_gesturing_no_tone4", + ["🙅🏿‍♀️"] = "woman_gesturing_no_tone5", + ["🙅‍♂️"] = "man_gesturing_no", + ["🙅🏻‍♂️"] = "man_gesturing_no_tone1", + ["🙅🏼‍♂️"] = "man_gesturing_no_tone2", + ["🙅🏽‍♂️"] = "man_gesturing_no_tone3", + ["🙅🏾‍♂️"] = "man_gesturing_no_tone4", + ["🙅🏿‍♂️"] = "man_gesturing_no_tone5", + ["🙆"] = "person_gesturing_ok", + ["🙆🏻"] = "person_gesturing_ok_tone1", + ["🙆🏼"] = "person_gesturing_ok_tone2", + ["🙆🏽"] = "person_gesturing_ok_tone3", + ["🙆🏾"] = "person_gesturing_ok_tone4", + ["🙆🏿"] = "person_gesturing_ok_tone5", + ["🙆‍♀️"] = "woman_gesturing_ok", + ["🙆🏻‍♀️"] = "woman_gesturing_ok_tone1", + ["🙆🏼‍♀️"] = "woman_gesturing_ok_tone2", + ["🙆🏽‍♀️"] = "woman_gesturing_ok_tone3", + ["🙆🏾‍♀️"] = "woman_gesturing_ok_tone4", + ["🙆🏿‍♀️"] = "woman_gesturing_ok_tone5", + ["🙆‍♂️"] = "man_gesturing_ok", + ["🙆🏻‍♂️"] = "man_gesturing_ok_tone1", + ["🙆🏼‍♂️"] = "man_gesturing_ok_tone2", + ["🙆🏽‍♂️"] = "man_gesturing_ok_tone3", + ["🙆🏾‍♂️"] = "man_gesturing_ok_tone4", + ["🙆🏿‍♂️"] = "man_gesturing_ok_tone5", + ["🙋"] = "person_raising_hand", + ["🙋🏻"] = "person_raising_hand_tone1", + ["🙋🏼"] = "person_raising_hand_tone2", + ["🙋🏽"] = "person_raising_hand_tone3", + ["🙋🏾"] = "person_raising_hand_tone4", + ["🙋🏿"] = "person_raising_hand_tone5", + ["🙋‍♀️"] = "woman_raising_hand", + ["🙋🏻‍♀️"] = "woman_raising_hand_tone1", + ["🙋🏼‍♀️"] = "woman_raising_hand_tone2", + ["🙋🏽‍♀️"] = "woman_raising_hand_tone3", + ["🙋🏾‍♀️"] = "woman_raising_hand_tone4", + ["🙋🏿‍♀️"] = "woman_raising_hand_tone5", + ["🙋‍♂️"] = "man_raising_hand", + ["🙋🏻‍♂️"] = "man_raising_hand_tone1", + ["🙋🏼‍♂️"] = "man_raising_hand_tone2", + ["🙋🏽‍♂️"] = "man_raising_hand_tone3", + ["🙋🏾‍♂️"] = "man_raising_hand_tone4", + ["🙋🏿‍♂️"] = "man_raising_hand_tone5", + ["🧏"] = "deaf_person", + ["🧏🏻"] = "deaf_person_tone1", + ["🧏🏼"] = "deaf_person_tone2", + ["🧏🏽"] = "deaf_person_tone3", + ["🧏🏾"] = "deaf_person_tone4", + ["🧏🏿"] = "deaf_person_tone5", + ["🧏‍♀️"] = "deaf_woman", + ["🧏🏻‍♀️"] = "deaf_woman_tone1", + ["🧏🏼‍♀️"] = "deaf_woman_tone2", + ["🧏🏽‍♀️"] = "deaf_woman_tone3", + ["🧏🏾‍♀️"] = "deaf_woman_tone4", + ["🧏🏿‍♀️"] = "deaf_woman_tone5", + ["🧏‍♂️"] = "deaf_man", + ["🧏🏻‍♂️"] = "deaf_man_tone1", + ["🧏🏼‍♂️"] = "deaf_man_tone2", + ["🧏🏽‍♂️"] = "deaf_man_tone3", + ["🧏🏾‍♂️"] = "deaf_man_tone4", + ["🧏🏿‍♂️"] = "deaf_man_tone5", + ["🤦"] = "person_facepalming", + ["🤦🏻"] = "person_facepalming_tone1", + ["🤦🏼"] = "person_facepalming_tone2", + ["🤦🏽"] = "person_facepalming_tone3", + ["🤦🏾"] = "person_facepalming_tone4", + ["🤦🏿"] = "person_facepalming_tone5", + ["🤦‍♀️"] = "woman_facepalming", + ["🤦🏻‍♀️"] = "woman_facepalming_tone1", + ["🤦🏼‍♀️"] = "woman_facepalming_tone2", + ["🤦🏽‍♀️"] = "woman_facepalming_tone3", + ["🤦🏾‍♀️"] = "woman_facepalming_tone4", + ["🤦🏿‍♀️"] = "woman_facepalming_tone5", + ["🤦‍♂️"] = "man_facepalming", + ["🤦🏻‍♂️"] = "man_facepalming_tone1", + ["🤦🏼‍♂️"] = "man_facepalming_tone2", + ["🤦🏽‍♂️"] = "man_facepalming_tone3", + ["🤦🏾‍♂️"] = "man_facepalming_tone4", + ["🤦🏿‍♂️"] = "man_facepalming_tone5", + ["🤷"] = "person_shrugging", + ["🤷🏻"] = "person_shrugging_tone1", + ["🤷🏼"] = "person_shrugging_tone2", + ["🤷🏽"] = "person_shrugging_tone3", + ["🤷🏾"] = "person_shrugging_tone4", + ["🤷🏿"] = "person_shrugging_tone5", + ["🤷‍♀️"] = "woman_shrugging", + ["🤷🏻‍♀️"] = "woman_shrugging_tone1", + ["🤷🏼‍♀️"] = "woman_shrugging_tone2", + ["🤷🏽‍♀️"] = "woman_shrugging_tone3", + ["🤷🏾‍♀️"] = "woman_shrugging_tone4", + ["🤷🏿‍♀️"] = "woman_shrugging_tone5", + ["🤷‍♂️"] = "man_shrugging", + ["🤷🏻‍♂️"] = "man_shrugging_tone1", + ["🤷🏼‍♂️"] = "man_shrugging_tone2", + ["🤷🏽‍♂️"] = "man_shrugging_tone3", + ["🤷🏾‍♂️"] = "man_shrugging_tone4", + ["🤷🏿‍♂️"] = "man_shrugging_tone5", + ["🙎"] = "person_pouting", + ["🙎🏻"] = "person_pouting_tone1", + ["🙎🏼"] = "person_pouting_tone2", + ["🙎🏽"] = "person_pouting_tone3", + ["🙎🏾"] = "person_pouting_tone4", + ["🙎🏿"] = "person_pouting_tone5", + ["🙎‍♀️"] = "woman_pouting", + ["🙎🏻‍♀️"] = "woman_pouting_tone1", + ["🙎🏼‍♀️"] = "woman_pouting_tone2", + ["🙎🏽‍♀️"] = "woman_pouting_tone3", + ["🙎🏾‍♀️"] = "woman_pouting_tone4", + ["🙎🏿‍♀️"] = "woman_pouting_tone5", + ["🙎‍♂️"] = "man_pouting", + ["🙎🏻‍♂️"] = "man_pouting_tone1", + ["🙎🏼‍♂️"] = "man_pouting_tone2", + ["🙎🏽‍♂️"] = "man_pouting_tone3", + ["🙎🏾‍♂️"] = "man_pouting_tone4", + ["🙎🏿‍♂️"] = "man_pouting_tone5", + ["🙍"] = "person_frowning", + ["🙍🏻"] = "person_frowning_tone1", + ["🙍🏼"] = "person_frowning_tone2", + ["🙍🏽"] = "person_frowning_tone3", + ["🙍🏾"] = "person_frowning_tone4", + ["🙍🏿"] = "person_frowning_tone5", + ["🙍‍♀️"] = "woman_frowning", + ["🙍🏻‍♀️"] = "woman_frowning_tone1", + ["🙍🏼‍♀️"] = "woman_frowning_tone2", + ["🙍🏽‍♀️"] = "woman_frowning_tone3", + ["🙍🏾‍♀️"] = "woman_frowning_tone4", + ["🙍🏿‍♀️"] = "woman_frowning_tone5", + ["🙍‍♂️"] = "man_frowning", + ["🙍🏻‍♂️"] = "man_frowning_tone1", + ["🙍🏼‍♂️"] = "man_frowning_tone2", + ["🙍🏽‍♂️"] = "man_frowning_tone3", + ["🙍🏾‍♂️"] = "man_frowning_tone4", + ["🙍🏿‍♂️"] = "man_frowning_tone5", + ["💇"] = "person_getting_haircut", + ["💇🏻"] = "person_getting_haircut_tone1", + ["💇🏼"] = "person_getting_haircut_tone2", + ["💇🏽"] = "person_getting_haircut_tone3", + ["💇🏾"] = "person_getting_haircut_tone4", + ["💇🏿"] = "person_getting_haircut_tone5", + ["💇‍♀️"] = "woman_getting_haircut", + ["💇🏻‍♀️"] = "woman_getting_haircut_tone1", + ["💇🏼‍♀️"] = "woman_getting_haircut_tone2", + ["💇🏽‍♀️"] = "woman_getting_haircut_tone3", + ["💇🏾‍♀️"] = "woman_getting_haircut_tone4", + ["💇🏿‍♀️"] = "woman_getting_haircut_tone5", + ["💇‍♂️"] = "man_getting_haircut", + ["💇🏻‍♂️"] = "man_getting_haircut_tone1", + ["💇🏼‍♂️"] = "man_getting_haircut_tone2", + ["💇🏽‍♂️"] = "man_getting_haircut_tone3", + ["💇🏾‍♂️"] = "man_getting_haircut_tone4", + ["💇🏿‍♂️"] = "man_getting_haircut_tone5", + ["💆"] = "person_getting_massage", + ["💆🏻"] = "person_getting_massage_tone1", + ["💆🏼"] = "person_getting_massage_tone2", + ["💆🏽"] = "person_getting_massage_tone3", + ["💆🏾"] = "person_getting_massage_tone4", + ["💆🏿"] = "person_getting_massage_tone5", + ["💆‍♀️"] = "woman_getting_face_massage", + ["💆🏻‍♀️"] = "woman_getting_face_massage_tone1", + ["💆🏼‍♀️"] = "woman_getting_face_massage_tone2", + ["💆🏽‍♀️"] = "woman_getting_face_massage_tone3", + ["💆🏾‍♀️"] = "woman_getting_face_massage_tone4", + ["💆🏿‍♀️"] = "woman_getting_face_massage_tone5", + ["💆‍♂️"] = "man_getting_face_massage", + ["💆🏻‍♂️"] = "man_getting_face_massage_tone1", + ["💆🏼‍♂️"] = "man_getting_face_massage_tone2", + ["💆🏽‍♂️"] = "man_getting_face_massage_tone3", + ["💆🏾‍♂️"] = "man_getting_face_massage_tone4", + ["💆🏿‍♂️"] = "man_getting_face_massage_tone5", + ["🧖"] = "person_in_steamy_room", + ["🧖🏻"] = "person_in_steamy_room_tone1", + ["🧖🏼"] = "person_in_steamy_room_tone2", + ["🧖🏽"] = "person_in_steamy_room_tone3", + ["🧖🏾"] = "person_in_steamy_room_tone4", + ["🧖🏿"] = "person_in_steamy_room_tone5", + ["🧖‍♀️"] = "woman_in_steamy_room", + ["🧖🏻‍♀️"] = "woman_in_steamy_room_tone1", + ["🧖🏼‍♀️"] = "woman_in_steamy_room_tone2", + ["🧖🏽‍♀️"] = "woman_in_steamy_room_tone3", + ["🧖🏾‍♀️"] = "woman_in_steamy_room_tone4", + ["🧖🏿‍♀️"] = "woman_in_steamy_room_tone5", + ["🧖‍♂️"] = "man_in_steamy_room", + ["🧖🏻‍♂️"] = "man_in_steamy_room_tone1", + ["🧖🏼‍♂️"] = "man_in_steamy_room_tone2", + ["🧖🏽‍♂️"] = "man_in_steamy_room_tone3", + ["🧖🏾‍♂️"] = "man_in_steamy_room_tone4", + ["🧖🏿‍♂️"] = "man_in_steamy_room_tone5", + ["💅"] = "nail_care", + ["💅🏻"] = "nail_care_tone1", + ["💅🏼"] = "nail_care_tone2", + ["💅🏽"] = "nail_care_tone3", + ["💅🏾"] = "nail_care_tone4", + ["💅🏿"] = "nail_care_tone5", + ["🤳"] = "selfie", + ["🤳🏻"] = "selfie_tone1", + ["🤳🏼"] = "selfie_tone2", + ["🤳🏽"] = "selfie_tone3", + ["🤳🏾"] = "selfie_tone4", + ["🤳🏿"] = "selfie_tone5", + ["💃"] = "dancer", + ["💃🏻"] = "dancer_tone1", + ["💃🏼"] = "dancer_tone2", + ["💃🏽"] = "dancer_tone3", + ["💃🏾"] = "dancer_tone4", + ["💃🏿"] = "dancer_tone5", + ["🕺"] = "man_dancing", + ["🕺🏻"] = "man_dancing_tone1", + ["🕺🏼"] = "man_dancing_tone2", + ["🕺🏽"] = "man_dancing_tone3", + ["🕺🏿"] = "man_dancing_tone5", + ["🕺🏾"] = "man_dancing_tone4", + ["👯"] = "people_with_bunny_ears_partying", + ["👯‍♀️"] = "women_with_bunny_ears_partying", + ["👯‍♂️"] = "men_with_bunny_ears_partying", + ["🕴️"] = "levitate", + ["🕴🏻"] = "levitate_tone1", + ["🕴🏼"] = "levitate_tone2", + ["🕴🏽"] = "levitate_tone3", + ["🕴🏾"] = "levitate_tone4", + ["🕴🏿"] = "levitate_tone5", + ["🧑‍🦽"] = "person_in_manual_wheelchair", + ["🧑🏻‍🦽"] = "person_in_manual_wheelchair_tone1", + ["🧑🏼‍🦽"] = "person_in_manual_wheelchair_tone2", + ["🧑🏽‍🦽"] = "person_in_manual_wheelchair_tone3", + ["🧑🏾‍🦽"] = "person_in_manual_wheelchair_tone4", + ["🧑🏿‍🦽"] = "person_in_manual_wheelchair_tone5", + ["👩‍🦽"] = "woman_in_manual_wheelchair", + ["👩🏻‍🦽"] = "woman_in_manual_wheelchair_tone1", + ["👩🏼‍🦽"] = "woman_in_manual_wheelchair_tone2", + ["👩🏽‍🦽"] = "woman_in_manual_wheelchair_tone3", + ["👩🏾‍🦽"] = "woman_in_manual_wheelchair_tone4", + ["👩🏿‍🦽"] = "woman_in_manual_wheelchair_tone5", + ["👨‍🦽"] = "man_in_manual_wheelchair", + ["👨🏻‍🦽"] = "man_in_manual_wheelchair_tone1", + ["👨🏼‍🦽"] = "man_in_manual_wheelchair_tone2", + ["👨🏽‍🦽"] = "man_in_manual_wheelchair_tone3", + ["👨🏾‍🦽"] = "man_in_manual_wheelchair_tone4", + ["👨🏿‍🦽"] = "man_in_manual_wheelchair_tone5", + ["🧑‍🦼"] = "person_in_motorized_wheelchair", + ["🧑🏻‍🦼"] = "person_in_motorized_wheelchair_tone1", + ["🧑🏼‍🦼"] = "person_in_motorized_wheelchair_tone2", + ["🧑🏽‍🦼"] = "person_in_motorized_wheelchair_tone3", + ["🧑🏾‍🦼"] = "person_in_motorized_wheelchair_tone4", + ["🧑🏿‍🦼"] = "person_in_motorized_wheelchair_tone5", + ["👩‍🦼"] = "woman_in_motorized_wheelchair", + ["👩🏻‍🦼"] = "woman_in_motorized_wheelchair_tone1", + ["👩🏼‍🦼"] = "woman_in_motorized_wheelchair_tone2", + ["👩🏽‍🦼"] = "woman_in_motorized_wheelchair_tone3", + ["👩🏾‍🦼"] = "woman_in_motorized_wheelchair_tone4", + ["👩🏿‍🦼"] = "woman_in_motorized_wheelchair_tone5", + ["👨‍🦼"] = "man_in_motorized_wheelchair", + ["👨🏻‍🦼"] = "man_in_motorized_wheelchair_tone1", + ["👨🏼‍🦼"] = "man_in_motorized_wheelchair_tone2", + ["👨🏽‍🦼"] = "man_in_motorized_wheelchair_tone3", + ["👨🏾‍🦼"] = "man_in_motorized_wheelchair_tone4", + ["👨🏿‍🦼"] = "man_in_motorized_wheelchair_tone5", + ["🚶"] = "person_walking", + ["🚶🏻"] = "person_walking_tone1", + ["🚶🏼"] = "person_walking_tone2", + ["🚶🏽"] = "person_walking_tone3", + ["🚶🏾"] = "person_walking_tone4", + ["🚶🏿"] = "person_walking_tone5", + ["🚶‍♀️"] = "woman_walking", + ["🚶🏻‍♀️"] = "woman_walking_tone1", + ["🚶🏼‍♀️"] = "woman_walking_tone2", + ["🚶🏽‍♀️"] = "woman_walking_tone3", + ["🚶🏾‍♀️"] = "woman_walking_tone4", + ["🚶🏿‍♀️"] = "woman_walking_tone5", + ["🚶‍♂️"] = "man_walking", + ["🚶🏻‍♂️"] = "man_walking_tone1", + ["🚶🏼‍♂️"] = "man_walking_tone2", + ["🚶🏽‍♂️"] = "man_walking_tone3", + ["🚶🏾‍♂️"] = "man_walking_tone4", + ["🚶🏿‍♂️"] = "man_walking_tone5", + ["🧑‍🦯"] = "person_with_probing_cane", + ["🧑🏻‍🦯"] = "person_with_probing_cane_tone1", + ["🧑🏼‍🦯"] = "person_with_probing_cane_tone2", + ["🧑🏽‍🦯"] = "person_with_probing_cane_tone3", + ["🧑🏾‍🦯"] = "person_with_probing_cane_tone4", + ["🧑🏿‍🦯"] = "person_with_probing_cane_tone5", + ["👩‍🦯"] = "woman_with_probing_cane", + ["👩🏻‍🦯"] = "woman_with_probing_cane_tone1", + ["👩🏼‍🦯"] = "woman_with_probing_cane_tone2", + ["👩🏽‍🦯"] = "woman_with_probing_cane_tone3", + ["👩🏾‍🦯"] = "woman_with_probing_cane_tone4", + ["👩🏿‍🦯"] = "woman_with_probing_cane_tone5", + ["👨‍🦯"] = "man_with_probing_cane", + ["👨🏻‍🦯"] = "man_with_probing_cane_tone1", + ["👨🏽‍🦯"] = "man_with_probing_cane_tone3", + ["👨🏼‍🦯"] = "man_with_probing_cane_tone2", + ["👨🏾‍🦯"] = "man_with_probing_cane_tone4", + ["👨🏿‍🦯"] = "man_with_probing_cane_tone5", + ["🧎"] = "person_kneeling", + ["🧎🏻"] = "person_kneeling_tone1", + ["🧎🏼"] = "person_kneeling_tone2", + ["🧎🏽"] = "person_kneeling_tone3", + ["🧎🏾"] = "person_kneeling_tone4", + ["🧎🏿"] = "person_kneeling_tone5", + ["🧎‍♀️"] = "woman_kneeling", + ["🧎🏻‍♀️"] = "woman_kneeling_tone1", + ["🧎🏼‍♀️"] = "woman_kneeling_tone2", + ["🧎🏽‍♀️"] = "woman_kneeling_tone3", + ["🧎🏾‍♀️"] = "woman_kneeling_tone4", + ["🧎🏿‍♀️"] = "woman_kneeling_tone5", + ["🧎‍♂️"] = "man_kneeling", + ["🧎🏻‍♂️"] = "man_kneeling_tone1", + ["🧎🏼‍♂️"] = "man_kneeling_tone2", + ["🧎🏽‍♂️"] = "man_kneeling_tone3", + ["🧎🏾‍♂️"] = "man_kneeling_tone4", + ["🧎🏿‍♂️"] = "man_kneeling_tone5", + ["🏃"] = "person_running", + ["🏃🏻"] = "person_running_tone1", + ["🏃🏼"] = "person_running_tone2", + ["🏃🏽"] = "person_running_tone3", + ["🏃🏾"] = "person_running_tone4", + ["🏃🏿"] = "person_running_tone5", + ["🏃‍♀️"] = "woman_running", + ["🏃🏻‍♀️"] = "woman_running_tone1", + ["🏃🏼‍♀️"] = "woman_running_tone2", + ["🏃🏽‍♀️"] = "woman_running_tone3", + ["🏃🏾‍♀️"] = "woman_running_tone4", + ["🏃🏿‍♀️"] = "woman_running_tone5", + ["🏃‍♂️"] = "man_running", + ["🏃🏻‍♂️"] = "man_running_tone1", + ["🏃🏼‍♂️"] = "man_running_tone2", + ["🏃🏽‍♂️"] = "man_running_tone3", + ["🏃🏾‍♂️"] = "man_running_tone4", + ["🏃🏿‍♂️"] = "man_running_tone5", + ["🧍"] = "person_standing", + ["🧍🏻"] = "person_standing_tone1", + ["🧍🏼"] = "person_standing_tone2", + ["🧍🏽"] = "person_standing_tone3", + ["🧍🏾"] = "person_standing_tone4", + ["🧍🏿"] = "person_standing_tone5", + ["🧍‍♀️"] = "woman_standing", + ["🧍🏻‍♀️"] = "woman_standing_tone1", + ["🧍🏼‍♀️"] = "woman_standing_tone2", + ["🧍🏽‍♀️"] = "woman_standing_tone3", + ["🧍🏾‍♀️"] = "woman_standing_tone4", + ["🧍🏿‍♀️"] = "woman_standing_tone5", + ["🧍‍♂️"] = "man_standing", + ["🧍🏻‍♂️"] = "man_standing_tone1", + ["🧍🏼‍♂️"] = "man_standing_tone2", + ["🧍🏽‍♂️"] = "man_standing_tone3", + ["🧍🏾‍♂️"] = "man_standing_tone4", + ["🧍🏿‍♂️"] = "man_standing_tone5", + ["🧑‍🤝‍🧑"] = "people_holding_hands", + ["🧑🏻‍🤝‍🧑🏻"] = "people_holding_hands_tone1", + ["🧑🏻‍🤝‍🧑🏼"] = "people_holding_hands_tone1_tone2", + ["🧑🏻‍🤝‍🧑🏽"] = "people_holding_hands_tone1_tone3", + ["🧑🏻‍🤝‍🧑🏾"] = "people_holding_hands_tone1_tone4", + ["🧑🏻‍🤝‍🧑🏿"] = "people_holding_hands_tone1_tone5", + ["🧑🏼‍🤝‍🧑🏻"] = "people_holding_hands_tone2_tone1", + ["🧑🏼‍🤝‍🧑🏼"] = "people_holding_hands_tone2", + ["🧑🏼‍🤝‍🧑🏽"] = "people_holding_hands_tone2_tone3", + ["🧑🏼‍🤝‍🧑🏾"] = "people_holding_hands_tone2_tone4", + ["🧑🏼‍🤝‍🧑🏿"] = "people_holding_hands_tone2_tone5", + ["🧑🏽‍🤝‍🧑🏻"] = "people_holding_hands_tone3_tone1", + ["🧑🏽‍🤝‍🧑🏼"] = "people_holding_hands_tone3_tone2", + ["🧑🏽‍🤝‍🧑🏽"] = "people_holding_hands_tone3", + ["🧑🏽‍🤝‍🧑🏾"] = "people_holding_hands_tone3_tone4", + ["🧑🏽‍🤝‍🧑🏿"] = "people_holding_hands_tone3_tone5", + ["🧑🏾‍🤝‍🧑🏻"] = "people_holding_hands_tone4_tone1", + ["🧑🏾‍🤝‍🧑🏼"] = "people_holding_hands_tone4_tone2", + ["🧑🏾‍🤝‍🧑🏽"] = "people_holding_hands_tone4_tone3", + ["🧑🏾‍🤝‍🧑🏾"] = "people_holding_hands_tone4", + ["🧑🏾‍🤝‍🧑🏿"] = "people_holding_hands_tone4_tone5", + ["🧑🏿‍🤝‍🧑🏻"] = "people_holding_hands_tone5_tone1", + ["🧑🏿‍🤝‍🧑🏼"] = "people_holding_hands_tone5_tone2", + ["🧑🏿‍🤝‍🧑🏽"] = "people_holding_hands_tone5_tone3", + ["🧑🏿‍🤝‍🧑🏾"] = "people_holding_hands_tone5_tone4", + ["🧑🏿‍🤝‍🧑🏿"] = "people_holding_hands_tone5", + ["👫"] = "couple", + ["👫🏻"] = "woman_and_man_holding_hands_tone1", + ["👩🏻‍🤝‍👨🏼"] = "woman_and_man_holding_hands_tone1_tone2", + ["👩🏻‍🤝‍👨🏽"] = "woman_and_man_holding_hands_tone1_tone3", + ["👩🏻‍🤝‍👨🏾"] = "woman_and_man_holding_hands_tone1_tone4", + ["👩🏻‍🤝‍👨🏿"] = "woman_and_man_holding_hands_tone1_tone5", + ["👩🏼‍🤝‍👨🏻"] = "woman_and_man_holding_hands_tone2_tone1", + ["👫🏼"] = "woman_and_man_holding_hands_tone2", + ["👩🏼‍🤝‍👨🏽"] = "woman_and_man_holding_hands_tone2_tone3", + ["👩🏼‍🤝‍👨🏾"] = "woman_and_man_holding_hands_tone2_tone4", + ["👩🏼‍🤝‍👨🏿"] = "woman_and_man_holding_hands_tone2_tone5", + ["👩🏽‍🤝‍👨🏻"] = "woman_and_man_holding_hands_tone3_tone1", + ["👩🏽‍🤝‍👨🏼"] = "woman_and_man_holding_hands_tone3_tone2", + ["👫🏽"] = "woman_and_man_holding_hands_tone3", + ["👩🏽‍🤝‍👨🏾"] = "woman_and_man_holding_hands_tone3_tone4", + ["👩🏽‍🤝‍👨🏿"] = "woman_and_man_holding_hands_tone3_tone5", + ["👩🏾‍🤝‍👨🏻"] = "woman_and_man_holding_hands_tone4_tone1", + ["👩🏾‍🤝‍👨🏼"] = "woman_and_man_holding_hands_tone4_tone2", + ["👩🏾‍🤝‍👨🏽"] = "woman_and_man_holding_hands_tone4_tone3", + ["👫🏾"] = "woman_and_man_holding_hands_tone4", + ["👩🏾‍🤝‍👨🏿"] = "woman_and_man_holding_hands_tone4_tone5", + ["👩🏿‍🤝‍👨🏻"] = "woman_and_man_holding_hands_tone5_tone1", + ["👩🏿‍🤝‍👨🏼"] = "woman_and_man_holding_hands_tone5_tone2", + ["👩🏿‍🤝‍👨🏽"] = "woman_and_man_holding_hands_tone5_tone3", + ["👩🏿‍🤝‍👨🏾"] = "woman_and_man_holding_hands_tone5_tone4", + ["👫🏿"] = "woman_and_man_holding_hands_tone5", + ["👭"] = "two_women_holding_hands", + ["👭🏻"] = "women_holding_hands_tone1", + ["👩🏻‍🤝‍👩🏼"] = "women_holding_hands_tone1_tone2", + ["👩🏻‍🤝‍👩🏽"] = "women_holding_hands_tone1_tone3", + ["👩🏻‍🤝‍👩🏾"] = "women_holding_hands_tone1_tone4", + ["👩🏻‍🤝‍👩🏿"] = "women_holding_hands_tone1_tone5", + ["👩🏼‍🤝‍👩🏻"] = "women_holding_hands_tone2_tone1", + ["👭🏼"] = "women_holding_hands_tone2", + ["👩🏼‍🤝‍👩🏽"] = "women_holding_hands_tone2_tone3", + ["👩🏼‍🤝‍👩🏾"] = "women_holding_hands_tone2_tone4", + ["👩🏼‍🤝‍👩🏿"] = "women_holding_hands_tone2_tone5", + ["👩🏽‍🤝‍👩🏻"] = "women_holding_hands_tone3_tone1", + ["👩🏽‍🤝‍👩🏼"] = "women_holding_hands_tone3_tone2", + ["👭🏽"] = "women_holding_hands_tone3", + ["👩🏽‍🤝‍👩🏾"] = "women_holding_hands_tone3_tone4", + ["👩🏽‍🤝‍👩🏿"] = "women_holding_hands_tone3_tone5", + ["👩🏾‍🤝‍👩🏻"] = "women_holding_hands_tone4_tone1", + ["👩🏾‍🤝‍👩🏼"] = "women_holding_hands_tone4_tone2", + ["👩🏾‍🤝‍👩🏽"] = "women_holding_hands_tone4_tone3", + ["👭🏾"] = "women_holding_hands_tone4", + ["👩🏾‍🤝‍👩🏿"] = "women_holding_hands_tone4_tone5", + ["👩🏿‍🤝‍👩🏻"] = "women_holding_hands_tone5_tone1", + ["👩🏿‍🤝‍👩🏼"] = "women_holding_hands_tone5_tone2", + ["👩🏿‍🤝‍👩🏽"] = "women_holding_hands_tone5_tone3", + ["👩🏿‍🤝‍👩🏾"] = "women_holding_hands_tone5_tone4", + ["👭🏿"] = "women_holding_hands_tone5", + ["👬"] = "two_men_holding_hands", + ["👬🏻"] = "men_holding_hands_tone1", + ["👨🏻‍🤝‍👨🏼"] = "men_holding_hands_tone1_tone2", + ["👨🏻‍🤝‍👨🏽"] = "men_holding_hands_tone1_tone3", + ["👨🏻‍🤝‍👨🏾"] = "men_holding_hands_tone1_tone4", + ["👨🏻‍🤝‍👨🏿"] = "men_holding_hands_tone1_tone5", + ["👨🏼‍🤝‍👨🏻"] = "men_holding_hands_tone2_tone1", + ["👬🏼"] = "men_holding_hands_tone2", + ["👨🏼‍🤝‍👨🏽"] = "men_holding_hands_tone2_tone3", + ["👨🏼‍🤝‍👨🏾"] = "men_holding_hands_tone2_tone4", + ["👨🏼‍🤝‍👨🏿"] = "men_holding_hands_tone2_tone5", + ["👨🏽‍🤝‍👨🏻"] = "men_holding_hands_tone3_tone1", + ["👨🏽‍🤝‍👨🏼"] = "men_holding_hands_tone3_tone2", + ["👬🏽"] = "men_holding_hands_tone3", + ["👨🏽‍🤝‍👨🏾"] = "men_holding_hands_tone3_tone4", + ["👨🏽‍🤝‍👨🏿"] = "men_holding_hands_tone3_tone5", + ["👨🏾‍🤝‍👨🏻"] = "men_holding_hands_tone4_tone1", + ["👨🏾‍🤝‍👨🏼"] = "men_holding_hands_tone4_tone2", + ["👨🏾‍🤝‍👨🏽"] = "men_holding_hands_tone4_tone3", + ["👬🏾"] = "men_holding_hands_tone4", + ["👨🏾‍🤝‍👨🏿"] = "men_holding_hands_tone4_tone5", + ["👨🏿‍🤝‍👨🏻"] = "men_holding_hands_tone5_tone1", + ["👨🏿‍🤝‍👨🏼"] = "men_holding_hands_tone5_tone2", + ["👨🏿‍🤝‍👨🏽"] = "men_holding_hands_tone5_tone3", + ["👨🏿‍🤝‍👨🏾"] = "men_holding_hands_tone5_tone4", + ["👬🏿"] = "men_holding_hands_tone5", + ["💑"] = "couple_with_heart", + ["💑🏻"] = "couple_with_heart_tone1", + ["🧑🏻‍❤️‍🧑🏼"] = "couple_with_heart_person_person_tone1_tone2", + ["🧑🏻‍❤️‍🧑🏽"] = "couple_with_heart_person_person_tone1_tone3", + ["🧑🏻‍❤️‍🧑🏾"] = "couple_with_heart_person_person_tone1_tone4", + ["🧑🏻‍❤️‍🧑🏿"] = "couple_with_heart_person_person_tone1_tone5", + ["🧑🏼‍❤️‍🧑🏻"] = "couple_with_heart_person_person_tone2_tone1", + ["💑🏼"] = "couple_with_heart_tone2", + ["🧑🏼‍❤️‍🧑🏽"] = "couple_with_heart_person_person_tone2_tone3", + ["🧑🏼‍❤️‍🧑🏾"] = "couple_with_heart_person_person_tone2_tone4", + ["🧑🏼‍❤️‍🧑🏿"] = "couple_with_heart_person_person_tone2_tone5", + ["🧑🏽‍❤️‍🧑🏻"] = "couple_with_heart_person_person_tone3_tone1", + ["🧑🏽‍❤️‍🧑🏼"] = "couple_with_heart_person_person_tone3_tone2", + ["💑🏽"] = "couple_with_heart_tone3", + ["🧑🏽‍❤️‍🧑🏾"] = "couple_with_heart_person_person_tone3_tone4", + ["🧑🏽‍❤️‍🧑🏿"] = "couple_with_heart_person_person_tone3_tone5", + ["🧑🏾‍❤️‍🧑🏻"] = "couple_with_heart_person_person_tone4_tone1", + ["🧑🏾‍❤️‍🧑🏼"] = "couple_with_heart_person_person_tone4_tone2", + ["🧑🏾‍❤️‍🧑🏽"] = "couple_with_heart_person_person_tone4_tone3", + ["💑🏾"] = "couple_with_heart_tone4", + ["🧑🏾‍❤️‍🧑🏿"] = "couple_with_heart_person_person_tone4_tone5", + ["🧑🏿‍❤️‍🧑🏻"] = "couple_with_heart_person_person_tone5_tone1", + ["🧑🏿‍❤️‍🧑🏼"] = "couple_with_heart_person_person_tone5_tone2", + ["🧑🏿‍❤️‍🧑🏽"] = "couple_with_heart_person_person_tone5_tone3", + ["🧑🏿‍❤️‍🧑🏾"] = "couple_with_heart_person_person_tone5_tone4", + ["💑🏿"] = "couple_with_heart_tone5", + ["👩‍❤️‍👨"] = "couple_with_heart_woman_man", + ["👩🏻‍❤️‍👨🏻"] = "couple_with_heart_woman_man_tone1", + ["👩🏻‍❤️‍👨🏼"] = "couple_with_heart_woman_man_tone1_tone2", + ["👩🏻‍❤️‍👨🏽"] = "couple_with_heart_woman_man_tone1_tone3", + ["👩🏻‍❤️‍👨🏾"] = "couple_with_heart_woman_man_tone1_tone4", + ["👩🏻‍❤️‍👨🏿"] = "couple_with_heart_woman_man_tone1_tone5", + ["👩🏼‍❤️‍👨🏻"] = "couple_with_heart_woman_man_tone2_tone1", + ["👩🏼‍❤️‍👨🏼"] = "couple_with_heart_woman_man_tone2", + ["👩🏼‍❤️‍👨🏽"] = "couple_with_heart_woman_man_tone2_tone3", + ["👩🏼‍❤️‍👨🏾"] = "couple_with_heart_woman_man_tone2_tone4", + ["👩🏼‍❤️‍👨🏿"] = "couple_with_heart_woman_man_tone2_tone5", + ["👩🏽‍❤️‍👨🏻"] = "couple_with_heart_woman_man_tone3_tone1", + ["👩🏽‍❤️‍👨🏼"] = "couple_with_heart_woman_man_tone3_tone2", + ["👩🏽‍❤️‍👨🏽"] = "couple_with_heart_woman_man_tone3", + ["👩🏽‍❤️‍👨🏾"] = "couple_with_heart_woman_man_tone3_tone4", + ["👩🏽‍❤️‍👨🏿"] = "couple_with_heart_woman_man_tone3_tone5", + ["👩🏾‍❤️‍👨🏻"] = "couple_with_heart_woman_man_tone4_tone1", + ["👩🏾‍❤️‍👨🏼"] = "couple_with_heart_woman_man_tone4_tone2", + ["👩🏾‍❤️‍👨🏽"] = "couple_with_heart_woman_man_tone4_tone3", + ["👩🏾‍❤️‍👨🏾"] = "couple_with_heart_woman_man_tone4", + ["👩🏾‍❤️‍👨🏿"] = "couple_with_heart_woman_man_tone4_tone5", + ["👩🏿‍❤️‍👨🏻"] = "couple_with_heart_woman_man_tone5_tone1", + ["👩🏿‍❤️‍👨🏼"] = "couple_with_heart_woman_man_tone5_tone2", + ["👩🏿‍❤️‍👨🏽"] = "couple_with_heart_woman_man_tone5_tone3", + ["👩🏿‍❤️‍👨🏾"] = "couple_with_heart_woman_man_tone5_tone4", + ["👩🏿‍❤️‍👨🏿"] = "couple_with_heart_woman_man_tone5", + ["👩‍❤️‍👩"] = "couple_ww", + ["👩🏻‍❤️‍👩🏻"] = "couple_with_heart_woman_woman_tone1", + ["👩🏻‍❤️‍👩🏼"] = "couple_with_heart_woman_woman_tone1_tone2", + ["👩🏻‍❤️‍👩🏽"] = "couple_with_heart_woman_woman_tone1_tone3", + ["👩🏻‍❤️‍👩🏾"] = "couple_with_heart_woman_woman_tone1_tone4", + ["👩🏻‍❤️‍👩🏿"] = "couple_with_heart_woman_woman_tone1_tone5", + ["👩🏼‍❤️‍👩🏻"] = "couple_with_heart_woman_woman_tone2_tone1", + ["👩🏼‍❤️‍👩🏼"] = "couple_with_heart_woman_woman_tone2", + ["👩🏼‍❤️‍👩🏽"] = "couple_with_heart_woman_woman_tone2_tone3", + ["👩🏼‍❤️‍👩🏾"] = "couple_with_heart_woman_woman_tone2_tone4", + ["👩🏼‍❤️‍👩🏿"] = "couple_with_heart_woman_woman_tone2_tone5", + ["👩🏽‍❤️‍👩🏻"] = "couple_with_heart_woman_woman_tone3_tone1", + ["👩🏽‍❤️‍👩🏼"] = "couple_with_heart_woman_woman_tone3_tone2", + ["👩🏽‍❤️‍👩🏽"] = "couple_with_heart_woman_woman_tone3", + ["👩🏽‍❤️‍👩🏾"] = "couple_with_heart_woman_woman_tone3_tone4", + ["👩🏽‍❤️‍👩🏿"] = "couple_with_heart_woman_woman_tone3_tone5", + ["👩🏾‍❤️‍👩🏻"] = "couple_with_heart_woman_woman_tone4_tone1", + ["👩🏾‍❤️‍👩🏼"] = "couple_with_heart_woman_woman_tone4_tone2", + ["👩🏾‍❤️‍👩🏽"] = "couple_with_heart_woman_woman_tone4_tone3", + ["👩🏾‍❤️‍👩🏾"] = "couple_with_heart_woman_woman_tone4", + ["👩🏾‍❤️‍👩🏿"] = "couple_with_heart_woman_woman_tone4_tone5", + ["👩🏿‍❤️‍👩🏻"] = "couple_with_heart_woman_woman_tone5_tone1", + ["👩🏿‍❤️‍👩🏼"] = "couple_with_heart_woman_woman_tone5_tone2", + ["👩🏿‍❤️‍👩🏽"] = "couple_with_heart_woman_woman_tone5_tone3", + ["👩🏿‍❤️‍👩🏾"] = "couple_with_heart_woman_woman_tone5_tone4", + ["👩🏿‍❤️‍👩🏿"] = "couple_with_heart_woman_woman_tone5", + ["👨‍❤️‍👨"] = "couple_mm", + ["👨🏻‍❤️‍👨🏻"] = "couple_with_heart_man_man_tone1", + ["👨🏻‍❤️‍👨🏼"] = "couple_with_heart_man_man_tone1_tone2", + ["👨🏻‍❤️‍👨🏽"] = "couple_with_heart_man_man_tone1_tone3", + ["👨🏻‍❤️‍👨🏾"] = "couple_with_heart_man_man_tone1_tone4", + ["👨🏻‍❤️‍👨🏿"] = "couple_with_heart_man_man_tone1_tone5", + ["👨🏼‍❤️‍👨🏻"] = "couple_with_heart_man_man_tone2_tone1", + ["👨🏼‍❤️‍👨🏼"] = "couple_with_heart_man_man_tone2", + ["👨🏼‍❤️‍👨🏽"] = "couple_with_heart_man_man_tone2_tone3", + ["👨🏼‍❤️‍👨🏾"] = "couple_with_heart_man_man_tone2_tone4", + ["👨🏼‍❤️‍👨🏿"] = "couple_with_heart_man_man_tone2_tone5", + ["👨🏽‍❤️‍👨🏻"] = "couple_with_heart_man_man_tone3_tone1", + ["👨🏽‍❤️‍👨🏼"] = "couple_with_heart_man_man_tone3_tone2", + ["👨🏽‍❤️‍👨🏽"] = "couple_with_heart_man_man_tone3", + ["👨🏽‍❤️‍👨🏾"] = "couple_with_heart_man_man_tone3_tone4", + ["👨🏽‍❤️‍👨🏿"] = "couple_with_heart_man_man_tone3_tone5", + ["👨🏾‍❤️‍👨🏻"] = "couple_with_heart_man_man_tone4_tone1", + ["👨🏾‍❤️‍👨🏼"] = "couple_with_heart_man_man_tone4_tone2", + ["👨🏾‍❤️‍👨🏽"] = "couple_with_heart_man_man_tone4_tone3", + ["👨🏾‍❤️‍👨🏾"] = "couple_with_heart_man_man_tone4", + ["👨🏾‍❤️‍👨🏿"] = "couple_with_heart_man_man_tone4_tone5", + ["👨🏿‍❤️‍👨🏻"] = "couple_with_heart_man_man_tone5_tone1", + ["👨🏿‍❤️‍👨🏼"] = "couple_with_heart_man_man_tone5_tone2", + ["👨🏿‍❤️‍👨🏽"] = "couple_with_heart_man_man_tone5_tone3", + ["👨🏿‍❤️‍👨🏾"] = "couple_with_heart_man_man_tone5_tone4", + ["👨🏿‍❤️‍👨🏿"] = "couple_with_heart_man_man_tone5", + ["💏"] = "couplekiss", + ["🧑🏿‍❤️‍💋‍🧑🏾"] = "kiss_person_person_tone5_tone4", + ["💏🏻"] = "kiss_tone1", + ["🧑🏻‍❤️‍💋‍🧑🏼"] = "kiss_person_person_tone1_tone2", + ["🧑🏻‍❤️‍💋‍🧑🏽"] = "kiss_person_person_tone1_tone3", + ["🧑🏻‍❤️‍💋‍🧑🏾"] = "kiss_person_person_tone1_tone4", + ["🧑🏻‍❤️‍💋‍🧑🏿"] = "kiss_person_person_tone1_tone5", + ["🧑🏼‍❤️‍💋‍🧑🏻"] = "kiss_person_person_tone2_tone1", + ["💏🏼"] = "kiss_tone2", + ["🧑🏼‍❤️‍💋‍🧑🏽"] = "kiss_person_person_tone2_tone3", + ["🧑🏼‍❤️‍💋‍🧑🏾"] = "kiss_person_person_tone2_tone4", + ["🧑🏼‍❤️‍💋‍🧑🏿"] = "kiss_person_person_tone2_tone5", + ["🧑🏽‍❤️‍💋‍🧑🏻"] = "kiss_person_person_tone3_tone1", + ["🧑🏽‍❤️‍💋‍🧑🏼"] = "kiss_person_person_tone3_tone2", + ["💏🏽"] = "kiss_tone3", + ["🧑🏽‍❤️‍💋‍🧑🏾"] = "kiss_person_person_tone3_tone4", + ["🧑🏽‍❤️‍💋‍🧑🏿"] = "kiss_person_person_tone3_tone5", + ["🧑🏾‍❤️‍💋‍🧑🏻"] = "kiss_person_person_tone4_tone1", + ["🧑🏾‍❤️‍💋‍🧑🏼"] = "kiss_person_person_tone4_tone2", + ["🧑🏾‍❤️‍💋‍🧑🏽"] = "kiss_person_person_tone4_tone3", + ["💏🏾"] = "kiss_tone4", + ["🧑🏾‍❤️‍💋‍🧑🏿"] = "kiss_person_person_tone4_tone5", + ["🧑🏿‍❤️‍💋‍🧑🏻"] = "kiss_person_person_tone5_tone1", + ["🧑🏿‍❤️‍💋‍🧑🏼"] = "kiss_person_person_tone5_tone2", + ["🧑🏿‍❤️‍💋‍🧑🏽"] = "kiss_person_person_tone5_tone3", + ["💏🏿"] = "kiss_tone5", + ["👩‍❤️‍💋‍👨"] = "kiss_woman_man", + ["👩🏻‍❤️‍💋‍👨🏻"] = "kiss_woman_man_tone1", + ["👩🏻‍❤️‍💋‍👨🏼"] = "kiss_woman_man_tone1_tone2", + ["👩🏻‍❤️‍💋‍👨🏽"] = "kiss_woman_man_tone1_tone3", + ["👩🏻‍❤️‍💋‍👨🏾"] = "kiss_woman_man_tone1_tone4", + ["👩🏻‍❤️‍💋‍👨🏿"] = "kiss_woman_man_tone1_tone5", + ["👩🏼‍❤️‍💋‍👨🏻"] = "kiss_woman_man_tone2_tone1", + ["👩🏼‍❤️‍💋‍👨🏼"] = "kiss_woman_man_tone2", + ["👩🏼‍❤️‍💋‍👨🏽"] = "kiss_woman_man_tone2_tone3", + ["👩🏼‍❤️‍💋‍👨🏾"] = "kiss_woman_man_tone2_tone4", + ["👩🏼‍❤️‍💋‍👨🏿"] = "kiss_woman_man_tone2_tone5", + ["👩🏽‍❤️‍💋‍👨🏻"] = "kiss_woman_man_tone3_tone1", + ["👩🏽‍❤️‍💋‍👨🏼"] = "kiss_woman_man_tone3_tone2", + ["👩🏽‍❤️‍💋‍👨🏽"] = "kiss_woman_man_tone3", + ["👩🏽‍❤️‍💋‍👨🏾"] = "kiss_woman_man_tone3_tone4", + ["👩🏽‍❤️‍💋‍👨🏿"] = "kiss_woman_man_tone3_tone5", + ["👩🏾‍❤️‍💋‍👨🏻"] = "kiss_woman_man_tone4_tone1", + ["👩🏾‍❤️‍💋‍👨🏼"] = "kiss_woman_man_tone4_tone2", + ["👩🏾‍❤️‍💋‍👨🏽"] = "kiss_woman_man_tone4_tone3", + ["👩🏾‍❤️‍💋‍👨🏾"] = "kiss_woman_man_tone4", + ["👩🏾‍❤️‍💋‍👨🏿"] = "kiss_woman_man_tone4_tone5", + ["👩🏿‍❤️‍💋‍👨🏻"] = "kiss_woman_man_tone5_tone1", + ["👩🏿‍❤️‍💋‍👨🏼"] = "kiss_woman_man_tone5_tone2", + ["👩🏿‍❤️‍💋‍👨🏽"] = "kiss_woman_man_tone5_tone3", + ["👩🏿‍❤️‍💋‍👨🏾"] = "kiss_woman_man_tone5_tone4", + ["👩🏿‍❤️‍💋‍👨🏿"] = "kiss_woman_man_tone5", + ["👩‍❤️‍💋‍👩"] = "kiss_ww", + ["👩🏻‍❤️‍💋‍👩🏻"] = "kiss_woman_woman_tone1", + ["👩🏻‍❤️‍💋‍👩🏼"] = "kiss_woman_woman_tone1_tone2", + ["👩🏻‍❤️‍💋‍👩🏽"] = "kiss_woman_woman_tone1_tone3", + ["👩🏻‍❤️‍💋‍👩🏾"] = "kiss_woman_woman_tone1_tone4", + ["👩🏻‍❤️‍💋‍👩🏿"] = "kiss_woman_woman_tone1_tone5", + ["👩🏼‍❤️‍💋‍👩🏻"] = "kiss_woman_woman_tone2_tone1", + ["👩🏼‍❤️‍💋‍👩🏼"] = "kiss_woman_woman_tone2", + ["👩🏼‍❤️‍💋‍👩🏽"] = "kiss_woman_woman_tone2_tone3", + ["👩🏼‍❤️‍💋‍👩🏾"] = "kiss_woman_woman_tone2_tone4", + ["👩🏼‍❤️‍💋‍👩🏿"] = "kiss_woman_woman_tone2_tone5", + ["👩🏽‍❤️‍💋‍👩🏻"] = "kiss_woman_woman_tone3_tone1", + ["👩🏽‍❤️‍💋‍👩🏼"] = "kiss_woman_woman_tone3_tone2", + ["👩🏽‍❤️‍💋‍👩🏽"] = "kiss_woman_woman_tone3", + ["👩🏽‍❤️‍💋‍👩🏾"] = "kiss_woman_woman_tone3_tone4", + ["👩🏽‍❤️‍💋‍👩🏿"] = "kiss_woman_woman_tone3_tone5", + ["👩🏾‍❤️‍💋‍👩🏻"] = "kiss_woman_woman_tone4_tone1", + ["👩🏾‍❤️‍💋‍👩🏼"] = "kiss_woman_woman_tone4_tone2", + ["👩🏾‍❤️‍💋‍👩🏽"] = "kiss_woman_woman_tone4_tone3", + ["👩🏾‍❤️‍💋‍👩🏾"] = "kiss_woman_woman_tone4", + ["👩🏾‍❤️‍💋‍👩🏿"] = "kiss_woman_woman_tone4_tone5", + ["👩🏿‍❤️‍💋‍👩🏻"] = "kiss_woman_woman_tone5_tone1", + ["👩🏿‍❤️‍💋‍👩🏼"] = "kiss_woman_woman_tone5_tone2", + ["👩🏿‍❤️‍💋‍👩🏽"] = "kiss_woman_woman_tone5_tone3", + ["👩🏿‍❤️‍💋‍👩🏾"] = "kiss_woman_woman_tone5_tone4", + ["👩🏿‍❤️‍💋‍👩🏿"] = "kiss_woman_woman_tone5", + ["👨‍❤️‍💋‍👨"] = "kiss_mm", + ["👨🏻‍❤️‍💋‍👨🏻"] = "kiss_man_man_tone1", + ["👨🏻‍❤️‍💋‍👨🏼"] = "kiss_man_man_tone1_tone2", + ["👨🏻‍❤️‍💋‍👨🏽"] = "kiss_man_man_tone1_tone3", + ["👨🏻‍❤️‍💋‍👨🏾"] = "kiss_man_man_tone1_tone4", + ["👨🏻‍❤️‍💋‍👨🏿"] = "kiss_man_man_tone1_tone5", + ["👨🏼‍❤️‍💋‍👨🏻"] = "kiss_man_man_tone2_tone1", + ["👨🏼‍❤️‍💋‍👨🏼"] = "kiss_man_man_tone2", + ["👨🏼‍❤️‍💋‍👨🏽"] = "kiss_man_man_tone2_tone3", + ["👨🏼‍❤️‍💋‍👨🏾"] = "kiss_man_man_tone2_tone4", + ["👨🏼‍❤️‍💋‍👨🏿"] = "kiss_man_man_tone2_tone5", + ["👨🏽‍❤️‍💋‍👨🏻"] = "kiss_man_man_tone3_tone1", + ["👨🏽‍❤️‍💋‍👨🏼"] = "kiss_man_man_tone3_tone2", + ["👨🏽‍❤️‍💋‍👨🏽"] = "kiss_man_man_tone3", + ["👨🏽‍❤️‍💋‍👨🏾"] = "kiss_man_man_tone3_tone4", + ["👨🏽‍❤️‍💋‍👨🏿"] = "kiss_man_man_tone3_tone5", + ["👨🏾‍❤️‍💋‍👨🏻"] = "kiss_man_man_tone4_tone1", + ["👨🏾‍❤️‍💋‍👨🏼"] = "kiss_man_man_tone4_tone2", + ["👨🏾‍❤️‍💋‍👨🏽"] = "kiss_man_man_tone4_tone3", + ["👨🏾‍❤️‍💋‍👨🏾"] = "kiss_man_man_tone4", + ["👨🏾‍❤️‍💋‍👨🏿"] = "kiss_man_man_tone4_tone5", + ["👨🏿‍❤️‍💋‍👨🏻"] = "kiss_man_man_tone5_tone1", + ["👨🏿‍❤️‍💋‍👨🏼"] = "kiss_man_man_tone5_tone2", + ["👨🏿‍❤️‍💋‍👨🏽"] = "kiss_man_man_tone5_tone3", + ["👨🏿‍❤️‍💋‍👨🏾"] = "kiss_man_man_tone5_tone4", + ["👨🏿‍❤️‍💋‍👨🏿"] = "kiss_man_man_tone5", + ["👪"] = "family", + ["👨‍👩‍👦"] = "family_man_woman_boy", + ["👨‍👩‍👧"] = "family_mwg", + ["👨‍👩‍👧‍👦"] = "family_mwgb", + ["👨‍👩‍👦‍👦"] = "family_mwbb", + ["👨‍👩‍👧‍👧"] = "family_mwgg", + ["👩‍👩‍👦"] = "family_wwb", + ["👩‍👩‍👧"] = "family_wwg", + ["👩‍👩‍👧‍👦"] = "family_wwgb", + ["👩‍👩‍👦‍👦"] = "family_wwbb", + ["👩‍👩‍👧‍👧"] = "family_wwgg", + ["👨‍👨‍👦"] = "family_mmb", + ["👨‍👨‍👧"] = "family_mmg", + ["👨‍👨‍👧‍👦"] = "family_mmgb", + ["👨‍👨‍👦‍👦"] = "family_mmbb", + ["👨‍👨‍👧‍👧"] = "family_mmgg", + ["👩‍👦"] = "family_woman_boy", + ["👩‍👧"] = "family_woman_girl", + ["👩‍👧‍👦"] = "family_woman_girl_boy", + ["👩‍👦‍👦"] = "family_woman_boy_boy", + ["👩‍👧‍👧"] = "family_woman_girl_girl", + ["👨‍👦"] = "family_man_boy", + ["👨‍👧"] = "family_man_girl", + ["👨‍👧‍👦"] = "family_man_girl_boy", + ["👨‍👦‍👦"] = "family_man_boy_boy", + ["👨‍👧‍👧"] = "family_man_girl_girl", + ["🧶"] = "yarn", + ["🧵"] = "thread", + ["🧥"] = "coat", + ["🥼"] = "lab_coat", + ["🦺"] = "safety_vest", + ["👚"] = "womans_clothes", + ["👕"] = "shirt", + ["👖"] = "jeans", + ["🩲"] = "briefs", + ["🩳"] = "shorts", + ["👔"] = "necktie", + ["👗"] = "dress", + ["👙"] = "bikini", + ["🩱"] = "one_piece_swimsuit", + ["👘"] = "kimono", + ["🥻"] = "sari", + ["🥿"] = "womans_flat_shoe", + ["👠"] = "high_heel", + ["👡"] = "sandal", + ["👢"] = "boot", + ["👞"] = "mans_shoe", + ["👟"] = "athletic_shoe", + ["🥾"] = "hiking_boot", + ["🩴"] = "thong_sandal", + ["🧦"] = "socks", + ["🧤"] = "gloves", + ["🧣"] = "scarf", + ["🎩"] = "tophat", + ["🧢"] = "billed_cap", + ["👒"] = "womans_hat", + ["🎓"] = "mortar_board", + ["⛑️"] = "helmet_with_cross", + ["🪖"] = "military_helmet", + ["👑"] = "crown", + ["💍"] = "ring", + ["👝"] = "pouch", + ["👛"] = "purse", + ["👜"] = "handbag", + ["💼"] = "briefcase", + ["🎒"] = "school_satchel", + ["🧳"] = "luggage", + ["👓"] = "eyeglasses", + ["🕶️"] = "dark_sunglasses", + ["🥽"] = "goggles", + ["🌂"] = "closed_umbrella", + ["🐶"] = "dog", + ["🐱"] = "cat", + ["🐭"] = "mouse", + ["🐹"] = "hamster", + ["🐰"] = "rabbit", + ["🦊"] = "fox", + ["🐻"] = "bear", + ["🐼"] = "panda_face", + ["🐻‍❄️"] = "polar_bear", + ["🐨"] = "koala", + ["🐯"] = "tiger", + ["🦁"] = "lion_face", + ["🐮"] = "cow", + ["🐷"] = "pig", + ["🐽"] = "pig_nose", + ["🐸"] = "frog", + ["🐵"] = "monkey_face", + ["🙈"] = "see_no_evil", + ["🙉"] = "hear_no_evil", + ["🙊"] = "speak_no_evil", + ["🐒"] = "monkey", + ["🐔"] = "chicken", + ["🐧"] = "penguin", + ["🐦"] = "bird", + ["🐤"] = "baby_chick", + ["🐣"] = "hatching_chick", + ["🐥"] = "hatched_chick", + ["🦆"] = "duck", + ["🦤"] = "dodo", + ["🦅"] = "eagle", + ["🦉"] = "owl", + ["🦇"] = "bat", + ["🐺"] = "wolf", + ["🐗"] = "boar", + ["🐴"] = "horse", + ["🦄"] = "unicorn", + ["🐝"] = "bee", + ["🐛"] = "bug", + ["🦋"] = "butterfly", + ["🐌"] = "snail", + ["🪱"] = "worm", + ["🐞"] = "lady_beetle", + ["🐜"] = "ant", + ["🪰"] = "fly", + ["🦟"] = "mosquito", + ["🪳"] = "cockroach", + ["🪲"] = "beetle", + ["🦗"] = "cricket", + ["🕷️"] = "spider", + ["🕸️"] = "spider_web", + ["🦂"] = "scorpion", + ["🐢"] = "turtle", + ["🐍"] = "snake", + ["🦎"] = "lizard", + ["🦖"] = "t_rex", + ["🦕"] = "sauropod", + ["🐙"] = "octopus", + ["🦑"] = "squid", + ["🦐"] = "shrimp", + ["🦞"] = "lobster", + ["🦀"] = "crab", + ["🐡"] = "blowfish", + ["🐠"] = "tropical_fish", + ["🐟"] = "fish", + ["🦭"] = "seal", + ["🐬"] = "dolphin", + ["🐳"] = "whale", + ["🐋"] = "whale2", + ["🦈"] = "shark", + ["🐊"] = "crocodile", + ["🐅"] = "tiger2", + ["🐆"] = "leopard", + ["🦓"] = "zebra", + ["🦍"] = "gorilla", + ["🦧"] = "orangutan", + ["🐘"] = "elephant", + ["🦣"] = "mammoth", + ["🦬"] = "bison", + ["🦛"] = "hippopotamus", + ["🦏"] = "rhino", + ["🐪"] = "dromedary_camel", + ["🐫"] = "camel", + ["🦒"] = "giraffe", + ["🦘"] = "kangaroo", + ["🐃"] = "water_buffalo", + ["🐂"] = "ox", + ["🐄"] = "cow2", + ["🐎"] = "racehorse", + ["🐖"] = "pig2", + ["🐏"] = "ram", + ["🐑"] = "sheep", + ["🦙"] = "llama", + ["🐐"] = "goat", + ["🦌"] = "deer", + ["🐕"] = "dog2", + ["🐩"] = "poodle", + ["🦮"] = "guide_dog", + ["🐕‍🦺"] = "service_dog", + ["🐈"] = "cat2", + ["🐈‍⬛"] = "black_cat", + ["🐓"] = "rooster", + ["🦃"] = "turkey", + ["🦚"] = "peacock", + ["🦜"] = "parrot", + ["🦢"] = "swan", + ["🦩"] = "flamingo", + ["🕊️"] = "dove", + ["🐇"] = "rabbit2", + ["🦝"] = "raccoon", + ["🦨"] = "skunk", + ["🦡"] = "badger", + ["🦫"] = "beaver", + ["🦦"] = "otter", + ["🦥"] = "sloth", + ["🐁"] = "mouse2", + ["🐀"] = "rat", + ["🐿️"] = "chipmunk", + ["🦔"] = "hedgehog", + ["🐾"] = "feet", + ["🐉"] = "dragon", + ["🐲"] = "dragon_face", + ["🌵"] = "cactus", + ["🎄"] = "christmas_tree", + ["🌲"] = "evergreen_tree", + ["🌳"] = "deciduous_tree", + ["🌴"] = "palm_tree", + ["🌱"] = "seedling", + ["🌿"] = "herb", + ["☘️"] = "shamrock", + ["🍀"] = "four_leaf_clover", + ["🎍"] = "bamboo", + ["🎋"] = "tanabata_tree", + ["🍃"] = "leaves", + ["🍂"] = "fallen_leaf", + ["🍁"] = "maple_leaf", + ["🪶"] = "feather", + ["🍄"] = "mushroom", + ["🐚"] = "shell", + ["🪨"] = "rock", + ["🪵"] = "wood", + ["🌾"] = "ear_of_rice", + ["🪴"] = "potted_plant", + ["💐"] = "bouquet", + ["🌷"] = "tulip", + ["🌹"] = "rose", + ["🥀"] = "wilted_rose", + ["🌺"] = "hibiscus", + ["🌸"] = "cherry_blossom", + ["🌼"] = "blossom", + ["🌻"] = "sunflower", + ["🌞"] = "sun_with_face", + ["🌝"] = "full_moon_with_face", + ["🌛"] = "first_quarter_moon_with_face", + ["🌜"] = "last_quarter_moon_with_face", + ["🌚"] = "new_moon_with_face", + ["🌕"] = "full_moon", + ["🌖"] = "waning_gibbous_moon", + ["🌗"] = "last_quarter_moon", + ["🌘"] = "waning_crescent_moon", + ["🌑"] = "new_moon", + ["🌒"] = "waxing_crescent_moon", + ["🌓"] = "first_quarter_moon", + ["🌔"] = "waxing_gibbous_moon", + ["🌙"] = "crescent_moon", + ["🌎"] = "earth_americas", + ["🌍"] = "earth_africa", + ["🌏"] = "earth_asia", + ["🪐"] = "ringed_planet", + ["💫"] = "dizzy", + ["⭐"] = "star", + ["🌟"] = "star2", + ["✨"] = "sparkles", + ["⚡"] = "zap", + ["☄️"] = "comet", + ["💥"] = "boom", + ["🔥"] = "fire", + ["🌪️"] = "cloud_tornado", + ["🌈"] = "rainbow", + ["☀️"] = "sunny", + ["🌤️"] = "white_sun_small_cloud", + ["⛅"] = "partly_sunny", + ["🌥️"] = "white_sun_cloud", + ["☁️"] = "cloud", + ["🌦️"] = "white_sun_rain_cloud", + ["🌧️"] = "cloud_rain", + ["⛈️"] = "thunder_cloud_rain", + ["🌩️"] = "cloud_lightning", + ["🌨️"] = "cloud_snow", + ["❄️"] = "snowflake", + ["☃️"] = "snowman2", + ["⛄"] = "snowman", + ["🌬️"] = "wind_blowing_face", + ["💨"] = "dash", + ["💧"] = "droplet", + ["💦"] = "sweat_drops", + ["☔"] = "umbrella", + ["☂️"] = "umbrella2", + ["🌊"] = "ocean", + ["🌫️"] = "fog", + ["🍏"] = "green_apple", + ["🍎"] = "apple", + ["🍐"] = "pear", + ["🍊"] = "tangerine", + ["🍋"] = "lemon", + ["🍌"] = "banana", + ["🍉"] = "watermelon", + ["🍇"] = "grapes", + ["🫐"] = "blueberries", + ["🍓"] = "strawberry", + ["🍈"] = "melon", + ["🍒"] = "cherries", + ["🍑"] = "peach", + ["🥭"] = "mango", + ["🍍"] = "pineapple", + ["🥥"] = "coconut", + ["🥝"] = "kiwi", + ["🍅"] = "tomato", + ["🍆"] = "eggplant", + ["🥑"] = "avocado", + ["🫒"] = "olive", + ["🥦"] = "broccoli", + ["🥬"] = "leafy_green", + ["🫑"] = "bell_pepper", + ["🥒"] = "cucumber", + ["🌶️"] = "hot_pepper", + ["🌽"] = "corn", + ["🥕"] = "carrot", + ["🧄"] = "garlic", + ["🧅"] = "onion", + ["🥔"] = "potato", + ["🍠"] = "sweet_potato", + ["🥐"] = "croissant", + ["🥯"] = "bagel", + ["🍞"] = "bread", + ["🥖"] = "french_bread", + ["🫓"] = "flatbread", + ["🥨"] = "pretzel", + ["🧀"] = "cheese", + ["🥚"] = "egg", + ["🍳"] = "cooking", + ["🧈"] = "butter", + ["🥞"] = "pancakes", + ["🧇"] = "waffle", + ["🥓"] = "bacon", + ["🥩"] = "cut_of_meat", + ["🍗"] = "poultry_leg", + ["🍖"] = "meat_on_bone", + ["🌭"] = "hotdog", + ["🍔"] = "hamburger", + ["🍟"] = "fries", + ["🍕"] = "pizza", + ["🥪"] = "sandwich", + ["🥙"] = "stuffed_flatbread", + ["🧆"] = "falafel", + ["🌮"] = "taco", + ["🌯"] = "burrito", + ["🫔"] = "tamale", + ["🥗"] = "salad", + ["🥘"] = "shallow_pan_of_food", + ["🫕"] = "fondue", + ["🥫"] = "canned_food", + ["🍝"] = "spaghetti", + ["🍜"] = "ramen", + ["🍲"] = "stew", + ["🍛"] = "curry", + ["🍣"] = "sushi", + ["🍱"] = "bento", + ["🥟"] = "dumpling", + ["🦪"] = "oyster", + ["🍤"] = "fried_shrimp", + ["🍙"] = "rice_ball", + ["🍚"] = "rice", + ["🍘"] = "rice_cracker", + ["🍥"] = "fish_cake", + ["🥠"] = "fortune_cookie", + ["🥮"] = "moon_cake", + ["🍢"] = "oden", + ["🍡"] = "dango", + ["🍧"] = "shaved_ice", + ["🍨"] = "ice_cream", + ["🍦"] = "icecream", + ["🥧"] = "pie", + ["🧁"] = "cupcake", + ["🍰"] = "cake", + ["🎂"] = "birthday", + ["🍮"] = "custard", + ["🍭"] = "lollipop", + ["🍬"] = "candy", + ["🍫"] = "chocolate_bar", + ["🍿"] = "popcorn", + ["🍩"] = "doughnut", + ["🍪"] = "cookie", + ["🌰"] = "chestnut", + ["🥜"] = "peanuts", + ["🍯"] = "honey_pot", + ["🥛"] = "milk", + ["🍼"] = "baby_bottle", + ["☕"] = "coffee", + ["🍵"] = "tea", + ["🫖"] = "teapot", + ["🧉"] = "mate", + ["🧋"] = "bubble_tea", + ["🧃"] = "beverage_box", + ["🥤"] = "cup_with_straw", + ["🍶"] = "sake", + ["🍺"] = "beer", + ["🍻"] = "beers", + ["🥂"] = "champagne_glass", + ["🍷"] = "wine_glass", + ["🥃"] = "tumbler_glass", + ["🍸"] = "cocktail", + ["🍹"] = "tropical_drink", + ["🍾"] = "champagne", + ["🧊"] = "ice_cube", + ["🥄"] = "spoon", + ["🍴"] = "fork_and_knife", + ["🍽️"] = "fork_knife_plate", + ["🥣"] = "bowl_with_spoon", + ["🥡"] = "takeout_box", + ["🥢"] = "chopsticks", + ["🧂"] = "salt", + ["⚽"] = "soccer", + ["🏀"] = "basketball", + ["🏈"] = "football", + ["⚾"] = "baseball", + ["🥎"] = "softball", + ["🎾"] = "tennis", + ["🏐"] = "volleyball", + ["🏉"] = "rugby_football", + ["🥏"] = "flying_disc", + ["🪃"] = "boomerang", + ["🎱"] = "8ball", + ["🪀"] = "yo_yo", + ["🏓"] = "ping_pong", + ["🏸"] = "badminton", + ["🏒"] = "hockey", + ["🏑"] = "field_hockey", + ["🥍"] = "lacrosse", + ["🏏"] = "cricket_game", + ["🥅"] = "goal", + ["⛳"] = "golf", + ["🪁"] = "kite", + ["🏹"] = "bow_and_arrow", + ["🎣"] = "fishing_pole_and_fish", + ["🤿"] = "diving_mask", + ["🥊"] = "boxing_glove", + ["🥋"] = "martial_arts_uniform", + ["🎽"] = "running_shirt_with_sash", + ["🛹"] = "skateboard", + ["🛼"] = "roller_skate", + ["🛷"] = "sled", + ["⛸️"] = "ice_skate", + ["🥌"] = "curling_stone", + ["🎿"] = "ski", + ["⛷️"] = "skier", + ["🏂"] = "snowboarder", + ["🏂🏻"] = "snowboarder_tone1", + ["🏂🏼"] = "snowboarder_tone2", + ["🏂🏽"] = "snowboarder_tone3", + ["🏂🏾"] = "snowboarder_tone4", + ["🏂🏿"] = "snowboarder_tone5", + ["🪂"] = "parachute", + ["🏋️"] = "person_lifting_weights", + ["🏋🏻"] = "person_lifting_weights_tone1", + ["🏋🏼"] = "person_lifting_weights_tone2", + ["🏋🏽"] = "person_lifting_weights_tone3", + ["🏋🏾"] = "person_lifting_weights_tone4", + ["🏋🏿"] = "person_lifting_weights_tone5", + ["🏋️‍♀️"] = "woman_lifting_weights", + ["🏋🏻‍♀️"] = "woman_lifting_weights_tone1", + ["🏋🏼‍♀️"] = "woman_lifting_weights_tone2", + ["🏋🏽‍♀️"] = "woman_lifting_weights_tone3", + ["🏋🏾‍♀️"] = "woman_lifting_weights_tone4", + ["🏋🏿‍♀️"] = "woman_lifting_weights_tone5", + ["🏋️‍♂️"] = "man_lifting_weights", + ["🏋🏻‍♂️"] = "man_lifting_weights_tone1", + ["🏋🏼‍♂️"] = "man_lifting_weights_tone2", + ["🏋🏽‍♂️"] = "man_lifting_weights_tone3", + ["🏋🏾‍♂️"] = "man_lifting_weights_tone4", + ["🏋🏿‍♂️"] = "man_lifting_weights_tone5", + ["🤼"] = "people_wrestling", + ["🤼‍♀️"] = "women_wrestling", + ["🤼‍♂️"] = "men_wrestling", + ["🤸"] = "person_doing_cartwheel", + ["🤸🏻"] = "person_doing_cartwheel_tone1", + ["🤸🏼"] = "person_doing_cartwheel_tone2", + ["🤸🏽"] = "person_doing_cartwheel_tone3", + ["🤸🏾"] = "person_doing_cartwheel_tone4", + ["🤸🏿"] = "person_doing_cartwheel_tone5", + ["🤸‍♀️"] = "woman_cartwheeling", + ["🤸🏻‍♀️"] = "woman_cartwheeling_tone1", + ["🤸🏼‍♀️"] = "woman_cartwheeling_tone2", + ["🤸🏽‍♀️"] = "woman_cartwheeling_tone3", + ["🤸🏾‍♀️"] = "woman_cartwheeling_tone4", + ["🤸🏿‍♀️"] = "woman_cartwheeling_tone5", + ["🤸‍♂️"] = "man_cartwheeling", + ["🤸🏻‍♂️"] = "man_cartwheeling_tone1", + ["🤸🏼‍♂️"] = "man_cartwheeling_tone2", + ["🤸🏽‍♂️"] = "man_cartwheeling_tone3", + ["🤸🏾‍♂️"] = "man_cartwheeling_tone4", + ["🤸🏿‍♂️"] = "man_cartwheeling_tone5", + ["⛹️"] = "person_bouncing_ball", + ["⛹🏻"] = "person_bouncing_ball_tone1", + ["⛹🏼"] = "person_bouncing_ball_tone2", + ["⛹🏽"] = "person_bouncing_ball_tone3", + ["⛹🏾"] = "person_bouncing_ball_tone4", + ["⛹🏿"] = "person_bouncing_ball_tone5", + ["⛹️‍♀️"] = "woman_bouncing_ball", + ["⛹🏻‍♀️"] = "woman_bouncing_ball_tone1", + ["⛹🏼‍♀️"] = "woman_bouncing_ball_tone2", + ["⛹🏽‍♀️"] = "woman_bouncing_ball_tone3", + ["⛹🏾‍♀️"] = "woman_bouncing_ball_tone4", + ["⛹🏿‍♀️"] = "woman_bouncing_ball_tone5", + ["⛹️‍♂️"] = "man_bouncing_ball", + ["⛹🏻‍♂️"] = "man_bouncing_ball_tone1", + ["⛹🏼‍♂️"] = "man_bouncing_ball_tone2", + ["⛹🏽‍♂️"] = "man_bouncing_ball_tone3", + ["⛹🏾‍♂️"] = "man_bouncing_ball_tone4", + ["⛹🏿‍♂️"] = "man_bouncing_ball_tone5", + ["🤺"] = "person_fencing", + ["🤾"] = "person_playing_handball", + ["🤾🏻"] = "person_playing_handball_tone1", + ["🤾🏼"] = "person_playing_handball_tone2", + ["🤾🏽"] = "person_playing_handball_tone3", + ["🤾🏾"] = "person_playing_handball_tone4", + ["🤾🏿"] = "person_playing_handball_tone5", + ["🤾‍♀️"] = "woman_playing_handball", + ["🤾🏻‍♀️"] = "woman_playing_handball_tone1", + ["🤾🏼‍♀️"] = "woman_playing_handball_tone2", + ["🤾🏽‍♀️"] = "woman_playing_handball_tone3", + ["🤾🏾‍♀️"] = "woman_playing_handball_tone4", + ["🤾🏿‍♀️"] = "woman_playing_handball_tone5", + ["🤾‍♂️"] = "man_playing_handball", + ["🤾🏻‍♂️"] = "man_playing_handball_tone1", + ["🤾🏼‍♂️"] = "man_playing_handball_tone2", + ["🤾🏽‍♂️"] = "man_playing_handball_tone3", + ["🤾🏾‍♂️"] = "man_playing_handball_tone4", + ["🤾🏿‍♂️"] = "man_playing_handball_tone5", + ["🏌️"] = "person_golfing", + ["🏌🏻"] = "person_golfing_tone1", + ["🏌🏼"] = "person_golfing_tone2", + ["🏌🏽"] = "person_golfing_tone3", + ["🏌🏾"] = "person_golfing_tone4", + ["🏌🏿"] = "person_golfing_tone5", + ["🏌️‍♀️"] = "woman_golfing", + ["🏌🏻‍♀️"] = "woman_golfing_tone1", + ["🏌🏼‍♀️"] = "woman_golfing_tone2", + ["🏌🏽‍♀️"] = "woman_golfing_tone3", + ["🏌🏾‍♀️"] = "woman_golfing_tone4", + ["🏌🏿‍♀️"] = "woman_golfing_tone5", + ["🏌️‍♂️"] = "man_golfing", + ["🏌🏻‍♂️"] = "man_golfing_tone1", + ["🏌🏼‍♂️"] = "man_golfing_tone2", + ["🏌🏽‍♂️"] = "man_golfing_tone3", + ["🏌🏾‍♂️"] = "man_golfing_tone4", + ["🏌🏿‍♂️"] = "man_golfing_tone5", + ["🏇"] = "horse_racing", + ["🏇🏻"] = "horse_racing_tone1", + ["🏇🏼"] = "horse_racing_tone2", + ["🏇🏽"] = "horse_racing_tone3", + ["🏇🏾"] = "horse_racing_tone4", + ["🏇🏿"] = "horse_racing_tone5", + ["🧘"] = "person_in_lotus_position", + ["🧘🏻"] = "person_in_lotus_position_tone1", + ["🧘🏼"] = "person_in_lotus_position_tone2", + ["🧘🏽"] = "person_in_lotus_position_tone3", + ["🧘🏾"] = "person_in_lotus_position_tone4", + ["🧘🏿"] = "person_in_lotus_position_tone5", + ["🧘‍♀️"] = "woman_in_lotus_position", + ["🧘🏻‍♀️"] = "woman_in_lotus_position_tone1", + ["🧘🏼‍♀️"] = "woman_in_lotus_position_tone2", + ["🧘🏽‍♀️"] = "woman_in_lotus_position_tone3", + ["🧘🏾‍♀️"] = "woman_in_lotus_position_tone4", + ["🧘🏿‍♀️"] = "woman_in_lotus_position_tone5", + ["🧘‍♂️"] = "man_in_lotus_position", + ["🧘🏻‍♂️"] = "man_in_lotus_position_tone1", + ["🧘🏼‍♂️"] = "man_in_lotus_position_tone2", + ["🧘🏽‍♂️"] = "man_in_lotus_position_tone3", + ["🧘🏾‍♂️"] = "man_in_lotus_position_tone4", + ["🧘🏿‍♂️"] = "man_in_lotus_position_tone5", + ["🏄"] = "person_surfing", + ["🏄🏻"] = "person_surfing_tone1", + ["🏄🏼"] = "person_surfing_tone2", + ["🏄🏽"] = "person_surfing_tone3", + ["🏄🏾"] = "person_surfing_tone4", + ["🏄🏿"] = "person_surfing_tone5", + ["🏄‍♀️"] = "woman_surfing", + ["🏄🏻‍♀️"] = "woman_surfing_tone1", + ["🏄🏼‍♀️"] = "woman_surfing_tone2", + ["🏄🏽‍♀️"] = "woman_surfing_tone3", + ["🏄🏾‍♀️"] = "woman_surfing_tone4", + ["🏄🏿‍♀️"] = "woman_surfing_tone5", + ["🏄‍♂️"] = "man_surfing", + ["🏄🏻‍♂️"] = "man_surfing_tone1", + ["🏄🏼‍♂️"] = "man_surfing_tone2", + ["🏄🏽‍♂️"] = "man_surfing_tone3", + ["🏄🏾‍♂️"] = "man_surfing_tone4", + ["🏄🏿‍♂️"] = "man_surfing_tone5", + ["🏊"] = "person_swimming", + ["🏊🏻"] = "person_swimming_tone1", + ["🏊🏼"] = "person_swimming_tone2", + ["🏊🏽"] = "person_swimming_tone3", + ["🏊🏾"] = "person_swimming_tone4", + ["🏊🏿"] = "person_swimming_tone5", + ["🏊‍♀️"] = "woman_swimming", + ["🏊🏻‍♀️"] = "woman_swimming_tone1", + ["🏊🏼‍♀️"] = "woman_swimming_tone2", + ["🏊🏽‍♀️"] = "woman_swimming_tone3", + ["🏊🏾‍♀️"] = "woman_swimming_tone4", + ["🏊🏿‍♀️"] = "woman_swimming_tone5", + ["🏊‍♂️"] = "man_swimming", + ["🏊🏻‍♂️"] = "man_swimming_tone1", + ["🏊🏼‍♂️"] = "man_swimming_tone2", + ["🏊🏽‍♂️"] = "man_swimming_tone3", + ["🏊🏾‍♂️"] = "man_swimming_tone4", + ["🏊🏿‍♂️"] = "man_swimming_tone5", + ["🤽"] = "person_playing_water_polo", + ["🤽🏻"] = "person_playing_water_polo_tone1", + ["🤽🏼"] = "person_playing_water_polo_tone2", + ["🤽🏽"] = "person_playing_water_polo_tone3", + ["🤽🏾"] = "person_playing_water_polo_tone4", + ["🤽🏿"] = "person_playing_water_polo_tone5", + ["🤽‍♀️"] = "woman_playing_water_polo", + ["🤽🏻‍♀️"] = "woman_playing_water_polo_tone1", + ["🤽🏼‍♀️"] = "woman_playing_water_polo_tone2", + ["🤽🏽‍♀️"] = "woman_playing_water_polo_tone3", + ["🤽🏾‍♀️"] = "woman_playing_water_polo_tone4", + ["🤽🏿‍♀️"] = "woman_playing_water_polo_tone5", + ["🤽‍♂️"] = "man_playing_water_polo", + ["🤽🏻‍♂️"] = "man_playing_water_polo_tone1", + ["🤽🏼‍♂️"] = "man_playing_water_polo_tone2", + ["🤽🏽‍♂️"] = "man_playing_water_polo_tone3", + ["🤽🏾‍♂️"] = "man_playing_water_polo_tone4", + ["🤽🏿‍♂️"] = "man_playing_water_polo_tone5", + ["🚣"] = "person_rowing_boat", + ["🚣🏻"] = "person_rowing_boat_tone1", + ["🚣🏼"] = "person_rowing_boat_tone2", + ["🚣🏽"] = "person_rowing_boat_tone3", + ["🚣🏾"] = "person_rowing_boat_tone4", + ["🚣🏿"] = "person_rowing_boat_tone5", + ["🚣‍♀️"] = "woman_rowing_boat", + ["🚣🏻‍♀️"] = "woman_rowing_boat_tone1", + ["🚣🏼‍♀️"] = "woman_rowing_boat_tone2", + ["🚣🏽‍♀️"] = "woman_rowing_boat_tone3", + ["🚣🏾‍♀️"] = "woman_rowing_boat_tone4", + ["🚣🏿‍♀️"] = "woman_rowing_boat_tone5", + ["🚣‍♂️"] = "man_rowing_boat", + ["🚣🏻‍♂️"] = "man_rowing_boat_tone1", + ["🚣🏼‍♂️"] = "man_rowing_boat_tone2", + ["🚣🏽‍♂️"] = "man_rowing_boat_tone3", + ["🚣🏾‍♂️"] = "man_rowing_boat_tone4", + ["🚣🏿‍♂️"] = "man_rowing_boat_tone5", + ["🧗"] = "person_climbing", + ["🧗🏻"] = "person_climbing_tone1", + ["🧗🏼"] = "person_climbing_tone2", + ["🧗🏽"] = "person_climbing_tone3", + ["🧗🏾"] = "person_climbing_tone4", + ["🧗🏿"] = "person_climbing_tone5", + ["🧗‍♀️"] = "woman_climbing", + ["🧗🏻‍♀️"] = "woman_climbing_tone1", + ["🧗🏼‍♀️"] = "woman_climbing_tone2", + ["🧗🏽‍♀️"] = "woman_climbing_tone3", + ["🧗🏾‍♀️"] = "woman_climbing_tone4", + ["🧗🏿‍♀️"] = "woman_climbing_tone5", + ["🧗‍♂️"] = "man_climbing", + ["🧗🏻‍♂️"] = "man_climbing_tone1", + ["🧗🏼‍♂️"] = "man_climbing_tone2", + ["🧗🏽‍♂️"] = "man_climbing_tone3", + ["🧗🏾‍♂️"] = "man_climbing_tone4", + ["🧗🏿‍♂️"] = "man_climbing_tone5", + ["🚵"] = "person_mountain_biking", + ["🚵🏻"] = "person_mountain_biking_tone1", + ["🚵🏼"] = "person_mountain_biking_tone2", + ["🚵🏽"] = "person_mountain_biking_tone3", + ["🚵🏾"] = "person_mountain_biking_tone4", + ["🚵🏿"] = "person_mountain_biking_tone5", + ["🚵‍♀️"] = "woman_mountain_biking", + ["🚵🏻‍♀️"] = "woman_mountain_biking_tone1", + ["🚵🏼‍♀️"] = "woman_mountain_biking_tone2", + ["🚵🏽‍♀️"] = "woman_mountain_biking_tone3", + ["🚵🏾‍♀️"] = "woman_mountain_biking_tone4", + ["🚵🏿‍♀️"] = "woman_mountain_biking_tone5", + ["🚵‍♂️"] = "man_mountain_biking", + ["🚵🏻‍♂️"] = "man_mountain_biking_tone1", + ["🚵🏼‍♂️"] = "man_mountain_biking_tone2", + ["🚵🏽‍♂️"] = "man_mountain_biking_tone3", + ["🚵🏾‍♂️"] = "man_mountain_biking_tone4", + ["🚵🏿‍♂️"] = "man_mountain_biking_tone5", + ["🚴"] = "person_biking", + ["🚴🏻"] = "person_biking_tone1", + ["🚴🏼"] = "person_biking_tone2", + ["🚴🏽"] = "person_biking_tone3", + ["🚴🏾"] = "person_biking_tone4", + ["🚴🏿"] = "person_biking_tone5", + ["🚴‍♀️"] = "woman_biking", + ["🚴🏻‍♀️"] = "woman_biking_tone1", + ["🚴🏼‍♀️"] = "woman_biking_tone2", + ["🚴🏽‍♀️"] = "woman_biking_tone3", + ["🚴🏾‍♀️"] = "woman_biking_tone4", + ["🚴🏿‍♀️"] = "woman_biking_tone5", + ["🚴‍♂️"] = "man_biking", + ["🚴🏻‍♂️"] = "man_biking_tone1", + ["🚴🏼‍♂️"] = "man_biking_tone2", + ["🚴🏽‍♂️"] = "man_biking_tone3", + ["🚴🏾‍♂️"] = "man_biking_tone4", + ["🚴🏿‍♂️"] = "man_biking_tone5", + ["🏆"] = "trophy", + ["🥇"] = "first_place", + ["🥈"] = "second_place", + ["🥉"] = "third_place", + ["🏅"] = "medal", + ["🎖️"] = "military_medal", + ["🏵️"] = "rosette", + ["🎗️"] = "reminder_ribbon", + ["🎫"] = "ticket", + ["🎟️"] = "tickets", + ["🎪"] = "circus_tent", + ["🤹"] = "person_juggling", + ["🤹🏻"] = "person_juggling_tone1", + ["🤹🏼"] = "person_juggling_tone2", + ["🤹🏽"] = "person_juggling_tone3", + ["🤹🏾"] = "person_juggling_tone4", + ["🤹🏿"] = "person_juggling_tone5", + ["🤹‍♀️"] = "woman_juggling", + ["🤹🏻‍♀️"] = "woman_juggling_tone1", + ["🤹🏼‍♀️"] = "woman_juggling_tone2", + ["🤹🏽‍♀️"] = "woman_juggling_tone3", + ["🤹🏾‍♀️"] = "woman_juggling_tone4", + ["🤹🏿‍♀️"] = "woman_juggling_tone5", + ["🤹‍♂️"] = "man_juggling", + ["🤹🏻‍♂️"] = "man_juggling_tone1", + ["🤹🏼‍♂️"] = "man_juggling_tone2", + ["🤹🏽‍♂️"] = "man_juggling_tone3", + ["🤹🏾‍♂️"] = "man_juggling_tone4", + ["🤹🏿‍♂️"] = "man_juggling_tone5", + ["🎭"] = "performing_arts", + ["🩰"] = "ballet_shoes", + ["🎨"] = "art", + ["🎬"] = "clapper", + ["🎤"] = "microphone", + ["🎧"] = "headphones", + ["🎼"] = "musical_score", + ["🎹"] = "musical_keyboard", + ["🥁"] = "drum", + ["🪘"] = "long_drum", + ["🎷"] = "saxophone", + ["🎺"] = "trumpet", + ["🎸"] = "guitar", + ["🪕"] = "banjo", + ["🎻"] = "violin", + ["🪗"] = "accordion", + ["🎲"] = "game_die", + ["♟️"] = "chess_pawn", + ["🎯"] = "dart", + ["🎳"] = "bowling", + ["🎮"] = "video_game", + ["🎰"] = "slot_machine", + ["🧩"] = "jigsaw", + ["🚗"] = "red_car", + ["🚕"] = "taxi", + ["🚙"] = "blue_car", + ["🛻"] = "pickup_truck", + ["🚌"] = "bus", + ["🚎"] = "trolleybus", + ["🏎️"] = "race_car", + ["🚓"] = "police_car", + ["🚑"] = "ambulance", + ["🚒"] = "fire_engine", + ["🚐"] = "minibus", + ["🚚"] = "truck", + ["🚛"] = "articulated_lorry", + ["🚜"] = "tractor", + ["🦯"] = "probing_cane", + ["🦽"] = "manual_wheelchair", + ["🦼"] = "motorized_wheelchair", + ["🛴"] = "scooter", + ["🚲"] = "bike", + ["🛵"] = "motor_scooter", + ["🏍️"] = "motorcycle", + ["🛺"] = "auto_rickshaw", + ["🚨"] = "rotating_light", + ["🚔"] = "oncoming_police_car", + ["🚍"] = "oncoming_bus", + ["🚘"] = "oncoming_automobile", + ["🚖"] = "oncoming_taxi", + ["🚡"] = "aerial_tramway", + ["🚠"] = "mountain_cableway", + ["🚟"] = "suspension_railway", + ["🚃"] = "railway_car", + ["🚋"] = "train", + ["🚞"] = "mountain_railway", + ["🚝"] = "monorail", + ["🚄"] = "bullettrain_side", + ["🚅"] = "bullettrain_front", + ["🚈"] = "light_rail", + ["🚂"] = "steam_locomotive", + ["🚆"] = "train2", + ["🚇"] = "metro", + ["🚊"] = "tram", + ["🚉"] = "station", + ["✈️"] = "airplane", + ["🛫"] = "airplane_departure", + ["🛬"] = "airplane_arriving", + ["🛩️"] = "airplane_small", + ["💺"] = "seat", + ["🛰️"] = "satellite_orbital", + ["🚀"] = "rocket", + ["🛸"] = "flying_saucer", + ["🚁"] = "helicopter", + ["🛶"] = "canoe", + ["⛵"] = "sailboat", + ["🚤"] = "speedboat", + ["🛥️"] = "motorboat", + ["🛳️"] = "cruise_ship", + ["⛴️"] = "ferry", + ["🚢"] = "ship", + ["⚓"] = "anchor", + ["⛽"] = "fuelpump", + ["🚧"] = "construction", + ["🚦"] = "vertical_traffic_light", + ["🚥"] = "traffic_light", + ["🚏"] = "busstop", + ["🗺️"] = "map", + ["🗿"] = "moyai", + ["🗽"] = "statue_of_liberty", + ["🗼"] = "tokyo_tower", + ["🏰"] = "european_castle", + ["🏯"] = "japanese_castle", + ["🏟️"] = "stadium", + ["🎡"] = "ferris_wheel", + ["🎢"] = "roller_coaster", + ["🎠"] = "carousel_horse", + ["⛲"] = "fountain", + ["⛱️"] = "beach_umbrella", + ["🏖️"] = "beach", + ["🏝️"] = "island", + ["🏜️"] = "desert", + ["🌋"] = "volcano", + ["⛰️"] = "mountain", + ["🏔️"] = "mountain_snow", + ["🗻"] = "mount_fuji", + ["🏕️"] = "camping", + ["⛺"] = "tent", + ["🏠"] = "house", + ["🏡"] = "house_with_garden", + ["🏘️"] = "homes", + ["🏚️"] = "house_abandoned", + ["🛖"] = "hut", + ["🏗️"] = "construction_site", + ["🏭"] = "factory", + ["🏢"] = "office", + ["🏬"] = "department_store", + ["🏣"] = "post_office", + ["🏤"] = "european_post_office", + ["🏥"] = "hospital", + ["🏦"] = "bank", + ["🏨"] = "hotel", + ["🏪"] = "convenience_store", + ["🏫"] = "school", + ["🏩"] = "love_hotel", + ["💒"] = "wedding", + ["🏛️"] = "classical_building", + ["⛪"] = "church", + ["🕌"] = "mosque", + ["🕍"] = "synagogue", + ["🛕"] = "hindu_temple", + ["🕋"] = "kaaba", + ["⛩️"] = "shinto_shrine", + ["🛤️"] = "railway_track", + ["🛣️"] = "motorway", + ["🗾"] = "japan", + ["🎑"] = "rice_scene", + ["🏞️"] = "park", + ["🌅"] = "sunrise", + ["🌄"] = "sunrise_over_mountains", + ["🌠"] = "stars", + ["🎇"] = "sparkler", + ["🎆"] = "fireworks", + ["🌇"] = "city_sunset", + ["🌆"] = "city_dusk", + ["🏙️"] = "cityscape", + ["🌃"] = "night_with_stars", + ["🌌"] = "milky_way", + ["🌉"] = "bridge_at_night", + ["🌁"] = "foggy", + ["⌚"] = "watch", + ["📱"] = "mobile_phone", + ["📲"] = "calling", + ["💻"] = "computer", + ["⌨️"] = "keyboard", + ["🖥️"] = "desktop", + ["🖨️"] = "printer", + ["🖱️"] = "mouse_three_button", + ["🖲️"] = "trackball", + ["🕹️"] = "joystick", + ["🗜️"] = "compression", + ["💽"] = "minidisc", + ["💾"] = "floppy_disk", + ["💿"] = "cd", + ["📀"] = "dvd", + ["📼"] = "vhs", + ["📷"] = "camera", + ["📸"] = "camera_with_flash", + ["📹"] = "video_camera", + ["🎥"] = "movie_camera", + ["📽️"] = "projector", + ["🎞️"] = "film_frames", + ["📞"] = "telephone_receiver", + ["☎️"] = "telephone", + ["📟"] = "pager", + ["📠"] = "fax", + ["📺"] = "tv", + ["📻"] = "radio", + ["🎙️"] = "microphone2", + ["🎚️"] = "level_slider", + ["🎛️"] = "control_knobs", + ["🧭"] = "compass", + ["⏱️"] = "stopwatch", + ["⏲️"] = "timer", + ["⏰"] = "alarm_clock", + ["🕰️"] = "clock", + ["⌛"] = "hourglass", + ["⏳"] = "hourglass_flowing_sand", + ["📡"] = "satellite", + ["🔋"] = "battery", + ["🔌"] = "electric_plug", + ["💡"] = "bulb", + ["🔦"] = "flashlight", + ["🕯️"] = "candle", + ["🪔"] = "diya_lamp", + ["🧯"] = "fire_extinguisher", + ["🛢️"] = "oil", + ["💸"] = "money_with_wings", + ["💵"] = "dollar", + ["💴"] = "yen", + ["💶"] = "euro", + ["💷"] = "pound", + ["🪙"] = "coin", + ["💰"] = "moneybag", + ["💳"] = "credit_card", + ["💎"] = "gem", + ["⚖️"] = "scales", + ["🪜"] = "ladder", + ["🧰"] = "toolbox", + ["🪛"] = "screwdriver", + ["🔧"] = "wrench", + ["🔨"] = "hammer", + ["⚒️"] = "hammer_pick", + ["🛠️"] = "tools", + ["⛏️"] = "pick", + ["🔩"] = "nut_and_bolt", + ["⚙️"] = "gear", + ["🧱"] = "bricks", + ["⛓️"] = "chains", + ["🪝"] = "hook", + ["🪢"] = "knot", + ["🧲"] = "magnet", + ["🔫"] = "gun", + ["💣"] = "bomb", + ["🧨"] = "firecracker", + ["🪓"] = "axe", + ["🪚"] = "carpentry_saw", + ["🔪"] = "knife", + ["🗡️"] = "dagger", + ["⚔️"] = "crossed_swords", + ["🛡️"] = "shield", + ["🚬"] = "smoking", + ["⚰️"] = "coffin", + ["🪦"] = "headstone", + ["⚱️"] = "urn", + ["🏺"] = "amphora", + ["🪄"] = "magic_wand", + ["🔮"] = "crystal_ball", + ["📿"] = "prayer_beads", + ["🧿"] = "nazar_amulet", + ["💈"] = "barber", + ["⚗️"] = "alembic", + ["🔭"] = "telescope", + ["🔬"] = "microscope", + ["🕳️"] = "hole", + ["🪟"] = "window", + ["🩹"] = "adhesive_bandage", + ["🩺"] = "stethoscope", + ["💊"] = "pill", + ["💉"] = "syringe", + ["🩸"] = "drop_of_blood", + ["🧬"] = "dna", + ["🦠"] = "microbe", + ["🧫"] = "petri_dish", + ["🧪"] = "test_tube", + ["🌡️"] = "thermometer", + ["🪤"] = "mouse_trap", + ["🧹"] = "broom", + ["🧺"] = "basket", + ["🪡"] = "sewing_needle", + ["🧻"] = "roll_of_paper", + ["🚽"] = "toilet", + ["🪠"] = "plunger", + ["🪣"] = "bucket", + ["🚰"] = "potable_water", + ["🚿"] = "shower", + ["🛁"] = "bathtub", + ["🛀"] = "bath", + ["🛀🏻"] = "bath_tone1", + ["🛀🏼"] = "bath_tone2", + ["🛀🏽"] = "bath_tone3", + ["🛀🏾"] = "bath_tone4", + ["🛀🏿"] = "bath_tone5", + ["🪥"] = "toothbrush", + ["🧼"] = "soap", + ["🪒"] = "razor", + ["🧽"] = "sponge", + ["🧴"] = "squeeze_bottle", + ["🛎️"] = "bellhop", + ["🔑"] = "key", + ["🗝️"] = "key2", + ["🚪"] = "door", + ["🪑"] = "chair", + ["🪞"] = "mirror", + ["🛋️"] = "couch", + ["🛏️"] = "bed", + ["🛌"] = "sleeping_accommodation", + ["🛌🏻"] = "person_in_bed_tone1", + ["🛌🏼"] = "person_in_bed_tone2", + ["🛌🏽"] = "person_in_bed_tone3", + ["🛌🏾"] = "person_in_bed_tone4", + ["🛌🏿"] = "person_in_bed_tone5", + ["🧸"] = "teddy_bear", + ["🖼️"] = "frame_photo", + ["🛍️"] = "shopping_bags", + ["🛒"] = "shopping_cart", + ["🎁"] = "gift", + ["🎈"] = "balloon", + ["🎏"] = "flags", + ["🎀"] = "ribbon", + ["🎊"] = "confetti_ball", + ["🎉"] = "tada", + ["🪅"] = "piñata", + ["🪆"] = "nesting_dolls", + ["🎎"] = "dolls", + ["🏮"] = "izakaya_lantern", + ["🎐"] = "wind_chime", + ["🧧"] = "red_envelope", + ["✉️"] = "envelope", + ["📩"] = "envelope_with_arrow", + ["📨"] = "incoming_envelope", + ["📧"] = "e_mail", + ["💌"] = "love_letter", + ["📥"] = "inbox_tray", + ["📤"] = "outbox_tray", + ["📦"] = "package", + ["🏷️"] = "label", + ["📪"] = "mailbox_closed", + ["📫"] = "mailbox", + ["📬"] = "mailbox_with_mail", + ["📭"] = "mailbox_with_no_mail", + ["📮"] = "postbox", + ["📯"] = "postal_horn", + ["🪧"] = "placard", + ["📜"] = "scroll", + ["📃"] = "page_with_curl", + ["📄"] = "page_facing_up", + ["📑"] = "bookmark_tabs", + ["🧾"] = "receipt", + ["📊"] = "bar_chart", + ["📈"] = "chart_with_upwards_trend", + ["📉"] = "chart_with_downwards_trend", + ["🗒️"] = "notepad_spiral", + ["🗓️"] = "calendar_spiral", + ["📆"] = "calendar", + ["📅"] = "date", + ["🗑️"] = "wastebasket", + ["📇"] = "card_index", + ["🗃️"] = "card_box", + ["🗳️"] = "ballot_box", + ["🗄️"] = "file_cabinet", + ["📋"] = "clipboard", + ["📁"] = "file_folder", + ["📂"] = "open_file_folder", + ["🗂️"] = "dividers", + ["🗞️"] = "newspaper2", + ["📰"] = "newspaper", + ["📓"] = "notebook", + ["📔"] = "notebook_with_decorative_cover", + ["📒"] = "ledger", + ["📕"] = "closed_book", + ["📗"] = "green_book", + ["📘"] = "blue_book", + ["📙"] = "orange_book", + ["📚"] = "books", + ["📖"] = "book", + ["🔖"] = "bookmark", + ["🧷"] = "safety_pin", + ["🔗"] = "link", + ["📎"] = "paperclip", + ["🖇️"] = "paperclips", + ["📐"] = "triangular_ruler", + ["📏"] = "straight_ruler", + ["🧮"] = "abacus", + ["📌"] = "pushpin", + ["📍"] = "round_pushpin", + ["✂️"] = "scissors", + ["🖊️"] = "pen_ballpoint", + ["🖋️"] = "pen_fountain", + ["✒️"] = "black_nib", + ["🖌️"] = "paintbrush", + ["🖍️"] = "crayon", + ["📝"] = "pencil", + ["✏️"] = "pencil2", + ["🔍"] = "mag", + ["🔎"] = "mag_right", + ["🔏"] = "lock_with_ink_pen", + ["🔐"] = "closed_lock_with_key", + ["🔒"] = "lock", + ["🔓"] = "unlock", + ["❤️"] = "heart", + ["🧡"] = "orange_heart", + ["💛"] = "yellow_heart", + ["💚"] = "green_heart", + ["💙"] = "blue_heart", + ["💜"] = "purple_heart", + ["🖤"] = "black_heart", + ["🤎"] = "brown_heart", + ["🤍"] = "white_heart", + ["💔"] = "broken_heart", + ["❣️"] = "heart_exclamation", + ["💕"] = "two_hearts", + ["💞"] = "revolving_hearts", + ["💓"] = "heartbeat", + ["💗"] = "heartpulse", + ["💖"] = "sparkling_heart", + ["💘"] = "cupid", + ["💝"] = "gift_heart", + ["❤️‍🩹"] = "mending_heart", + ["❤️‍🔥"] = "heart_on_fire", + ["💟"] = "heart_decoration", + ["☮️"] = "peace", + ["✝️"] = "cross", + ["☪️"] = "star_and_crescent", + ["🕉️"] = "om_symbol", + ["☸️"] = "wheel_of_dharma", + ["✡️"] = "star_of_david", + ["🔯"] = "six_pointed_star", + ["🕎"] = "menorah", + ["☯️"] = "yin_yang", + ["☦️"] = "orthodox_cross", + ["🛐"] = "place_of_worship", + ["⛎"] = "ophiuchus", + ["♈"] = "aries", + ["♉"] = "taurus", + ["♊"] = "gemini", + ["♋"] = "cancer", + ["♌"] = "leo", + ["♍"] = "virgo", + ["♎"] = "libra", + ["♏"] = "scorpius", + ["♐"] = "sagittarius", + ["♑"] = "capricorn", + ["♒"] = "aquarius", + ["♓"] = "pisces", + ["🆔"] = "id", + ["⚛️"] = "atom", + ["🉑"] = "accept", + ["☢️"] = "radioactive", + ["☣️"] = "biohazard", + ["📴"] = "mobile_phone_off", + ["📳"] = "vibration_mode", + ["🈶"] = "u6709", + ["🈚"] = "u7121", + ["🈸"] = "u7533", + ["🈺"] = "u55b6", + ["🈷️"] = "u6708", + ["✴️"] = "eight_pointed_black_star", + ["🆚"] = "vs", + ["💮"] = "white_flower", + ["🉐"] = "ideograph_advantage", + ["㊙️"] = "secret", + ["㊗️"] = "congratulations", + ["🈴"] = "u5408", + ["🈵"] = "u6e80", + ["🈹"] = "u5272", + ["🈲"] = "u7981", + ["🅰️"] = "a", + ["🅱️"] = "b", + ["🆎"] = "ab", + ["🆑"] = "cl", + ["🅾️"] = "o2", + ["🆘"] = "sos", + ["❌"] = "x", + ["⭕"] = "o", + ["🛑"] = "octagonal_sign", + ["⛔"] = "no_entry", + ["📛"] = "name_badge", + ["🚫"] = "no_entry_sign", + ["💯"] = "100", + ["💢"] = "anger", + ["♨️"] = "hotsprings", + ["🚷"] = "no_pedestrians", + ["🚯"] = "do_not_litter", + ["🚳"] = "no_bicycles", + ["🚱"] = "non_potable_water", + ["🔞"] = "underage", + ["📵"] = "no_mobile_phones", + ["🚭"] = "no_smoking", + ["❗"] = "exclamation", + ["❕"] = "grey_exclamation", + ["❓"] = "question", + ["❔"] = "grey_question", + ["‼️"] = "bangbang", + ["⁉️"] = "interrobang", + ["🔅"] = "low_brightness", + ["🔆"] = "high_brightness", + ["〽️"] = "part_alternation_mark", + ["⚠️"] = "warning", + ["🚸"] = "children_crossing", + ["🔱"] = "trident", + ["⚜️"] = "fleur_de_lis", + ["🔰"] = "beginner", + ["♻️"] = "recycle", + ["✅"] = "white_check_mark", + ["🈯"] = "u6307", + ["💹"] = "chart", + ["❇️"] = "sparkle", + ["✳️"] = "eight_spoked_asterisk", + ["❎"] = "negative_squared_cross_mark", + ["🌐"] = "globe_with_meridians", + ["💠"] = "diamond_shape_with_a_dot_inside", + ["Ⓜ️"] = "m", + ["🌀"] = "cyclone", + ["💤"] = "zzz", + ["🏧"] = "atm", + ["🚾"] = "wc", + ["♿"] = "wheelchair", + ["🅿️"] = "parking", + ["🈳"] = "u7a7a", + ["🈂️"] = "sa", + ["🛂"] = "passport_control", + ["🛃"] = "customs", + ["🛄"] = "baggage_claim", + ["🛅"] = "left_luggage", + ["🛗"] = "elevator", + ["🚹"] = "mens", + ["🚺"] = "womens", + ["🚼"] = "baby_symbol", + ["🚻"] = "restroom", + ["🚮"] = "put_litter_in_its_place", + ["🎦"] = "cinema", + ["📶"] = "signal_strength", + ["🈁"] = "koko", + ["🔣"] = "symbols", + ["ℹ️"] = "information_source", + ["🔤"] = "abc", + ["🔡"] = "abcd", + ["🔠"] = "capital_abcd", + ["🆖"] = "ng", + ["🆗"] = "ok", + ["🆙"] = "up", + ["🆒"] = "cool", + ["🆕"] = "new", + ["🆓"] = "free", + ["0️⃣"] = "zero", + ["1️⃣"] = "one", + ["2️⃣"] = "two", + ["3️⃣"] = "three", + ["4️⃣"] = "four", + ["5️⃣"] = "five", + ["6️⃣"] = "six", + ["7️⃣"] = "seven", + ["8️⃣"] = "eight", + ["9️⃣"] = "nine", + ["🔟"] = "keycap_ten", + ["🔢"] = "1234", + ["#️⃣"] = "hash", + ["*️⃣"] = "asterisk", + ["⏏️"] = "eject", + ["▶️"] = "arrow_forward", + ["⏸️"] = "pause_button", + ["⏯️"] = "play_pause", + ["⏹️"] = "stop_button", + ["⏺️"] = "record_button", + ["⏭️"] = "track_next", + ["⏮️"] = "track_previous", + ["⏩"] = "fast_forward", + ["⏪"] = "rewind", + ["⏫"] = "arrow_double_up", + ["⏬"] = "arrow_double_down", + ["◀️"] = "arrow_backward", + ["🔼"] = "arrow_up_small", + ["🔽"] = "arrow_down_small", + ["➡️"] = "arrow_right", + ["⬅️"] = "arrow_left", + ["⬆️"] = "arrow_up", + ["⬇️"] = "arrow_down", + ["↗️"] = "arrow_upper_right", + ["↘️"] = "arrow_lower_right", + ["↙️"] = "arrow_lower_left", + ["↖️"] = "arrow_upper_left", + ["↕️"] = "arrow_up_down", + ["↔️"] = "left_right_arrow", + ["↪️"] = "arrow_right_hook", + ["↩️"] = "leftwards_arrow_with_hook", + ["⤴️"] = "arrow_heading_up", + ["⤵️"] = "arrow_heading_down", + ["🔀"] = "twisted_rightwards_arrows", + ["🔁"] = "repeat", + ["🔂"] = "repeat_one", + ["🔄"] = "arrows_counterclockwise", + ["🔃"] = "arrows_clockwise", + ["🎵"] = "musical_note", + ["🎶"] = "notes", + ["➕"] = "heavy_plus_sign", + ["➖"] = "heavy_minus_sign", + ["➗"] = "heavy_division_sign", + ["✖️"] = "heavy_multiplication_x", + ["♾️"] = "infinity", + ["💲"] = "heavy_dollar_sign", + ["💱"] = "currency_exchange", + ["™️"] = "tm", + ["©️"] = "copyright", + ["®️"] = "registered", + ["〰️"] = "wavy_dash", + ["➰"] = "curly_loop", + ["➿"] = "loop", + ["🔚"] = "end", + ["🔙"] = "back", + ["🔛"] = "on", + ["🔝"] = "top", + ["🔜"] = "soon", + ["✔️"] = "heavy_check_mark", + ["☑️"] = "ballot_box_with_check", + ["🔘"] = "radio_button", + ["⚪"] = "white_circle", + ["⚫"] = "black_circle", + ["🔴"] = "red_circle", + ["🔵"] = "blue_circle", + ["🟤"] = "brown_circle", + ["🟣"] = "purple_circle", + ["🟢"] = "green_circle", + ["🟡"] = "yellow_circle", + ["🟠"] = "orange_circle", + ["🔺"] = "small_red_triangle", + ["🔻"] = "small_red_triangle_down", + ["🔸"] = "small_orange_diamond", + ["🔹"] = "small_blue_diamond", + ["🔶"] = "large_orange_diamond", + ["🔷"] = "large_blue_diamond", + ["🔳"] = "white_square_button", + ["🔲"] = "black_square_button", + ["▪️"] = "black_small_square", + ["▫️"] = "white_small_square", + ["◾"] = "black_medium_small_square", + ["◽"] = "white_medium_small_square", + ["◼️"] = "black_medium_square", + ["◻️"] = "white_medium_square", + ["⬛"] = "black_large_square", + ["⬜"] = "white_large_square", + ["🟧"] = "orange_square", + ["🟦"] = "blue_square", + ["🟥"] = "red_square", + ["🟫"] = "brown_square", + ["🟪"] = "purple_square", + ["🟩"] = "green_square", + ["🟨"] = "yellow_square", + ["🔈"] = "speaker", + ["🔇"] = "mute", + ["🔉"] = "sound", + ["🔊"] = "loud_sound", + ["🔔"] = "bell", + ["🔕"] = "no_bell", + ["📣"] = "mega", + ["📢"] = "loudspeaker", + ["🗨️"] = "speech_left", + ["👁‍🗨"] = "eye_in_speech_bubble", + ["💬"] = "speech_balloon", + ["💭"] = "thought_balloon", + ["🗯️"] = "anger_right", + ["♠️"] = "spades", + ["♣️"] = "clubs", + ["♥️"] = "hearts", + ["♦️"] = "diamonds", + ["🃏"] = "black_joker", + ["🎴"] = "flower_playing_cards", + ["🀄"] = "mahjong", + ["🕐"] = "clock1", + ["🕑"] = "clock2", + ["🕒"] = "clock3", + ["🕓"] = "clock4", + ["🕔"] = "clock5", + ["🕕"] = "clock6", + ["🕖"] = "clock7", + ["🕗"] = "clock8", + ["🕘"] = "clock9", + ["🕙"] = "clock10", + ["🕚"] = "clock11", + ["🕛"] = "clock12", + ["🕜"] = "clock130", + ["🕝"] = "clock230", + ["🕞"] = "clock330", + ["🕟"] = "clock430", + ["🕠"] = "clock530", + ["🕡"] = "clock630", + ["🕢"] = "clock730", + ["🕣"] = "clock830", + ["🕤"] = "clock930", + ["🕥"] = "clock1030", + ["🕦"] = "clock1130", + ["🕧"] = "clock1230", + ["♀️"] = "female_sign", + ["♂️"] = "male_sign", + ["⚧"] = "transgender_symbol", + ["⚕️"] = "medical_symbol", + ["🇿"] = "regional_indicator_z", + ["🇾"] = "regional_indicator_y", + ["🇽"] = "regional_indicator_x", + ["🇼"] = "regional_indicator_w", + ["🇻"] = "regional_indicator_v", + ["🇺"] = "regional_indicator_u", + ["🇹"] = "regional_indicator_t", + ["🇸"] = "regional_indicator_s", + ["🇷"] = "regional_indicator_r", + ["🇶"] = "regional_indicator_q", + ["🇵"] = "regional_indicator_p", + ["🇴"] = "regional_indicator_o", + ["🇳"] = "regional_indicator_n", + ["🇲"] = "regional_indicator_m", + ["🇱"] = "regional_indicator_l", + ["🇰"] = "regional_indicator_k", + ["🇯"] = "regional_indicator_j", + ["🇮"] = "regional_indicator_i", + ["🇭"] = "regional_indicator_h", + ["🇬"] = "regional_indicator_g", + ["🇫"] = "regional_indicator_f", + ["🇪"] = "regional_indicator_e", + ["🇩"] = "regional_indicator_d", + ["🇨"] = "regional_indicator_c", + ["🇧"] = "regional_indicator_b", + ["🇦"] = "regional_indicator_a", + ["🏳️"] = "flag_white", + ["🏴"] = "flag_black", + ["🏁"] = "checkered_flag", + ["🚩"] = "triangular_flag_on_post", + ["🏳️‍🌈"] = "rainbow_flag", + ["🏳️‍⚧️"] = "transgender_flag", + ["🏴‍☠️"] = "pirate_flag", + ["🇦🇫"] = "flag_af", + ["🇦🇽"] = "flag_ax", + ["🇦🇱"] = "flag_al", + ["🇩🇿"] = "flag_dz", + ["🇦🇸"] = "flag_as", + ["🇦🇩"] = "flag_ad", + ["🇦🇴"] = "flag_ao", + ["🇦🇮"] = "flag_ai", + ["🇦🇶"] = "flag_aq", + ["🇦🇬"] = "flag_ag", + ["🇦🇷"] = "flag_ar", + ["🇦🇲"] = "flag_am", + ["🇦🇼"] = "flag_aw", + ["🇦🇺"] = "flag_au", + ["🇦🇹"] = "flag_at", + ["🇦🇿"] = "flag_az", + ["🇧🇸"] = "flag_bs", + ["🇧🇭"] = "flag_bh", + ["🇧🇩"] = "flag_bd", + ["🇧🇧"] = "flag_bb", + ["🇧🇾"] = "flag_by", + ["🇧🇪"] = "flag_be", + ["🇧🇿"] = "flag_bz", + ["🇧🇯"] = "flag_bj", + ["🇧🇲"] = "flag_bm", + ["🇧🇹"] = "flag_bt", + ["🇧🇴"] = "flag_bo", + ["🇧🇦"] = "flag_ba", + ["🇧🇼"] = "flag_bw", + ["🇧🇷"] = "flag_br", + ["🇮🇴"] = "flag_io", + ["🇻🇬"] = "flag_vg", + ["🇧🇳"] = "flag_bn", + ["🇧🇬"] = "flag_bg", + ["🇧🇫"] = "flag_bf", + ["🇧🇮"] = "flag_bi", + ["🇰🇭"] = "flag_kh", + ["🇨🇲"] = "flag_cm", + ["🇨🇦"] = "flag_ca", + ["🇮🇨"] = "flag_ic", + ["🇨🇻"] = "flag_cv", + ["🇧🇶"] = "flag_bq", + ["🇰🇾"] = "flag_ky", + ["🇨🇫"] = "flag_cf", + ["🇹🇩"] = "flag_td", + ["🇨🇱"] = "flag_cl", + ["🇨🇳"] = "flag_cn", + ["🇨🇽"] = "flag_cx", + ["🇨🇨"] = "flag_cc", + ["🇨🇴"] = "flag_co", + ["🇰🇲"] = "flag_km", + ["🇨🇬"] = "flag_cg", + ["🇨🇩"] = "flag_cd", + ["🇨🇰"] = "flag_ck", + ["🇨🇷"] = "flag_cr", + ["🇨🇮"] = "flag_ci", + ["🇭🇷"] = "flag_hr", + ["🇨🇺"] = "flag_cu", + ["🇨🇼"] = "flag_cw", + ["🇨🇾"] = "flag_cy", + ["🇨🇿"] = "flag_cz", + ["🇩🇰"] = "flag_dk", + ["🇩🇯"] = "flag_dj", + ["🇩🇲"] = "flag_dm", + ["🇩🇴"] = "flag_do", + ["🇪🇨"] = "flag_ec", + ["🇪🇬"] = "flag_eg", + ["🇸🇻"] = "flag_sv", + ["🇬🇶"] = "flag_gq", + ["🇪🇷"] = "flag_er", + ["🇪🇪"] = "flag_ee", + ["🇪🇹"] = "flag_et", + ["🇪🇺"] = "flag_eu", + ["🇫🇰"] = "flag_fk", + ["🇫🇴"] = "flag_fo", + ["🇫🇯"] = "flag_fj", + ["🇫🇮"] = "flag_fi", + ["🇫🇷"] = "flag_fr", + ["🇬🇫"] = "flag_gf", + ["🇵🇫"] = "flag_pf", + ["🇹🇫"] = "flag_tf", + ["🇬🇦"] = "flag_ga", + ["🇬🇲"] = "flag_gm", + ["🇬🇪"] = "flag_ge", + ["🇩🇪"] = "flag_de", + ["🇬🇭"] = "flag_gh", + ["🇬🇮"] = "flag_gi", + ["🇬🇷"] = "flag_gr", + ["🇬🇱"] = "flag_gl", + ["🇬🇩"] = "flag_gd", + ["🇬🇵"] = "flag_gp", + ["🇬🇺"] = "flag_gu", + ["🇬🇹"] = "flag_gt", + ["🇬🇬"] = "flag_gg", + ["🇬🇳"] = "flag_gn", + ["🇬🇼"] = "flag_gw", + ["🇬🇾"] = "flag_gy", + ["🇭🇹"] = "flag_ht", + ["🇭🇳"] = "flag_hn", + ["🇭🇰"] = "flag_hk", + ["🇭🇺"] = "flag_hu", + ["🇮🇸"] = "flag_is", + ["🇮🇳"] = "flag_in", + ["🇮🇩"] = "flag_id", + ["🇮🇷"] = "flag_ir", + ["🇮🇶"] = "flag_iq", + ["🇮🇪"] = "flag_ie", + ["🇮🇲"] = "flag_im", + ["🇮🇱"] = "flag_il", + ["🇮🇹"] = "flag_it", + ["🇯🇲"] = "flag_jm", + ["🇯🇵"] = "flag_jp", + ["🎌"] = "crossed_flags", + ["🇯🇪"] = "flag_je", + ["🇯🇴"] = "flag_jo", + ["🇰🇿"] = "flag_kz", + ["🇰🇪"] = "flag_ke", + ["🇰🇮"] = "flag_ki", + ["🇽🇰"] = "flag_xk", + ["🇰🇼"] = "flag_kw", + ["🇰🇬"] = "flag_kg", + ["🇱🇦"] = "flag_la", + ["🇱🇻"] = "flag_lv", + ["🇱🇧"] = "flag_lb", + ["🇱🇸"] = "flag_ls", + ["🇱🇷"] = "flag_lr", + ["🇱🇾"] = "flag_ly", + ["🇱🇮"] = "flag_li", + ["🇱🇹"] = "flag_lt", + ["🇱🇺"] = "flag_lu", + ["🇲🇴"] = "flag_mo", + ["🇲🇰"] = "flag_mk", + ["🇲🇬"] = "flag_mg", + ["🇲🇼"] = "flag_mw", + ["🇲🇾"] = "flag_my", + ["🇲🇻"] = "flag_mv", + ["🇲🇱"] = "flag_ml", + ["🇲🇹"] = "flag_mt", + ["🇲🇭"] = "flag_mh", + ["🇲🇶"] = "flag_mq", + ["🇲🇷"] = "flag_mr", + ["🇲🇺"] = "flag_mu", + ["🇾🇹"] = "flag_yt", + ["🇲🇽"] = "flag_mx", + ["🇫🇲"] = "flag_fm", + ["🇲🇩"] = "flag_md", + ["🇲🇨"] = "flag_mc", + ["🇲🇳"] = "flag_mn", + ["🇲🇪"] = "flag_me", + ["🇲🇸"] = "flag_ms", + ["🇲🇦"] = "flag_ma", + ["🇲🇿"] = "flag_mz", + ["🇲🇲"] = "flag_mm", + ["🇳🇦"] = "flag_na", + ["🇳🇷"] = "flag_nr", + ["🇳🇵"] = "flag_np", + ["🇳🇱"] = "flag_nl", + ["🇳🇨"] = "flag_nc", + ["🇳🇿"] = "flag_nz", + ["🇳🇮"] = "flag_ni", + ["🇳🇪"] = "flag_ne", + ["🇳🇬"] = "flag_ng", + ["🇳🇺"] = "flag_nu", + ["🇳🇫"] = "flag_nf", + ["🇰🇵"] = "flag_kp", + ["🇲🇵"] = "flag_mp", + ["🇳🇴"] = "flag_no", + ["🇴🇲"] = "flag_om", + ["🇵🇰"] = "flag_pk", + ["🇵🇼"] = "flag_pw", + ["🇵🇸"] = "flag_ps", + ["🇵🇦"] = "flag_pa", + ["🇵🇬"] = "flag_pg", + ["🇵🇾"] = "flag_py", + ["🇵🇪"] = "flag_pe", + ["🇵🇭"] = "flag_ph", + ["🇵🇳"] = "flag_pn", + ["🇵🇱"] = "flag_pl", + ["🇵🇹"] = "flag_pt", + ["🇵🇷"] = "flag_pr", + ["🇶🇦"] = "flag_qa", + ["🇷🇪"] = "flag_re", + ["🇷🇴"] = "flag_ro", + ["🇷🇺"] = "flag_ru", + ["🇷🇼"] = "flag_rw", + ["🇼🇸"] = "flag_ws", + ["🇸🇲"] = "flag_sm", + ["🇸🇹"] = "flag_st", + ["🇸🇦"] = "flag_sa", + ["🇸🇳"] = "flag_sn", + ["🇷🇸"] = "flag_rs", + ["🇸🇨"] = "flag_sc", + ["🇸🇱"] = "flag_sl", + ["🇸🇬"] = "flag_sg", + ["🇸🇽"] = "flag_sx", + ["🇸🇰"] = "flag_sk", + ["🇸🇮"] = "flag_si", + ["🇬🇸"] = "flag_gs", + ["🇸🇧"] = "flag_sb", + ["🇸🇴"] = "flag_so", + ["🇿🇦"] = "flag_za", + ["🇰🇷"] = "flag_kr", + ["🇸🇸"] = "flag_ss", + ["🇪🇸"] = "flag_es", + ["🇱🇰"] = "flag_lk", + ["🇧🇱"] = "flag_bl", + ["🇸🇭"] = "flag_sh", + ["🇰🇳"] = "flag_kn", + ["🇱🇨"] = "flag_lc", + ["🇵🇲"] = "flag_pm", + ["🇻🇨"] = "flag_vc", + ["🇸🇩"] = "flag_sd", + ["🇸🇷"] = "flag_sr", + ["🇸🇿"] = "flag_sz", + ["🇸🇪"] = "flag_se", + ["🇨🇭"] = "flag_ch", + ["🇸🇾"] = "flag_sy", + ["🇹🇼"] = "flag_tw", + ["🇹🇯"] = "flag_tj", + ["🇹🇿"] = "flag_tz", + ["🇹🇭"] = "flag_th", + ["🇹🇱"] = "flag_tl", + ["🇹🇬"] = "flag_tg", + ["🇹🇰"] = "flag_tk", + ["🇹🇴"] = "flag_to", + ["🇹🇹"] = "flag_tt", + ["🇹🇳"] = "flag_tn", + ["🇹🇷"] = "flag_tr", + ["🇹🇲"] = "flag_tm", + ["🇹🇨"] = "flag_tc", + ["🇻🇮"] = "flag_vi", + ["🇹🇻"] = "flag_tv", + ["🇺🇬"] = "flag_ug", + ["🇺🇦"] = "flag_ua", + ["🇦🇪"] = "flag_ae", + ["🇬🇧"] = "flag_gb", + ["🏴󠁧󠁢󠁥󠁮󠁧󠁿"] = "england", + ["🏴󠁧󠁢󠁳󠁣󠁴󠁿"] = "scotland", + ["🏴󠁧󠁢󠁷󠁬󠁳󠁿"] = "wales", + ["🇺🇸"] = "flag_us", + ["🇺🇾"] = "flag_uy", + ["🇺🇿"] = "flag_uz", + ["🇻🇺"] = "flag_vu", + ["🇻🇦"] = "flag_va", + ["🇻🇪"] = "flag_ve", + ["🇻🇳"] = "flag_vn", + ["🇼🇫"] = "flag_wf", + ["🇪🇭"] = "flag_eh", + ["🇾🇪"] = "flag_ye", + ["🇿🇲"] = "flag_zm", + ["🇿🇼"] = "flag_zw", + ["🇦🇨"] = "flag_ac", + ["🇧🇻"] = "flag_bv", + ["🇨🇵"] = "flag_cp", + ["🇪🇦"] = "flag_ea", + ["🇩🇬"] = "flag_dg", + ["🇭🇲"] = "flag_hm", + ["🇲🇫"] = "flag_mf", + ["🇸🇯"] = "flag_sj", + ["🇹🇦"] = "flag_ta", + ["🇺🇲"] = "flag_um", + ["🇺🇳"] = "united_nations", + }; - private static Dictionary _fromCodes = - new(5000, StringComparer.Ordinal) - { - ["grinning"] = "😀", - ["smiley"] = "😃", - ["smile"] = "😄", - ["grin"] = "😁", - ["laughing"] = "😆", - ["satisfied"] = "😆", - ["sweat_smile"] = "😅", - ["joy"] = "😂", - ["rofl"] = "🤣", - ["rolling_on_the_floor_laughing"] = "🤣", - ["relaxed"] = "☺️", - ["blush"] = "😊", - ["innocent"] = "😇", - ["slight_smile"] = "🙂", - ["slightly_smiling_face"] = "🙂", - ["upside_down"] = "🙃", - ["upside_down_face"] = "🙃", - ["wink"] = "😉", - ["relieved"] = "😌", - ["smiling_face_with_tear"] = "🥲", - ["heart_eyes"] = "😍", - ["smiling_face_with_3_hearts"] = "🥰", - ["kissing_heart"] = "😘", - ["kissing"] = "😗", - ["kissing_smiling_eyes"] = "😙", - ["kissing_closed_eyes"] = "😚", - ["yum"] = "😋", - ["stuck_out_tongue"] = "😛", - ["stuck_out_tongue_closed_eyes"] = "😝", - ["stuck_out_tongue_winking_eye"] = "😜", - ["zany_face"] = "🤪", - ["face_with_raised_eyebrow"] = "🤨", - ["face_with_monocle"] = "🧐", - ["nerd"] = "🤓", - ["nerd_face"] = "🤓", - ["sunglasses"] = "😎", - ["star_struck"] = "🤩", - ["partying_face"] = "🥳", - ["smirk"] = "😏", - ["unamused"] = "😒", - ["disappointed"] = "😞", - ["pensive"] = "😔", - ["worried"] = "😟", - ["confused"] = "😕", - ["slight_frown"] = "🙁", - ["slightly_frowning_face"] = "🙁", - ["frowning2"] = "☹️", - ["white_frowning_face"] = "☹️", - ["persevere"] = "😣", - ["confounded"] = "😖", - ["tired_face"] = "😫", - ["weary"] = "😩", - ["pleading_face"] = "🥺", - ["cry"] = "😢", - ["sob"] = "😭", - ["triumph"] = "😤", - ["face_exhaling"] = "😮‍💨", - ["angry"] = "😠", - ["rage"] = "😡", - ["face_with_symbols_over_mouth"] = "🤬", - ["exploding_head"] = "🤯", - ["flushed"] = "😳", - ["face_in_clouds"] = "😶‍🌫️", - ["hot_face"] = "🥵", - ["cold_face"] = "🥶", - ["scream"] = "😱", - ["fearful"] = "😨", - ["cold_sweat"] = "😰", - ["disappointed_relieved"] = "😥", - ["sweat"] = "😓", - ["hugging"] = "🤗", - ["hugging_face"] = "🤗", - ["thinking"] = "🤔", - ["thinking_face"] = "🤔", - ["face_with_hand_over_mouth"] = "🤭", - ["yawning_face"] = "🥱", - ["shushing_face"] = "🤫", - ["lying_face"] = "🤥", - ["liar"] = "🤥", - ["no_mouth"] = "😶", - ["neutral_face"] = "😐", - ["expressionless"] = "😑", - ["grimacing"] = "😬", - ["rolling_eyes"] = "🙄", - ["face_with_rolling_eyes"] = "🙄", - ["hushed"] = "😯", - ["frowning"] = "😦", - ["anguished"] = "😧", - ["open_mouth"] = "😮", - ["astonished"] = "😲", - ["sleeping"] = "😴", - ["drooling_face"] = "🤤", - ["drool"] = "🤤", - ["sleepy"] = "😪", - ["dizzy_face"] = "😵", - ["face_with_spiral_eyes"] = "😵‍💫", - ["zipper_mouth"] = "🤐", - ["zipper_mouth_face"] = "🤐", - ["woozy_face"] = "🥴", - ["nauseated_face"] = "🤢", - ["sick"] = "🤢", - ["face_vomiting"] = "🤮", - ["sneezing_face"] = "🤧", - ["sneeze"] = "🤧", - ["mask"] = "😷", - ["thermometer_face"] = "🤒", - ["face_with_thermometer"] = "🤒", - ["head_bandage"] = "🤕", - ["face_with_head_bandage"] = "🤕", - ["money_mouth"] = "🤑", - ["money_mouth_face"] = "🤑", - ["cowboy"] = "🤠", - ["face_with_cowboy_hat"] = "🤠", - ["disguised_face"] = "🥸", - ["smiling_imp"] = "😈", - ["imp"] = "👿", - ["japanese_ogre"] = "👹", - ["japanese_goblin"] = "👺", - ["clown"] = "🤡", - ["clown_face"] = "🤡", - ["poop"] = "💩", - ["shit"] = "💩", - ["hankey"] = "💩", - ["poo"] = "💩", - ["ghost"] = "👻", - ["skull"] = "💀", - ["skeleton"] = "💀", - ["skull_crossbones"] = "☠️", - ["skull_and_crossbones"] = "☠️", - ["alien"] = "👽", - ["space_invader"] = "👾", - ["robot"] = "🤖", - ["robot_face"] = "🤖", - ["jack_o_lantern"] = "🎃", - ["smiley_cat"] = "😺", - ["smile_cat"] = "😸", - ["joy_cat"] = "😹", - ["heart_eyes_cat"] = "😻", - ["smirk_cat"] = "😼", - ["kissing_cat"] = "😽", - ["scream_cat"] = "🙀", - ["crying_cat_face"] = "😿", - ["pouting_cat"] = "😾", - ["palms_up_together"] = "🤲", - ["palms_up_together_tone1"] = "🤲🏻", - ["palms_up_together_light_skin_tone"] = "🤲🏻", - ["palms_up_together_tone2"] = "🤲🏼", - ["palms_up_together_medium_light_skin_tone"] = "🤲🏼", - ["palms_up_together_tone3"] = "🤲🏽", - ["palms_up_together_medium_skin_tone"] = "🤲🏽", - ["palms_up_together_tone4"] = "🤲🏾", - ["palms_up_together_medium_dark_skin_tone"] = "🤲🏾", - ["palms_up_together_tone5"] = "🤲🏿", - ["palms_up_together_dark_skin_tone"] = "🤲🏿", - ["open_hands"] = "👐", - ["open_hands_tone1"] = "👐🏻", - ["open_hands_tone2"] = "👐🏼", - ["open_hands_tone3"] = "👐🏽", - ["open_hands_tone4"] = "👐🏾", - ["open_hands_tone5"] = "👐🏿", - ["raised_hands"] = "🙌", - ["raised_hands_tone1"] = "🙌🏻", - ["raised_hands_tone2"] = "🙌🏼", - ["raised_hands_tone3"] = "🙌🏽", - ["raised_hands_tone4"] = "🙌🏾", - ["raised_hands_tone5"] = "🙌🏿", - ["clap"] = "👏", - ["clap_tone1"] = "👏🏻", - ["clap_tone2"] = "👏🏼", - ["clap_tone3"] = "👏🏽", - ["clap_tone4"] = "👏🏾", - ["clap_tone5"] = "👏🏿", - ["handshake"] = "🤝", - ["shaking_hands"] = "🤝", - ["thumbsup"] = "👍", - ["+1"] = "👍", - ["thumbup"] = "👍", - ["thumbsup_tone1"] = "👍🏻", - ["+1_tone1"] = "👍🏻", - ["thumbup_tone1"] = "👍🏻", - ["thumbsup_tone2"] = "👍🏼", - ["+1_tone2"] = "👍🏼", - ["thumbup_tone2"] = "👍🏼", - ["thumbsup_tone3"] = "👍🏽", - ["+1_tone3"] = "👍🏽", - ["thumbup_tone3"] = "👍🏽", - ["thumbsup_tone4"] = "👍🏾", - ["+1_tone4"] = "👍🏾", - ["thumbup_tone4"] = "👍🏾", - ["thumbsup_tone5"] = "👍🏿", - ["+1_tone5"] = "👍🏿", - ["thumbup_tone5"] = "👍🏿", - ["thumbsdown"] = "👎", - ["-1"] = "👎", - ["thumbdown"] = "👎", - ["thumbsdown_tone1"] = "👎🏻", - ["_1_tone1"] = "👎🏻", - ["thumbdown_tone1"] = "👎🏻", - ["thumbsdown_tone2"] = "👎🏼", - ["_1_tone2"] = "👎🏼", - ["thumbdown_tone2"] = "👎🏼", - ["thumbsdown_tone3"] = "👎🏽", - ["_1_tone3"] = "👎🏽", - ["thumbdown_tone3"] = "👎🏽", - ["thumbsdown_tone4"] = "👎🏾", - ["_1_tone4"] = "👎🏾", - ["thumbdown_tone4"] = "👎🏾", - ["thumbsdown_tone5"] = "👎🏿", - ["_1_tone5"] = "👎🏿", - ["thumbdown_tone5"] = "👎🏿", - ["punch"] = "👊", - ["punch_tone1"] = "👊🏻", - ["punch_tone2"] = "👊🏼", - ["punch_tone3"] = "👊🏽", - ["punch_tone4"] = "👊🏾", - ["punch_tone5"] = "👊🏿", - ["fist"] = "✊", - ["fist_tone1"] = "✊🏻", - ["fist_tone2"] = "✊🏼", - ["fist_tone3"] = "✊🏽", - ["fist_tone4"] = "✊🏾", - ["fist_tone5"] = "✊🏿", - ["left_facing_fist"] = "🤛", - ["left_fist"] = "🤛", - ["left_facing_fist_tone1"] = "🤛🏻", - ["left_fist_tone1"] = "🤛🏻", - ["left_facing_fist_tone2"] = "🤛🏼", - ["left_fist_tone2"] = "🤛🏼", - ["left_facing_fist_tone3"] = "🤛🏽", - ["left_fist_tone3"] = "🤛🏽", - ["left_facing_fist_tone4"] = "🤛🏾", - ["left_fist_tone4"] = "🤛🏾", - ["left_facing_fist_tone5"] = "🤛🏿", - ["left_fist_tone5"] = "🤛🏿", - ["right_facing_fist"] = "🤜", - ["right_fist"] = "🤜", - ["right_facing_fist_tone1"] = "🤜🏻", - ["right_fist_tone1"] = "🤜🏻", - ["right_facing_fist_tone2"] = "🤜🏼", - ["right_fist_tone2"] = "🤜🏼", - ["right_facing_fist_tone3"] = "🤜🏽", - ["right_fist_tone3"] = "🤜🏽", - ["right_facing_fist_tone4"] = "🤜🏾", - ["right_fist_tone4"] = "🤜🏾", - ["right_facing_fist_tone5"] = "🤜🏿", - ["right_fist_tone5"] = "🤜🏿", - ["fingers_crossed"] = "🤞", - ["hand_with_index_and_middle_finger_crossed"] = "🤞", - ["fingers_crossed_tone1"] = "🤞🏻", - ["hand_with_index_and_middle_fingers_crossed_tone1"] = "🤞🏻", - ["fingers_crossed_tone2"] = "🤞🏼", - ["hand_with_index_and_middle_fingers_crossed_tone2"] = "🤞🏼", - ["fingers_crossed_tone3"] = "🤞🏽", - ["hand_with_index_and_middle_fingers_crossed_tone3"] = "🤞🏽", - ["fingers_crossed_tone4"] = "🤞🏾", - ["hand_with_index_and_middle_fingers_crossed_tone4"] = "🤞🏾", - ["fingers_crossed_tone5"] = "🤞🏿", - ["hand_with_index_and_middle_fingers_crossed_tone5"] = "🤞🏿", - ["v"] = "✌️", - ["v_tone1"] = "✌🏻", - ["v_tone2"] = "✌🏼", - ["v_tone3"] = "✌🏽", - ["v_tone4"] = "✌🏾", - ["v_tone5"] = "✌🏿", - ["love_you_gesture"] = "🤟", - ["love_you_gesture_tone1"] = "🤟🏻", - ["love_you_gesture_light_skin_tone"] = "🤟🏻", - ["love_you_gesture_tone2"] = "🤟🏼", - ["love_you_gesture_medium_light_skin_tone"] = "🤟🏼", - ["love_you_gesture_tone3"] = "🤟🏽", - ["love_you_gesture_medium_skin_tone"] = "🤟🏽", - ["love_you_gesture_tone4"] = "🤟🏾", - ["love_you_gesture_medium_dark_skin_tone"] = "🤟🏾", - ["love_you_gesture_tone5"] = "🤟🏿", - ["love_you_gesture_dark_skin_tone"] = "🤟🏿", - ["metal"] = "🤘", - ["sign_of_the_horns"] = "🤘", - ["metal_tone1"] = "🤘🏻", - ["sign_of_the_horns_tone1"] = "🤘🏻", - ["metal_tone2"] = "🤘🏼", - ["sign_of_the_horns_tone2"] = "🤘🏼", - ["metal_tone3"] = "🤘🏽", - ["sign_of_the_horns_tone3"] = "🤘🏽", - ["metal_tone4"] = "🤘🏾", - ["sign_of_the_horns_tone4"] = "🤘🏾", - ["metal_tone5"] = "🤘🏿", - ["sign_of_the_horns_tone5"] = "🤘🏿", - ["ok_hand"] = "👌", - ["ok_hand_tone1"] = "👌🏻", - ["ok_hand_tone2"] = "👌🏼", - ["ok_hand_tone3"] = "👌🏽", - ["ok_hand_tone4"] = "👌🏾", - ["ok_hand_tone5"] = "👌🏿", - ["pinching_hand"] = "🤏", - ["pinching_hand_tone1"] = "🤏🏻", - ["pinching_hand_light_skin_tone"] = "🤏🏻", - ["pinching_hand_tone2"] = "🤏🏼", - ["pinching_hand_medium_light_skin_tone"] = "🤏🏼", - ["pinching_hand_tone3"] = "🤏🏽", - ["pinching_hand_medium_skin_tone"] = "🤏🏽", - ["pinching_hand_tone4"] = "🤏🏾", - ["pinching_hand_medium_dark_skin_tone"] = "🤏🏾", - ["pinching_hand_tone5"] = "🤏🏿", - ["pinching_hand_dark_skin_tone"] = "🤏🏿", - ["pinched_fingers"] = "🤌", - ["pinched_fingers_tone2"] = "🤌🏼", - ["pinched_fingers_medium_light_skin_tone"] = "🤌🏼", - ["pinched_fingers_tone1"] = "🤌🏻", - ["pinched_fingers_light_skin_tone"] = "🤌🏻", - ["pinched_fingers_tone3"] = "🤌🏽", - ["pinched_fingers_medium_skin_tone"] = "🤌🏽", - ["pinched_fingers_tone4"] = "🤌🏾", - ["pinched_fingers_medium_dark_skin_tone"] = "🤌🏾", - ["pinched_fingers_tone5"] = "🤌🏿", - ["pinched_fingers_dark_skin_tone"] = "🤌🏿", - ["point_left"] = "👈", - ["point_left_tone1"] = "👈🏻", - ["point_left_tone2"] = "👈🏼", - ["point_left_tone3"] = "👈🏽", - ["point_left_tone4"] = "👈🏾", - ["point_left_tone5"] = "👈🏿", - ["point_right"] = "👉", - ["point_right_tone1"] = "👉🏻", - ["point_right_tone2"] = "👉🏼", - ["point_right_tone3"] = "👉🏽", - ["point_right_tone4"] = "👉🏾", - ["point_right_tone5"] = "👉🏿", - ["point_up_2"] = "👆", - ["point_up_2_tone1"] = "👆🏻", - ["point_up_2_tone2"] = "👆🏼", - ["point_up_2_tone3"] = "👆🏽", - ["point_up_2_tone4"] = "👆🏾", - ["point_up_2_tone5"] = "👆🏿", - ["point_down"] = "👇", - ["point_down_tone1"] = "👇🏻", - ["point_down_tone2"] = "👇🏼", - ["point_down_tone3"] = "👇🏽", - ["point_down_tone4"] = "👇🏾", - ["point_down_tone5"] = "👇🏿", - ["point_up"] = "☝️", - ["point_up_tone1"] = "☝🏻", - ["point_up_tone2"] = "☝🏼", - ["point_up_tone3"] = "☝🏽", - ["point_up_tone4"] = "☝🏾", - ["point_up_tone5"] = "☝🏿", - ["raised_hand"] = "✋", - ["raised_hand_tone1"] = "✋🏻", - ["raised_hand_tone2"] = "✋🏼", - ["raised_hand_tone3"] = "✋🏽", - ["raised_hand_tone4"] = "✋🏾", - ["raised_hand_tone5"] = "✋🏿", - ["raised_back_of_hand"] = "🤚", - ["back_of_hand"] = "🤚", - ["raised_back_of_hand_tone1"] = "🤚🏻", - ["back_of_hand_tone1"] = "🤚🏻", - ["raised_back_of_hand_tone2"] = "🤚🏼", - ["back_of_hand_tone2"] = "🤚🏼", - ["raised_back_of_hand_tone3"] = "🤚🏽", - ["back_of_hand_tone3"] = "🤚🏽", - ["raised_back_of_hand_tone4"] = "🤚🏾", - ["back_of_hand_tone4"] = "🤚🏾", - ["raised_back_of_hand_tone5"] = "🤚🏿", - ["back_of_hand_tone5"] = "🤚🏿", - ["hand_splayed"] = "🖐️", - ["raised_hand_with_fingers_splayed"] = "🖐️", - ["hand_splayed_tone1"] = "🖐🏻", - ["raised_hand_with_fingers_splayed_tone1"] = "🖐🏻", - ["hand_splayed_tone2"] = "🖐🏼", - ["raised_hand_with_fingers_splayed_tone2"] = "🖐🏼", - ["hand_splayed_tone3"] = "🖐🏽", - ["raised_hand_with_fingers_splayed_tone3"] = "🖐🏽", - ["hand_splayed_tone4"] = "🖐🏾", - ["raised_hand_with_fingers_splayed_tone4"] = "🖐🏾", - ["hand_splayed_tone5"] = "🖐🏿", - ["raised_hand_with_fingers_splayed_tone5"] = "🖐🏿", - ["vulcan"] = "🖖", - ["raised_hand_with_part_between_middle_and_ring_fingers"] = "🖖", - ["vulcan_tone1"] = "🖖🏻", - ["raised_hand_with_part_between_middle_and_ring_fingers_tone1"] = "🖖🏻", - ["vulcan_tone2"] = "🖖🏼", - ["raised_hand_with_part_between_middle_and_ring_fingers_tone2"] = "🖖🏼", - ["vulcan_tone3"] = "🖖🏽", - ["raised_hand_with_part_between_middle_and_ring_fingers_tone3"] = "🖖🏽", - ["vulcan_tone4"] = "🖖🏾", - ["raised_hand_with_part_between_middle_and_ring_fingers_tone4"] = "🖖🏾", - ["vulcan_tone5"] = "🖖🏿", - ["raised_hand_with_part_between_middle_and_ring_fingers_tone5"] = "🖖🏿", - ["wave"] = "👋", - ["wave_tone1"] = "👋🏻", - ["wave_tone2"] = "👋🏼", - ["wave_tone3"] = "👋🏽", - ["wave_tone4"] = "👋🏾", - ["wave_tone5"] = "👋🏿", - ["call_me"] = "🤙", - ["call_me_hand"] = "🤙", - ["call_me_tone1"] = "🤙🏻", - ["call_me_hand_tone1"] = "🤙🏻", - ["call_me_tone2"] = "🤙🏼", - ["call_me_hand_tone2"] = "🤙🏼", - ["call_me_tone3"] = "🤙🏽", - ["call_me_hand_tone3"] = "🤙🏽", - ["call_me_tone4"] = "🤙🏾", - ["call_me_hand_tone4"] = "🤙🏾", - ["call_me_tone5"] = "🤙🏿", - ["call_me_hand_tone5"] = "🤙🏿", - ["muscle"] = "💪", - ["muscle_tone1"] = "💪🏻", - ["muscle_tone2"] = "💪🏼", - ["muscle_tone3"] = "💪🏽", - ["muscle_tone4"] = "💪🏾", - ["muscle_tone5"] = "💪🏿", - ["mechanical_arm"] = "🦾", - ["middle_finger"] = "🖕", - ["reversed_hand_with_middle_finger_extended"] = "🖕", - ["middle_finger_tone1"] = "🖕🏻", - ["reversed_hand_with_middle_finger_extended_tone1"] = "🖕🏻", - ["middle_finger_tone2"] = "🖕🏼", - ["reversed_hand_with_middle_finger_extended_tone2"] = "🖕🏼", - ["middle_finger_tone3"] = "🖕🏽", - ["reversed_hand_with_middle_finger_extended_tone3"] = "🖕🏽", - ["middle_finger_tone4"] = "🖕🏾", - ["reversed_hand_with_middle_finger_extended_tone4"] = "🖕🏾", - ["middle_finger_tone5"] = "🖕🏿", - ["reversed_hand_with_middle_finger_extended_tone5"] = "🖕🏿", - ["writing_hand"] = "✍️", - ["writing_hand_tone1"] = "✍🏻", - ["writing_hand_tone2"] = "✍🏼", - ["writing_hand_tone3"] = "✍🏽", - ["writing_hand_tone4"] = "✍🏾", - ["writing_hand_tone5"] = "✍🏿", - ["pray"] = "🙏", - ["pray_tone1"] = "🙏🏻", - ["pray_tone2"] = "🙏🏼", - ["pray_tone3"] = "🙏🏽", - ["pray_tone4"] = "🙏🏾", - ["pray_tone5"] = "🙏🏿", - ["foot"] = "🦶", - ["foot_tone1"] = "🦶🏻", - ["foot_light_skin_tone"] = "🦶🏻", - ["foot_tone2"] = "🦶🏼", - ["foot_medium_light_skin_tone"] = "🦶🏼", - ["foot_tone3"] = "🦶🏽", - ["foot_medium_skin_tone"] = "🦶🏽", - ["foot_tone4"] = "🦶🏾", - ["foot_medium_dark_skin_tone"] = "🦶🏾", - ["foot_tone5"] = "🦶🏿", - ["foot_dark_skin_tone"] = "🦶🏿", - ["leg"] = "🦵", - ["leg_tone1"] = "🦵🏻", - ["leg_light_skin_tone"] = "🦵🏻", - ["leg_tone2"] = "🦵🏼", - ["leg_medium_light_skin_tone"] = "🦵🏼", - ["leg_tone3"] = "🦵🏽", - ["leg_medium_skin_tone"] = "🦵🏽", - ["leg_tone4"] = "🦵🏾", - ["leg_medium_dark_skin_tone"] = "🦵🏾", - ["leg_tone5"] = "🦵🏿", - ["leg_dark_skin_tone"] = "🦵🏿", - ["mechanical_leg"] = "🦿", - ["lipstick"] = "💄", - ["kiss"] = "💋", - ["lips"] = "👄", - ["tooth"] = "🦷", - ["tongue"] = "👅", - ["ear"] = "👂", - ["ear_tone1"] = "👂🏻", - ["ear_tone2"] = "👂🏼", - ["ear_tone3"] = "👂🏽", - ["ear_tone4"] = "👂🏾", - ["ear_tone5"] = "👂🏿", - ["ear_with_hearing_aid"] = "🦻", - ["ear_with_hearing_aid_tone1"] = "🦻🏻", - ["ear_with_hearing_aid_light_skin_tone"] = "🦻🏻", - ["ear_with_hearing_aid_tone2"] = "🦻🏼", - ["ear_with_hearing_aid_medium_light_skin_tone"] = "🦻🏼", - ["ear_with_hearing_aid_tone3"] = "🦻🏽", - ["ear_with_hearing_aid_medium_skin_tone"] = "🦻🏽", - ["ear_with_hearing_aid_tone4"] = "🦻🏾", - ["ear_with_hearing_aid_medium_dark_skin_tone"] = "🦻🏾", - ["ear_with_hearing_aid_tone5"] = "🦻🏿", - ["ear_with_hearing_aid_dark_skin_tone"] = "🦻🏿", - ["nose"] = "👃", - ["nose_tone1"] = "👃🏻", - ["nose_tone2"] = "👃🏼", - ["nose_tone3"] = "👃🏽", - ["nose_tone4"] = "👃🏾", - ["nose_tone5"] = "👃🏿", - ["footprints"] = "👣", - ["eye"] = "👁️", - ["eyes"] = "👀", - ["brain"] = "🧠", - ["anatomical_heart"] = "🫀", - ["lungs"] = "🫁", - ["bone"] = "🦴", - ["speaking_head"] = "🗣️", - ["speaking_head_in_silhouette"] = "🗣️", - ["bust_in_silhouette"] = "👤", - ["busts_in_silhouette"] = "👥", - ["people_hugging"] = "🫂", - ["baby"] = "👶", - ["baby_tone1"] = "👶🏻", - ["baby_tone2"] = "👶🏼", - ["baby_tone3"] = "👶🏽", - ["baby_tone4"] = "👶🏾", - ["baby_tone5"] = "👶🏿", - ["girl"] = "👧", - ["girl_tone1"] = "👧🏻", - ["girl_tone2"] = "👧🏼", - ["girl_tone3"] = "👧🏽", - ["girl_tone4"] = "👧🏾", - ["girl_tone5"] = "👧🏿", - ["child"] = "🧒", - ["child_tone1"] = "🧒🏻", - ["child_light_skin_tone"] = "🧒🏻", - ["child_tone2"] = "🧒🏼", - ["child_medium_light_skin_tone"] = "🧒🏼", - ["child_tone3"] = "🧒🏽", - ["child_medium_skin_tone"] = "🧒🏽", - ["child_tone4"] = "🧒🏾", - ["child_medium_dark_skin_tone"] = "🧒🏾", - ["child_tone5"] = "🧒🏿", - ["child_dark_skin_tone"] = "🧒🏿", - ["boy"] = "👦", - ["boy_tone1"] = "👦🏻", - ["boy_tone2"] = "👦🏼", - ["boy_tone3"] = "👦🏽", - ["boy_tone4"] = "👦🏾", - ["boy_tone5"] = "👦🏿", - ["woman"] = "👩", - ["woman_tone1"] = "👩🏻", - ["woman_tone2"] = "👩🏼", - ["woman_tone3"] = "👩🏽", - ["woman_tone4"] = "👩🏾", - ["woman_tone5"] = "👩🏿", - ["adult"] = "🧑", - ["adult_tone1"] = "🧑🏻", - ["adult_light_skin_tone"] = "🧑🏻", - ["adult_tone2"] = "🧑🏼", - ["adult_medium_light_skin_tone"] = "🧑🏼", - ["adult_tone3"] = "🧑🏽", - ["adult_medium_skin_tone"] = "🧑🏽", - ["adult_tone4"] = "🧑🏾", - ["adult_medium_dark_skin_tone"] = "🧑🏾", - ["adult_tone5"] = "🧑🏿", - ["adult_dark_skin_tone"] = "🧑🏿", - ["man"] = "👨", - ["man_tone1"] = "👨🏻", - ["man_tone2"] = "👨🏼", - ["man_tone3"] = "👨🏽", - ["man_tone4"] = "👨🏾", - ["man_tone5"] = "👨🏿", - ["person_curly_hair"] = "🧑‍🦱", - ["person_tone1_curly_hair"] = "🧑🏻‍🦱", - ["person_light_skin_tone_curly_hair"] = "🧑🏻‍🦱", - ["person_tone2_curly_hair"] = "🧑🏼‍🦱", - ["person_medium_light_skin_tone_curly_hair"] = "🧑🏼‍🦱", - ["person_tone3_curly_hair"] = "🧑🏽‍🦱", - ["person_medium_skin_tone_curly_hair"] = "🧑🏽‍🦱", - ["person_tone4_curly_hair"] = "🧑🏾‍🦱", - ["person_medium_dark_skin_tone_curly_hair"] = "🧑🏾‍🦱", - ["person_tone5_curly_hair"] = "🧑🏿‍🦱", - ["person_dark_skin_tone_curly_hair"] = "🧑🏿‍🦱", - ["woman_curly_haired"] = "👩‍🦱", - ["woman_curly_haired_tone1"] = "👩🏻‍🦱", - ["woman_curly_haired_light_skin_tone"] = "👩🏻‍🦱", - ["woman_curly_haired_tone2"] = "👩🏼‍🦱", - ["woman_curly_haired_medium_light_skin_tone"] = "👩🏼‍🦱", - ["woman_curly_haired_tone3"] = "👩🏽‍🦱", - ["woman_curly_haired_medium_skin_tone"] = "👩🏽‍🦱", - ["woman_curly_haired_tone4"] = "👩🏾‍🦱", - ["woman_curly_haired_medium_dark_skin_tone"] = "👩🏾‍🦱", - ["woman_curly_haired_tone5"] = "👩🏿‍🦱", - ["woman_curly_haired_dark_skin_tone"] = "👩🏿‍🦱", - ["man_curly_haired"] = "👨‍🦱", - ["man_curly_haired_tone1"] = "👨🏻‍🦱", - ["man_curly_haired_light_skin_tone"] = "👨🏻‍🦱", - ["man_curly_haired_tone2"] = "👨🏼‍🦱", - ["man_curly_haired_medium_light_skin_tone"] = "👨🏼‍🦱", - ["man_curly_haired_tone3"] = "👨🏽‍🦱", - ["man_curly_haired_medium_skin_tone"] = "👨🏽‍🦱", - ["man_curly_haired_tone4"] = "👨🏾‍🦱", - ["man_curly_haired_medium_dark_skin_tone"] = "👨🏾‍🦱", - ["man_curly_haired_tone5"] = "👨🏿‍🦱", - ["man_curly_haired_dark_skin_tone"] = "👨🏿‍🦱", - ["person_red_hair"] = "🧑‍🦰", - ["person_tone1_red_hair"] = "🧑🏻‍🦰", - ["person_light_skin_tone_red_hair"] = "🧑🏻‍🦰", - ["person_tone2_red_hair"] = "🧑🏼‍🦰", - ["person_medium_light_skin_tone_red_hair"] = "🧑🏼‍🦰", - ["person_tone3_red_hair"] = "🧑🏽‍🦰", - ["person_medium_skin_tone_red_hair"] = "🧑🏽‍🦰", - ["person_tone4_red_hair"] = "🧑🏾‍🦰", - ["person_medium_dark_skin_tone_red_hair"] = "🧑🏾‍🦰", - ["person_tone5_red_hair"] = "🧑🏿‍🦰", - ["person_dark_skin_tone_red_hair"] = "🧑🏿‍🦰", - ["woman_red_haired"] = "👩‍🦰", - ["woman_red_haired_tone1"] = "👩🏻‍🦰", - ["woman_red_haired_light_skin_tone"] = "👩🏻‍🦰", - ["woman_red_haired_tone2"] = "👩🏼‍🦰", - ["woman_red_haired_medium_light_skin_tone"] = "👩🏼‍🦰", - ["woman_red_haired_tone3"] = "👩🏽‍🦰", - ["woman_red_haired_medium_skin_tone"] = "👩🏽‍🦰", - ["woman_red_haired_tone4"] = "👩🏾‍🦰", - ["woman_red_haired_medium_dark_skin_tone"] = "👩🏾‍🦰", - ["woman_red_haired_tone5"] = "👩🏿‍🦰", - ["woman_red_haired_dark_skin_tone"] = "👩🏿‍🦰", - ["man_red_haired"] = "👨‍🦰", - ["man_red_haired_tone1"] = "👨🏻‍🦰", - ["man_red_haired_light_skin_tone"] = "👨🏻‍🦰", - ["man_red_haired_tone2"] = "👨🏼‍🦰", - ["man_red_haired_medium_light_skin_tone"] = "👨🏼‍🦰", - ["man_red_haired_tone3"] = "👨🏽‍🦰", - ["man_red_haired_medium_skin_tone"] = "👨🏽‍🦰", - ["man_red_haired_tone4"] = "👨🏾‍🦰", - ["man_red_haired_medium_dark_skin_tone"] = "👨🏾‍🦰", - ["man_red_haired_tone5"] = "👨🏿‍🦰", - ["man_red_haired_dark_skin_tone"] = "👨🏿‍🦰", - ["blond_haired_woman"] = "👱‍♀️", - ["blond_haired_woman_tone1"] = "👱🏻‍♀️", - ["blond_haired_woman_light_skin_tone"] = "👱🏻‍♀️", - ["blond_haired_woman_tone2"] = "👱🏼‍♀️", - ["blond_haired_woman_medium_light_skin_tone"] = "👱🏼‍♀️", - ["blond_haired_woman_tone3"] = "👱🏽‍♀️", - ["blond_haired_woman_medium_skin_tone"] = "👱🏽‍♀️", - ["blond_haired_woman_tone4"] = "👱🏾‍♀️", - ["blond_haired_woman_medium_dark_skin_tone"] = "👱🏾‍♀️", - ["blond_haired_woman_tone5"] = "👱🏿‍♀️", - ["blond_haired_woman_dark_skin_tone"] = "👱🏿‍♀️", - ["blond_haired_person"] = "👱", - ["person_with_blond_hair"] = "👱", - ["blond_haired_person_tone1"] = "👱🏻", - ["person_with_blond_hair_tone1"] = "👱🏻", - ["blond_haired_person_tone2"] = "👱🏼", - ["person_with_blond_hair_tone2"] = "👱🏼", - ["blond_haired_person_tone3"] = "👱🏽", - ["person_with_blond_hair_tone3"] = "👱🏽", - ["blond_haired_person_tone4"] = "👱🏾", - ["person_with_blond_hair_tone4"] = "👱🏾", - ["blond_haired_person_tone5"] = "👱🏿", - ["person_with_blond_hair_tone5"] = "👱🏿", - ["blond_haired_man"] = "👱‍♂️", - ["blond_haired_man_tone1"] = "👱🏻‍♂️", - ["blond_haired_man_light_skin_tone"] = "👱🏻‍♂️", - ["blond_haired_man_tone2"] = "👱🏼‍♂️", - ["blond_haired_man_medium_light_skin_tone"] = "👱🏼‍♂️", - ["blond_haired_man_tone3"] = "👱🏽‍♂️", - ["blond_haired_man_medium_skin_tone"] = "👱🏽‍♂️", - ["blond_haired_man_tone4"] = "👱🏾‍♂️", - ["blond_haired_man_medium_dark_skin_tone"] = "👱🏾‍♂️", - ["blond_haired_man_tone5"] = "👱🏿‍♂️", - ["blond_haired_man_dark_skin_tone"] = "👱🏿‍♂️", - ["person_white_hair"] = "🧑‍🦳", - ["person_tone1_white_hair"] = "🧑🏻‍🦳", - ["person_light_skin_tone_white_hair"] = "🧑🏻‍🦳", - ["person_tone2_white_hair"] = "🧑🏼‍🦳", - ["person_medium_light_skin_tone_white_hair"] = "🧑🏼‍🦳", - ["person_tone3_white_hair"] = "🧑🏽‍🦳", - ["person_medium_skin_tone_white_hair"] = "🧑🏽‍🦳", - ["person_tone4_white_hair"] = "🧑🏾‍🦳", - ["person_medium_dark_skin_tone_white_hair"] = "🧑🏾‍🦳", - ["person_tone5_white_hair"] = "🧑🏿‍🦳", - ["person_dark_skin_tone_white_hair"] = "🧑🏿‍🦳", - ["woman_white_haired"] = "👩‍🦳", - ["woman_white_haired_tone1"] = "👩🏻‍🦳", - ["woman_white_haired_light_skin_tone"] = "👩🏻‍🦳", - ["woman_white_haired_tone2"] = "👩🏼‍🦳", - ["woman_white_haired_medium_light_skin_tone"] = "👩🏼‍🦳", - ["woman_white_haired_tone3"] = "👩🏽‍🦳", - ["woman_white_haired_medium_skin_tone"] = "👩🏽‍🦳", - ["woman_white_haired_tone4"] = "👩🏾‍🦳", - ["woman_white_haired_medium_dark_skin_tone"] = "👩🏾‍🦳", - ["woman_white_haired_tone5"] = "👩🏿‍🦳", - ["woman_white_haired_dark_skin_tone"] = "👩🏿‍🦳", - ["man_white_haired"] = "👨‍🦳", - ["man_white_haired_tone1"] = "👨🏻‍🦳", - ["man_white_haired_light_skin_tone"] = "👨🏻‍🦳", - ["man_white_haired_tone2"] = "👨🏼‍🦳", - ["man_white_haired_medium_light_skin_tone"] = "👨🏼‍🦳", - ["man_white_haired_tone3"] = "👨🏽‍🦳", - ["man_white_haired_medium_skin_tone"] = "👨🏽‍🦳", - ["man_white_haired_tone4"] = "👨🏾‍🦳", - ["man_white_haired_medium_dark_skin_tone"] = "👨🏾‍🦳", - ["man_white_haired_tone5"] = "👨🏿‍🦳", - ["man_white_haired_dark_skin_tone"] = "👨🏿‍🦳", - ["person_bald"] = "🧑‍🦲", - ["person_tone1_bald"] = "🧑🏻‍🦲", - ["person_light_skin_tone_bald"] = "🧑🏻‍🦲", - ["person_tone2_bald"] = "🧑🏼‍🦲", - ["person_medium_light_skin_tone_bald"] = "🧑🏼‍🦲", - ["person_tone3_bald"] = "🧑🏽‍🦲", - ["person_medium_skin_tone_bald"] = "🧑🏽‍🦲", - ["person_tone4_bald"] = "🧑🏾‍🦲", - ["person_medium_dark_skin_tone_bald"] = "🧑🏾‍🦲", - ["person_tone5_bald"] = "🧑🏿‍🦲", - ["person_dark_skin_tone_bald"] = "🧑🏿‍🦲", - ["woman_bald"] = "👩‍🦲", - ["woman_bald_tone1"] = "👩🏻‍🦲", - ["woman_bald_light_skin_tone"] = "👩🏻‍🦲", - ["woman_bald_tone2"] = "👩🏼‍🦲", - ["woman_bald_medium_light_skin_tone"] = "👩🏼‍🦲", - ["woman_bald_tone3"] = "👩🏽‍🦲", - ["woman_bald_medium_skin_tone"] = "👩🏽‍🦲", - ["woman_bald_tone4"] = "👩🏾‍🦲", - ["woman_bald_medium_dark_skin_tone"] = "👩🏾‍🦲", - ["woman_bald_tone5"] = "👩🏿‍🦲", - ["woman_bald_dark_skin_tone"] = "👩🏿‍🦲", - ["man_bald"] = "👨‍🦲", - ["man_bald_tone1"] = "👨🏻‍🦲", - ["man_bald_light_skin_tone"] = "👨🏻‍🦲", - ["man_bald_tone2"] = "👨🏼‍🦲", - ["man_bald_medium_light_skin_tone"] = "👨🏼‍🦲", - ["man_bald_tone3"] = "👨🏽‍🦲", - ["man_bald_medium_skin_tone"] = "👨🏽‍🦲", - ["man_bald_tone4"] = "👨🏾‍🦲", - ["man_bald_medium_dark_skin_tone"] = "👨🏾‍🦲", - ["man_bald_tone5"] = "👨🏿‍🦲", - ["man_bald_dark_skin_tone"] = "👨🏿‍🦲", - ["bearded_person"] = "🧔", - ["bearded_person_tone1"] = "🧔🏻", - ["bearded_person_light_skin_tone"] = "🧔🏻", - ["bearded_person_tone2"] = "🧔🏼", - ["bearded_person_medium_light_skin_tone"] = "🧔🏼", - ["bearded_person_tone3"] = "🧔🏽", - ["bearded_person_medium_skin_tone"] = "🧔🏽", - ["bearded_person_tone4"] = "🧔🏾", - ["bearded_person_medium_dark_skin_tone"] = "🧔🏾", - ["bearded_person_tone5"] = "🧔🏿", - ["bearded_person_dark_skin_tone"] = "🧔🏿", - ["man_beard"] = "🧔‍♂️", - ["man_tone1_beard"] = "🧔🏻‍♂️", - ["man_light_skin_tone_beard"] = "🧔🏻‍♂️", - ["man_tone2_beard"] = "🧔🏼‍♂️", - ["man_medium_light_skin_tone_beard"] = "🧔🏼‍♂️", - ["man_tone3_beard"] = "🧔🏽‍♂️", - ["man_medium_skin_tone_beard"] = "🧔🏽‍♂️", - ["man_tone4_beard"] = "🧔🏾‍♂️", - ["man_medium_dark_skin_tone_beard"] = "🧔🏾‍♂️", - ["man_tone5_beard"] = "🧔🏿‍♂️", - ["man_dark_skin_tone_beard"] = "🧔🏿‍♂️", - ["woman_beard"] = "🧔‍♀️", - ["woman_tone1_beard"] = "🧔🏻‍♀️", - ["woman_light_skin_tone_beard"] = "🧔🏻‍♀️", - ["woman_tone2_beard"] = "🧔🏼‍♀️", - ["woman_medium_light_skin_tone_beard"] = "🧔🏼‍♀️", - ["woman_tone3_beard"] = "🧔🏽‍♀️", - ["woman_medium_skin_tone_beard"] = "🧔🏽‍♀️", - ["woman_tone4_beard"] = "🧔🏾‍♀️", - ["woman_medium_dark_skin_tone_beard"] = "🧔🏾‍♀️", - ["woman_tone5_beard"] = "🧔🏿‍♀️", - ["woman_dark_skin_tone_beard"] = "🧔🏿‍♀️", - ["older_woman"] = "👵", - ["grandma"] = "👵", - ["older_woman_tone1"] = "👵🏻", - ["grandma_tone1"] = "👵🏻", - ["older_woman_tone2"] = "👵🏼", - ["grandma_tone2"] = "👵🏼", - ["older_woman_tone3"] = "👵🏽", - ["grandma_tone3"] = "👵🏽", - ["older_woman_tone4"] = "👵🏾", - ["grandma_tone4"] = "👵🏾", - ["older_woman_tone5"] = "👵🏿", - ["grandma_tone5"] = "👵🏿", - ["older_adult"] = "🧓", - ["older_adult_tone1"] = "🧓🏻", - ["older_adult_light_skin_tone"] = "🧓🏻", - ["older_adult_tone2"] = "🧓🏼", - ["older_adult_medium_light_skin_tone"] = "🧓🏼", - ["older_adult_tone3"] = "🧓🏽", - ["older_adult_medium_skin_tone"] = "🧓🏽", - ["older_adult_tone4"] = "🧓🏾", - ["older_adult_medium_dark_skin_tone"] = "🧓🏾", - ["older_adult_tone5"] = "🧓🏿", - ["older_adult_dark_skin_tone"] = "🧓🏿", - ["older_man"] = "👴", - ["older_man_tone1"] = "👴🏻", - ["older_man_tone2"] = "👴🏼", - ["older_man_tone3"] = "👴🏽", - ["older_man_tone4"] = "👴🏾", - ["older_man_tone5"] = "👴🏿", - ["man_with_chinese_cap"] = "👲", - ["man_with_gua_pi_mao"] = "👲", - ["man_with_chinese_cap_tone1"] = "👲🏻", - ["man_with_gua_pi_mao_tone1"] = "👲🏻", - ["man_with_chinese_cap_tone2"] = "👲🏼", - ["man_with_gua_pi_mao_tone2"] = "👲🏼", - ["man_with_chinese_cap_tone3"] = "👲🏽", - ["man_with_gua_pi_mao_tone3"] = "👲🏽", - ["man_with_chinese_cap_tone4"] = "👲🏾", - ["man_with_gua_pi_mao_tone4"] = "👲🏾", - ["man_with_chinese_cap_tone5"] = "👲🏿", - ["man_with_gua_pi_mao_tone5"] = "👲🏿", - ["person_wearing_turban"] = "👳", - ["man_with_turban"] = "👳", - ["person_wearing_turban_tone1"] = "👳🏻", - ["man_with_turban_tone1"] = "👳🏻", - ["person_wearing_turban_tone2"] = "👳🏼", - ["man_with_turban_tone2"] = "👳🏼", - ["person_wearing_turban_tone3"] = "👳🏽", - ["man_with_turban_tone3"] = "👳🏽", - ["person_wearing_turban_tone4"] = "👳🏾", - ["man_with_turban_tone4"] = "👳🏾", - ["person_wearing_turban_tone5"] = "👳🏿", - ["man_with_turban_tone5"] = "👳🏿", - ["woman_wearing_turban"] = "👳‍♀️", - ["woman_wearing_turban_tone1"] = "👳🏻‍♀️", - ["woman_wearing_turban_light_skin_tone"] = "👳🏻‍♀️", - ["woman_wearing_turban_tone2"] = "👳🏼‍♀️", - ["woman_wearing_turban_medium_light_skin_tone"] = "👳🏼‍♀️", - ["woman_wearing_turban_tone3"] = "👳🏽‍♀️", - ["woman_wearing_turban_medium_skin_tone"] = "👳🏽‍♀️", - ["woman_wearing_turban_tone4"] = "👳🏾‍♀️", - ["woman_wearing_turban_medium_dark_skin_tone"] = "👳🏾‍♀️", - ["woman_wearing_turban_tone5"] = "👳🏿‍♀️", - ["woman_wearing_turban_dark_skin_tone"] = "👳🏿‍♀️", - ["man_wearing_turban"] = "👳‍♂️", - ["man_wearing_turban_tone1"] = "👳🏻‍♂️", - ["man_wearing_turban_light_skin_tone"] = "👳🏻‍♂️", - ["man_wearing_turban_tone2"] = "👳🏼‍♂️", - ["man_wearing_turban_medium_light_skin_tone"] = "👳🏼‍♂️", - ["man_wearing_turban_tone3"] = "👳🏽‍♂️", - ["man_wearing_turban_medium_skin_tone"] = "👳🏽‍♂️", - ["man_wearing_turban_tone4"] = "👳🏾‍♂️", - ["man_wearing_turban_medium_dark_skin_tone"] = "👳🏾‍♂️", - ["man_wearing_turban_tone5"] = "👳🏿‍♂️", - ["man_wearing_turban_dark_skin_tone"] = "👳🏿‍♂️", - ["woman_with_headscarf"] = "🧕", - ["woman_with_headscarf_tone1"] = "🧕🏻", - ["woman_with_headscarf_light_skin_tone"] = "🧕🏻", - ["woman_with_headscarf_tone2"] = "🧕🏼", - ["woman_with_headscarf_medium_light_skin_tone"] = "🧕🏼", - ["woman_with_headscarf_tone3"] = "🧕🏽", - ["woman_with_headscarf_medium_skin_tone"] = "🧕🏽", - ["woman_with_headscarf_tone4"] = "🧕🏾", - ["woman_with_headscarf_medium_dark_skin_tone"] = "🧕🏾", - ["woman_with_headscarf_tone5"] = "🧕🏿", - ["woman_with_headscarf_dark_skin_tone"] = "🧕🏿", - ["police_officer"] = "👮", - ["cop"] = "👮", - ["police_officer_tone1"] = "👮🏻", - ["cop_tone1"] = "👮🏻", - ["police_officer_tone2"] = "👮🏼", - ["cop_tone2"] = "👮🏼", - ["police_officer_tone3"] = "👮🏽", - ["cop_tone3"] = "👮🏽", - ["police_officer_tone4"] = "👮🏾", - ["cop_tone4"] = "👮🏾", - ["police_officer_tone5"] = "👮🏿", - ["cop_tone5"] = "👮🏿", - ["woman_police_officer"] = "👮‍♀️", - ["woman_police_officer_tone1"] = "👮🏻‍♀️", - ["woman_police_officer_light_skin_tone"] = "👮🏻‍♀️", - ["woman_police_officer_tone2"] = "👮🏼‍♀️", - ["woman_police_officer_medium_light_skin_tone"] = "👮🏼‍♀️", - ["woman_police_officer_tone3"] = "👮🏽‍♀️", - ["woman_police_officer_medium_skin_tone"] = "👮🏽‍♀️", - ["woman_police_officer_tone4"] = "👮🏾‍♀️", - ["woman_police_officer_medium_dark_skin_tone"] = "👮🏾‍♀️", - ["woman_police_officer_tone5"] = "👮🏿‍♀️", - ["woman_police_officer_dark_skin_tone"] = "👮🏿‍♀️", - ["man_police_officer"] = "👮‍♂️", - ["man_police_officer_tone1"] = "👮🏻‍♂️", - ["man_police_officer_light_skin_tone"] = "👮🏻‍♂️", - ["man_police_officer_tone2"] = "👮🏼‍♂️", - ["man_police_officer_medium_light_skin_tone"] = "👮🏼‍♂️", - ["man_police_officer_tone3"] = "👮🏽‍♂️", - ["man_police_officer_medium_skin_tone"] = "👮🏽‍♂️", - ["man_police_officer_tone4"] = "👮🏾‍♂️", - ["man_police_officer_medium_dark_skin_tone"] = "👮🏾‍♂️", - ["man_police_officer_tone5"] = "👮🏿‍♂️", - ["man_police_officer_dark_skin_tone"] = "👮🏿‍♂️", - ["construction_worker"] = "👷", - ["construction_worker_tone1"] = "👷🏻", - ["construction_worker_tone2"] = "👷🏼", - ["construction_worker_tone3"] = "👷🏽", - ["construction_worker_tone4"] = "👷🏾", - ["construction_worker_tone5"] = "👷🏿", - ["woman_construction_worker"] = "👷‍♀️", - ["woman_construction_worker_tone1"] = "👷🏻‍♀️", - ["woman_construction_worker_light_skin_tone"] = "👷🏻‍♀️", - ["woman_construction_worker_tone2"] = "👷🏼‍♀️", - ["woman_construction_worker_medium_light_skin_tone"] = "👷🏼‍♀️", - ["woman_construction_worker_tone3"] = "👷🏽‍♀️", - ["woman_construction_worker_medium_skin_tone"] = "👷🏽‍♀️", - ["woman_construction_worker_tone4"] = "👷🏾‍♀️", - ["woman_construction_worker_medium_dark_skin_tone"] = "👷🏾‍♀️", - ["woman_construction_worker_tone5"] = "👷🏿‍♀️", - ["woman_construction_worker_dark_skin_tone"] = "👷🏿‍♀️", - ["man_construction_worker"] = "👷‍♂️", - ["man_construction_worker_tone1"] = "👷🏻‍♂️", - ["man_construction_worker_light_skin_tone"] = "👷🏻‍♂️", - ["man_construction_worker_tone2"] = "👷🏼‍♂️", - ["man_construction_worker_medium_light_skin_tone"] = "👷🏼‍♂️", - ["man_construction_worker_tone3"] = "👷🏽‍♂️", - ["man_construction_worker_medium_skin_tone"] = "👷🏽‍♂️", - ["man_construction_worker_tone4"] = "👷🏾‍♂️", - ["man_construction_worker_medium_dark_skin_tone"] = "👷🏾‍♂️", - ["man_construction_worker_tone5"] = "👷🏿‍♂️", - ["man_construction_worker_dark_skin_tone"] = "👷🏿‍♂️", - ["guard"] = "💂", - ["guardsman"] = "💂", - ["guard_tone1"] = "💂🏻", - ["guardsman_tone1"] = "💂🏻", - ["guard_tone2"] = "💂🏼", - ["guardsman_tone2"] = "💂🏼", - ["guard_tone3"] = "💂🏽", - ["guardsman_tone3"] = "💂🏽", - ["guard_tone4"] = "💂🏾", - ["guardsman_tone4"] = "💂🏾", - ["guard_tone5"] = "💂🏿", - ["guardsman_tone5"] = "💂🏿", - ["woman_guard"] = "💂‍♀️", - ["woman_guard_tone1"] = "💂🏻‍♀️", - ["woman_guard_light_skin_tone"] = "💂🏻‍♀️", - ["woman_guard_tone2"] = "💂🏼‍♀️", - ["woman_guard_medium_light_skin_tone"] = "💂🏼‍♀️", - ["woman_guard_tone3"] = "💂🏽‍♀️", - ["woman_guard_medium_skin_tone"] = "💂🏽‍♀️", - ["woman_guard_tone4"] = "💂🏾‍♀️", - ["woman_guard_medium_dark_skin_tone"] = "💂🏾‍♀️", - ["woman_guard_tone5"] = "💂🏿‍♀️", - ["woman_guard_dark_skin_tone"] = "💂🏿‍♀️", - ["man_guard"] = "💂‍♂️", - ["man_guard_tone1"] = "💂🏻‍♂️", - ["man_guard_light_skin_tone"] = "💂🏻‍♂️", - ["man_guard_tone2"] = "💂🏼‍♂️", - ["man_guard_medium_light_skin_tone"] = "💂🏼‍♂️", - ["man_guard_tone3"] = "💂🏽‍♂️", - ["man_guard_medium_skin_tone"] = "💂🏽‍♂️", - ["man_guard_tone4"] = "💂🏾‍♂️", - ["man_guard_medium_dark_skin_tone"] = "💂🏾‍♂️", - ["man_guard_tone5"] = "💂🏿‍♂️", - ["man_guard_dark_skin_tone"] = "💂🏿‍♂️", - ["detective"] = "🕵️", - ["spy"] = "🕵️", - ["sleuth_or_spy"] = "🕵️", - ["detective_tone1"] = "🕵🏻", - ["spy_tone1"] = "🕵🏻", - ["sleuth_or_spy_tone1"] = "🕵🏻", - ["detective_tone2"] = "🕵🏼", - ["spy_tone2"] = "🕵🏼", - ["sleuth_or_spy_tone2"] = "🕵🏼", - ["detective_tone3"] = "🕵🏽", - ["spy_tone3"] = "🕵🏽", - ["sleuth_or_spy_tone3"] = "🕵🏽", - ["detective_tone4"] = "🕵🏾", - ["spy_tone4"] = "🕵🏾", - ["sleuth_or_spy_tone4"] = "🕵🏾", - ["detective_tone5"] = "🕵🏿", - ["spy_tone5"] = "🕵🏿", - ["sleuth_or_spy_tone5"] = "🕵🏿", - ["woman_detective"] = "🕵️‍♀️", - ["woman_detective_tone1"] = "🕵🏻‍♀️", - ["woman_detective_light_skin_tone"] = "🕵🏻‍♀️", - ["woman_detective_tone2"] = "🕵🏼‍♀️", - ["woman_detective_medium_light_skin_tone"] = "🕵🏼‍♀️", - ["woman_detective_tone3"] = "🕵🏽‍♀️", - ["woman_detective_medium_skin_tone"] = "🕵🏽‍♀️", - ["woman_detective_tone4"] = "🕵🏾‍♀️", - ["woman_detective_medium_dark_skin_tone"] = "🕵🏾‍♀️", - ["woman_detective_tone5"] = "🕵🏿‍♀️", - ["woman_detective_dark_skin_tone"] = "🕵🏿‍♀️", - ["man_detective"] = "🕵️‍♂️", - ["man_detective_tone1"] = "🕵🏻‍♂️", - ["man_detective_light_skin_tone"] = "🕵🏻‍♂️", - ["man_detective_tone2"] = "🕵🏼‍♂️", - ["man_detective_medium_light_skin_tone"] = "🕵🏼‍♂️", - ["man_detective_tone3"] = "🕵🏽‍♂️", - ["man_detective_medium_skin_tone"] = "🕵🏽‍♂️", - ["man_detective_tone4"] = "🕵🏾‍♂️", - ["man_detective_medium_dark_skin_tone"] = "🕵🏾‍♂️", - ["man_detective_tone5"] = "🕵🏿‍♂️", - ["man_detective_dark_skin_tone"] = "🕵🏿‍♂️", - ["health_worker"] = "🧑‍⚕️", - ["health_worker_tone1"] = "🧑🏻‍⚕️", - ["health_worker_light_skin_tone"] = "🧑🏻‍⚕️", - ["health_worker_tone2"] = "🧑🏼‍⚕️", - ["health_worker_medium_light_skin_tone"] = "🧑🏼‍⚕️", - ["health_worker_tone3"] = "🧑🏽‍⚕️", - ["health_worker_medium_skin_tone"] = "🧑🏽‍⚕️", - ["health_worker_tone4"] = "🧑🏾‍⚕️", - ["health_worker_medium_dark_skin_tone"] = "🧑🏾‍⚕️", - ["health_worker_tone5"] = "🧑🏿‍⚕️", - ["health_worker_dark_skin_tone"] = "🧑🏿‍⚕️", - ["woman_health_worker"] = "👩‍⚕️", - ["woman_health_worker_tone1"] = "👩🏻‍⚕️", - ["woman_health_worker_light_skin_tone"] = "👩🏻‍⚕️", - ["woman_health_worker_tone2"] = "👩🏼‍⚕️", - ["woman_health_worker_medium_light_skin_tone"] = "👩🏼‍⚕️", - ["woman_health_worker_tone3"] = "👩🏽‍⚕️", - ["woman_health_worker_medium_skin_tone"] = "👩🏽‍⚕️", - ["woman_health_worker_tone4"] = "👩🏾‍⚕️", - ["woman_health_worker_medium_dark_skin_tone"] = "👩🏾‍⚕️", - ["woman_health_worker_tone5"] = "👩🏿‍⚕️", - ["woman_health_worker_dark_skin_tone"] = "👩🏿‍⚕️", - ["man_health_worker"] = "👨‍⚕️", - ["man_health_worker_tone1"] = "👨🏻‍⚕️", - ["man_health_worker_light_skin_tone"] = "👨🏻‍⚕️", - ["man_health_worker_tone2"] = "👨🏼‍⚕️", - ["man_health_worker_medium_light_skin_tone"] = "👨🏼‍⚕️", - ["man_health_worker_tone3"] = "👨🏽‍⚕️", - ["man_health_worker_medium_skin_tone"] = "👨🏽‍⚕️", - ["man_health_worker_tone4"] = "👨🏾‍⚕️", - ["man_health_worker_medium_dark_skin_tone"] = "👨🏾‍⚕️", - ["man_health_worker_tone5"] = "👨🏿‍⚕️", - ["man_health_worker_dark_skin_tone"] = "👨🏿‍⚕️", - ["farmer"] = "🧑‍🌾", - ["farmer_tone1"] = "🧑🏻‍🌾", - ["farmer_light_skin_tone"] = "🧑🏻‍🌾", - ["farmer_tone2"] = "🧑🏼‍🌾", - ["farmer_medium_light_skin_tone"] = "🧑🏼‍🌾", - ["farmer_tone3"] = "🧑🏽‍🌾", - ["farmer_medium_skin_tone"] = "🧑🏽‍🌾", - ["farmer_tone4"] = "🧑🏾‍🌾", - ["farmer_medium_dark_skin_tone"] = "🧑🏾‍🌾", - ["farmer_tone5"] = "🧑🏿‍🌾", - ["farmer_dark_skin_tone"] = "🧑🏿‍🌾", - ["woman_farmer"] = "👩‍🌾", - ["woman_farmer_tone1"] = "👩🏻‍🌾", - ["woman_farmer_light_skin_tone"] = "👩🏻‍🌾", - ["woman_farmer_tone2"] = "👩🏼‍🌾", - ["woman_farmer_medium_light_skin_tone"] = "👩🏼‍🌾", - ["woman_farmer_tone3"] = "👩🏽‍🌾", - ["woman_farmer_medium_skin_tone"] = "👩🏽‍🌾", - ["woman_farmer_tone4"] = "👩🏾‍🌾", - ["woman_farmer_medium_dark_skin_tone"] = "👩🏾‍🌾", - ["woman_farmer_tone5"] = "👩🏿‍🌾", - ["woman_farmer_dark_skin_tone"] = "👩🏿‍🌾", - ["man_farmer"] = "👨‍🌾", - ["man_farmer_tone1"] = "👨🏻‍🌾", - ["man_farmer_light_skin_tone"] = "👨🏻‍🌾", - ["man_farmer_tone2"] = "👨🏼‍🌾", - ["man_farmer_medium_light_skin_tone"] = "👨🏼‍🌾", - ["man_farmer_tone3"] = "👨🏽‍🌾", - ["man_farmer_medium_skin_tone"] = "👨🏽‍🌾", - ["man_farmer_tone4"] = "👨🏾‍🌾", - ["man_farmer_medium_dark_skin_tone"] = "👨🏾‍🌾", - ["man_farmer_tone5"] = "👨🏿‍🌾", - ["man_farmer_dark_skin_tone"] = "👨🏿‍🌾", - ["cook"] = "🧑‍🍳", - ["cook_tone1"] = "🧑🏻‍🍳", - ["cook_light_skin_tone"] = "🧑🏻‍🍳", - ["cook_tone2"] = "🧑🏼‍🍳", - ["cook_medium_light_skin_tone"] = "🧑🏼‍🍳", - ["cook_tone3"] = "🧑🏽‍🍳", - ["cook_medium_skin_tone"] = "🧑🏽‍🍳", - ["cook_tone4"] = "🧑🏾‍🍳", - ["cook_medium_dark_skin_tone"] = "🧑🏾‍🍳", - ["cook_tone5"] = "🧑🏿‍🍳", - ["cook_dark_skin_tone"] = "🧑🏿‍🍳", - ["woman_cook"] = "👩‍🍳", - ["woman_cook_tone1"] = "👩🏻‍🍳", - ["woman_cook_light_skin_tone"] = "👩🏻‍🍳", - ["woman_cook_tone2"] = "👩🏼‍🍳", - ["woman_cook_medium_light_skin_tone"] = "👩🏼‍🍳", - ["woman_cook_tone3"] = "👩🏽‍🍳", - ["woman_cook_medium_skin_tone"] = "👩🏽‍🍳", - ["woman_cook_tone4"] = "👩🏾‍🍳", - ["woman_cook_medium_dark_skin_tone"] = "👩🏾‍🍳", - ["woman_cook_tone5"] = "👩🏿‍🍳", - ["woman_cook_dark_skin_tone"] = "👩🏿‍🍳", - ["man_cook"] = "👨‍🍳", - ["man_cook_tone1"] = "👨🏻‍🍳", - ["man_cook_light_skin_tone"] = "👨🏻‍🍳", - ["man_cook_tone2"] = "👨🏼‍🍳", - ["man_cook_medium_light_skin_tone"] = "👨🏼‍🍳", - ["man_cook_tone3"] = "👨🏽‍🍳", - ["man_cook_medium_skin_tone"] = "👨🏽‍🍳", - ["man_cook_tone4"] = "👨🏾‍🍳", - ["man_cook_medium_dark_skin_tone"] = "👨🏾‍🍳", - ["man_cook_tone5"] = "👨🏿‍🍳", - ["man_cook_dark_skin_tone"] = "👨🏿‍🍳", - ["student"] = "🧑‍🎓", - ["student_tone1"] = "🧑🏻‍🎓", - ["student_light_skin_tone"] = "🧑🏻‍🎓", - ["student_tone2"] = "🧑🏼‍🎓", - ["student_medium_light_skin_tone"] = "🧑🏼‍🎓", - ["student_tone3"] = "🧑🏽‍🎓", - ["student_medium_skin_tone"] = "🧑🏽‍🎓", - ["student_tone4"] = "🧑🏾‍🎓", - ["student_medium_dark_skin_tone"] = "🧑🏾‍🎓", - ["student_tone5"] = "🧑🏿‍🎓", - ["student_dark_skin_tone"] = "🧑🏿‍🎓", - ["woman_student"] = "👩‍🎓", - ["woman_student_tone1"] = "👩🏻‍🎓", - ["woman_student_light_skin_tone"] = "👩🏻‍🎓", - ["woman_student_tone2"] = "👩🏼‍🎓", - ["woman_student_medium_light_skin_tone"] = "👩🏼‍🎓", - ["woman_student_tone3"] = "👩🏽‍🎓", - ["woman_student_medium_skin_tone"] = "👩🏽‍🎓", - ["woman_student_tone4"] = "👩🏾‍🎓", - ["woman_student_medium_dark_skin_tone"] = "👩🏾‍🎓", - ["woman_student_tone5"] = "👩🏿‍🎓", - ["woman_student_dark_skin_tone"] = "👩🏿‍🎓", - ["man_student"] = "👨‍🎓", - ["man_student_tone1"] = "👨🏻‍🎓", - ["man_student_light_skin_tone"] = "👨🏻‍🎓", - ["man_student_tone2"] = "👨🏼‍🎓", - ["man_student_medium_light_skin_tone"] = "👨🏼‍🎓", - ["man_student_tone3"] = "👨🏽‍🎓", - ["man_student_medium_skin_tone"] = "👨🏽‍🎓", - ["man_student_tone4"] = "👨🏾‍🎓", - ["man_student_medium_dark_skin_tone"] = "👨🏾‍🎓", - ["man_student_tone5"] = "👨🏿‍🎓", - ["man_student_dark_skin_tone"] = "👨🏿‍🎓", - ["singer"] = "🧑‍🎤", - ["singer_tone1"] = "🧑🏻‍🎤", - ["singer_light_skin_tone"] = "🧑🏻‍🎤", - ["singer_tone2"] = "🧑🏼‍🎤", - ["singer_medium_light_skin_tone"] = "🧑🏼‍🎤", - ["singer_tone3"] = "🧑🏽‍🎤", - ["singer_medium_skin_tone"] = "🧑🏽‍🎤", - ["singer_tone4"] = "🧑🏾‍🎤", - ["singer_medium_dark_skin_tone"] = "🧑🏾‍🎤", - ["singer_tone5"] = "🧑🏿‍🎤", - ["singer_dark_skin_tone"] = "🧑🏿‍🎤", - ["woman_singer"] = "👩‍🎤", - ["woman_singer_tone1"] = "👩🏻‍🎤", - ["woman_singer_light_skin_tone"] = "👩🏻‍🎤", - ["woman_singer_tone2"] = "👩🏼‍🎤", - ["woman_singer_medium_light_skin_tone"] = "👩🏼‍🎤", - ["woman_singer_tone3"] = "👩🏽‍🎤", - ["woman_singer_medium_skin_tone"] = "👩🏽‍🎤", - ["woman_singer_tone4"] = "👩🏾‍🎤", - ["woman_singer_medium_dark_skin_tone"] = "👩🏾‍🎤", - ["woman_singer_tone5"] = "👩🏿‍🎤", - ["woman_singer_dark_skin_tone"] = "👩🏿‍🎤", - ["man_singer"] = "👨‍🎤", - ["man_singer_tone1"] = "👨🏻‍🎤", - ["man_singer_light_skin_tone"] = "👨🏻‍🎤", - ["man_singer_tone2"] = "👨🏼‍🎤", - ["man_singer_medium_light_skin_tone"] = "👨🏼‍🎤", - ["man_singer_tone3"] = "👨🏽‍🎤", - ["man_singer_medium_skin_tone"] = "👨🏽‍🎤", - ["man_singer_tone4"] = "👨🏾‍🎤", - ["man_singer_medium_dark_skin_tone"] = "👨🏾‍🎤", - ["man_singer_tone5"] = "👨🏿‍🎤", - ["man_singer_dark_skin_tone"] = "👨🏿‍🎤", - ["teacher"] = "🧑‍🏫", - ["teacher_tone1"] = "🧑🏻‍🏫", - ["teacher_light_skin_tone"] = "🧑🏻‍🏫", - ["teacher_tone2"] = "🧑🏼‍🏫", - ["teacher_medium_light_skin_tone"] = "🧑🏼‍🏫", - ["teacher_tone3"] = "🧑🏽‍🏫", - ["teacher_medium_skin_tone"] = "🧑🏽‍🏫", - ["teacher_tone4"] = "🧑🏾‍🏫", - ["teacher_medium_dark_skin_tone"] = "🧑🏾‍🏫", - ["teacher_tone5"] = "🧑🏿‍🏫", - ["teacher_dark_skin_tone"] = "🧑🏿‍🏫", - ["woman_teacher"] = "👩‍🏫", - ["woman_teacher_tone1"] = "👩🏻‍🏫", - ["woman_teacher_light_skin_tone"] = "👩🏻‍🏫", - ["woman_teacher_tone2"] = "👩🏼‍🏫", - ["woman_teacher_medium_light_skin_tone"] = "👩🏼‍🏫", - ["woman_teacher_tone3"] = "👩🏽‍🏫", - ["woman_teacher_medium_skin_tone"] = "👩🏽‍🏫", - ["woman_teacher_tone4"] = "👩🏾‍🏫", - ["woman_teacher_medium_dark_skin_tone"] = "👩🏾‍🏫", - ["woman_teacher_tone5"] = "👩🏿‍🏫", - ["woman_teacher_dark_skin_tone"] = "👩🏿‍🏫", - ["man_teacher"] = "👨‍🏫", - ["man_teacher_tone1"] = "👨🏻‍🏫", - ["man_teacher_light_skin_tone"] = "👨🏻‍🏫", - ["man_teacher_tone2"] = "👨🏼‍🏫", - ["man_teacher_medium_light_skin_tone"] = "👨🏼‍🏫", - ["man_teacher_tone3"] = "👨🏽‍🏫", - ["man_teacher_medium_skin_tone"] = "👨🏽‍🏫", - ["man_teacher_tone4"] = "👨🏾‍🏫", - ["man_teacher_medium_dark_skin_tone"] = "👨🏾‍🏫", - ["man_teacher_tone5"] = "👨🏿‍🏫", - ["man_teacher_dark_skin_tone"] = "👨🏿‍🏫", - ["factory_worker"] = "🧑‍🏭", - ["factory_worker_tone1"] = "🧑🏻‍🏭", - ["factory_worker_light_skin_tone"] = "🧑🏻‍🏭", - ["factory_worker_tone2"] = "🧑🏼‍🏭", - ["factory_worker_medium_light_skin_tone"] = "🧑🏼‍🏭", - ["factory_worker_tone3"] = "🧑🏽‍🏭", - ["factory_worker_medium_skin_tone"] = "🧑🏽‍🏭", - ["factory_worker_tone4"] = "🧑🏾‍🏭", - ["factory_worker_medium_dark_skin_tone"] = "🧑🏾‍🏭", - ["factory_worker_tone5"] = "🧑🏿‍🏭", - ["factory_worker_dark_skin_tone"] = "🧑🏿‍🏭", - ["woman_factory_worker"] = "👩‍🏭", - ["woman_factory_worker_tone1"] = "👩🏻‍🏭", - ["woman_factory_worker_light_skin_tone"] = "👩🏻‍🏭", - ["woman_factory_worker_tone2"] = "👩🏼‍🏭", - ["woman_factory_worker_medium_light_skin_tone"] = "👩🏼‍🏭", - ["woman_factory_worker_tone3"] = "👩🏽‍🏭", - ["woman_factory_worker_medium_skin_tone"] = "👩🏽‍🏭", - ["woman_factory_worker_tone4"] = "👩🏾‍🏭", - ["woman_factory_worker_medium_dark_skin_tone"] = "👩🏾‍🏭", - ["woman_factory_worker_tone5"] = "👩🏿‍🏭", - ["woman_factory_worker_dark_skin_tone"] = "👩🏿‍🏭", - ["man_factory_worker"] = "👨‍🏭", - ["man_factory_worker_tone1"] = "👨🏻‍🏭", - ["man_factory_worker_light_skin_tone"] = "👨🏻‍🏭", - ["man_factory_worker_tone2"] = "👨🏼‍🏭", - ["man_factory_worker_medium_light_skin_tone"] = "👨🏼‍🏭", - ["man_factory_worker_tone3"] = "👨🏽‍🏭", - ["man_factory_worker_medium_skin_tone"] = "👨🏽‍🏭", - ["man_factory_worker_tone4"] = "👨🏾‍🏭", - ["man_factory_worker_medium_dark_skin_tone"] = "👨🏾‍🏭", - ["man_factory_worker_tone5"] = "👨🏿‍🏭", - ["man_factory_worker_dark_skin_tone"] = "👨🏿‍🏭", - ["technologist"] = "🧑‍💻", - ["technologist_tone1"] = "🧑🏻‍💻", - ["technologist_light_skin_tone"] = "🧑🏻‍💻", - ["technologist_tone2"] = "🧑🏼‍💻", - ["technologist_medium_light_skin_tone"] = "🧑🏼‍💻", - ["technologist_tone3"] = "🧑🏽‍💻", - ["technologist_medium_skin_tone"] = "🧑🏽‍💻", - ["technologist_tone4"] = "🧑🏾‍💻", - ["technologist_medium_dark_skin_tone"] = "🧑🏾‍💻", - ["technologist_tone5"] = "🧑🏿‍💻", - ["technologist_dark_skin_tone"] = "🧑🏿‍💻", - ["woman_technologist"] = "👩‍💻", - ["woman_technologist_tone1"] = "👩🏻‍💻", - ["woman_technologist_light_skin_tone"] = "👩🏻‍💻", - ["woman_technologist_tone2"] = "👩🏼‍💻", - ["woman_technologist_medium_light_skin_tone"] = "👩🏼‍💻", - ["woman_technologist_tone3"] = "👩🏽‍💻", - ["woman_technologist_medium_skin_tone"] = "👩🏽‍💻", - ["woman_technologist_tone4"] = "👩🏾‍💻", - ["woman_technologist_medium_dark_skin_tone"] = "👩🏾‍💻", - ["woman_technologist_tone5"] = "👩🏿‍💻", - ["woman_technologist_dark_skin_tone"] = "👩🏿‍💻", - ["man_technologist"] = "👨‍💻", - ["man_technologist_tone1"] = "👨🏻‍💻", - ["man_technologist_light_skin_tone"] = "👨🏻‍💻", - ["man_technologist_tone2"] = "👨🏼‍💻", - ["man_technologist_medium_light_skin_tone"] = "👨🏼‍💻", - ["man_technologist_tone3"] = "👨🏽‍💻", - ["man_technologist_medium_skin_tone"] = "👨🏽‍💻", - ["man_technologist_tone4"] = "👨🏾‍💻", - ["man_technologist_medium_dark_skin_tone"] = "👨🏾‍💻", - ["man_technologist_tone5"] = "👨🏿‍💻", - ["man_technologist_dark_skin_tone"] = "👨🏿‍💻", - ["office_worker"] = "🧑‍💼", - ["office_worker_tone1"] = "🧑🏻‍💼", - ["office_worker_light_skin_tone"] = "🧑🏻‍💼", - ["office_worker_tone2"] = "🧑🏼‍💼", - ["office_worker_medium_light_skin_tone"] = "🧑🏼‍💼", - ["office_worker_tone3"] = "🧑🏽‍💼", - ["office_worker_medium_skin_tone"] = "🧑🏽‍💼", - ["office_worker_tone4"] = "🧑🏾‍💼", - ["office_worker_medium_dark_skin_tone"] = "🧑🏾‍💼", - ["office_worker_tone5"] = "🧑🏿‍💼", - ["office_worker_dark_skin_tone"] = "🧑🏿‍💼", - ["woman_office_worker"] = "👩‍💼", - ["woman_office_worker_tone1"] = "👩🏻‍💼", - ["woman_office_worker_light_skin_tone"] = "👩🏻‍💼", - ["woman_office_worker_tone2"] = "👩🏼‍💼", - ["woman_office_worker_medium_light_skin_tone"] = "👩🏼‍💼", - ["woman_office_worker_tone3"] = "👩🏽‍💼", - ["woman_office_worker_medium_skin_tone"] = "👩🏽‍💼", - ["woman_office_worker_tone4"] = "👩🏾‍💼", - ["woman_office_worker_medium_dark_skin_tone"] = "👩🏾‍💼", - ["woman_office_worker_tone5"] = "👩🏿‍💼", - ["woman_office_worker_dark_skin_tone"] = "👩🏿‍💼", - ["man_office_worker"] = "👨‍💼", - ["man_office_worker_tone1"] = "👨🏻‍💼", - ["man_office_worker_light_skin_tone"] = "👨🏻‍💼", - ["man_office_worker_tone2"] = "👨🏼‍💼", - ["man_office_worker_medium_light_skin_tone"] = "👨🏼‍💼", - ["man_office_worker_tone3"] = "👨🏽‍💼", - ["man_office_worker_medium_skin_tone"] = "👨🏽‍💼", - ["man_office_worker_tone4"] = "👨🏾‍💼", - ["man_office_worker_medium_dark_skin_tone"] = "👨🏾‍💼", - ["man_office_worker_tone5"] = "👨🏿‍💼", - ["man_office_worker_dark_skin_tone"] = "👨🏿‍💼", - ["mechanic"] = "🧑‍🔧", - ["mechanic_tone1"] = "🧑🏻‍🔧", - ["mechanic_light_skin_tone"] = "🧑🏻‍🔧", - ["mechanic_tone2"] = "🧑🏼‍🔧", - ["mechanic_medium_light_skin_tone"] = "🧑🏼‍🔧", - ["mechanic_tone3"] = "🧑🏽‍🔧", - ["mechanic_medium_skin_tone"] = "🧑🏽‍🔧", - ["mechanic_tone4"] = "🧑🏾‍🔧", - ["mechanic_medium_dark_skin_tone"] = "🧑🏾‍🔧", - ["mechanic_tone5"] = "🧑🏿‍🔧", - ["mechanic_dark_skin_tone"] = "🧑🏿‍🔧", - ["woman_mechanic"] = "👩‍🔧", - ["woman_mechanic_tone1"] = "👩🏻‍🔧", - ["woman_mechanic_light_skin_tone"] = "👩🏻‍🔧", - ["woman_mechanic_tone2"] = "👩🏼‍🔧", - ["woman_mechanic_medium_light_skin_tone"] = "👩🏼‍🔧", - ["woman_mechanic_tone3"] = "👩🏽‍🔧", - ["woman_mechanic_medium_skin_tone"] = "👩🏽‍🔧", - ["woman_mechanic_tone4"] = "👩🏾‍🔧", - ["woman_mechanic_medium_dark_skin_tone"] = "👩🏾‍🔧", - ["woman_mechanic_tone5"] = "👩🏿‍🔧", - ["woman_mechanic_dark_skin_tone"] = "👩🏿‍🔧", - ["man_mechanic"] = "👨‍🔧", - ["man_mechanic_tone1"] = "👨🏻‍🔧", - ["man_mechanic_light_skin_tone"] = "👨🏻‍🔧", - ["man_mechanic_tone2"] = "👨🏼‍🔧", - ["man_mechanic_medium_light_skin_tone"] = "👨🏼‍🔧", - ["man_mechanic_tone3"] = "👨🏽‍🔧", - ["man_mechanic_medium_skin_tone"] = "👨🏽‍🔧", - ["man_mechanic_tone4"] = "👨🏾‍🔧", - ["man_mechanic_medium_dark_skin_tone"] = "👨🏾‍🔧", - ["man_mechanic_tone5"] = "👨🏿‍🔧", - ["man_mechanic_dark_skin_tone"] = "👨🏿‍🔧", - ["scientist"] = "🧑‍🔬", - ["scientist_tone1"] = "🧑🏻‍🔬", - ["scientist_light_skin_tone"] = "🧑🏻‍🔬", - ["scientist_tone2"] = "🧑🏼‍🔬", - ["scientist_medium_light_skin_tone"] = "🧑🏼‍🔬", - ["scientist_tone3"] = "🧑🏽‍🔬", - ["scientist_medium_skin_tone"] = "🧑🏽‍🔬", - ["scientist_tone4"] = "🧑🏾‍🔬", - ["scientist_medium_dark_skin_tone"] = "🧑🏾‍🔬", - ["scientist_tone5"] = "🧑🏿‍🔬", - ["scientist_dark_skin_tone"] = "🧑🏿‍🔬", - ["woman_scientist"] = "👩‍🔬", - ["woman_scientist_tone1"] = "👩🏻‍🔬", - ["woman_scientist_light_skin_tone"] = "👩🏻‍🔬", - ["woman_scientist_tone2"] = "👩🏼‍🔬", - ["woman_scientist_medium_light_skin_tone"] = "👩🏼‍🔬", - ["woman_scientist_tone3"] = "👩🏽‍🔬", - ["woman_scientist_medium_skin_tone"] = "👩🏽‍🔬", - ["woman_scientist_tone4"] = "👩🏾‍🔬", - ["woman_scientist_medium_dark_skin_tone"] = "👩🏾‍🔬", - ["woman_scientist_tone5"] = "👩🏿‍🔬", - ["woman_scientist_dark_skin_tone"] = "👩🏿‍🔬", - ["man_scientist"] = "👨‍🔬", - ["man_scientist_tone1"] = "👨🏻‍🔬", - ["man_scientist_light_skin_tone"] = "👨🏻‍🔬", - ["man_scientist_tone2"] = "👨🏼‍🔬", - ["man_scientist_medium_light_skin_tone"] = "👨🏼‍🔬", - ["man_scientist_tone3"] = "👨🏽‍🔬", - ["man_scientist_medium_skin_tone"] = "👨🏽‍🔬", - ["man_scientist_tone4"] = "👨🏾‍🔬", - ["man_scientist_medium_dark_skin_tone"] = "👨🏾‍🔬", - ["man_scientist_tone5"] = "👨🏿‍🔬", - ["man_scientist_dark_skin_tone"] = "👨🏿‍🔬", - ["artist"] = "🧑‍🎨", - ["artist_tone1"] = "🧑🏻‍🎨", - ["artist_light_skin_tone"] = "🧑🏻‍🎨", - ["artist_tone2"] = "🧑🏼‍🎨", - ["artist_medium_light_skin_tone"] = "🧑🏼‍🎨", - ["artist_tone3"] = "🧑🏽‍🎨", - ["artist_medium_skin_tone"] = "🧑🏽‍🎨", - ["artist_tone4"] = "🧑🏾‍🎨", - ["artist_medium_dark_skin_tone"] = "🧑🏾‍🎨", - ["artist_tone5"] = "🧑🏿‍🎨", - ["artist_dark_skin_tone"] = "🧑🏿‍🎨", - ["woman_artist"] = "👩‍🎨", - ["woman_artist_tone1"] = "👩🏻‍🎨", - ["woman_artist_light_skin_tone"] = "👩🏻‍🎨", - ["woman_artist_tone2"] = "👩🏼‍🎨", - ["woman_artist_medium_light_skin_tone"] = "👩🏼‍🎨", - ["woman_artist_tone3"] = "👩🏽‍🎨", - ["woman_artist_medium_skin_tone"] = "👩🏽‍🎨", - ["woman_artist_tone4"] = "👩🏾‍🎨", - ["woman_artist_medium_dark_skin_tone"] = "👩🏾‍🎨", - ["woman_artist_tone5"] = "👩🏿‍🎨", - ["woman_artist_dark_skin_tone"] = "👩🏿‍🎨", - ["man_artist"] = "👨‍🎨", - ["man_artist_tone1"] = "👨🏻‍🎨", - ["man_artist_light_skin_tone"] = "👨🏻‍🎨", - ["man_artist_tone2"] = "👨🏼‍🎨", - ["man_artist_medium_light_skin_tone"] = "👨🏼‍🎨", - ["man_artist_tone3"] = "👨🏽‍🎨", - ["man_artist_medium_skin_tone"] = "👨🏽‍🎨", - ["man_artist_tone4"] = "👨🏾‍🎨", - ["man_artist_medium_dark_skin_tone"] = "👨🏾‍🎨", - ["man_artist_tone5"] = "👨🏿‍🎨", - ["man_artist_dark_skin_tone"] = "👨🏿‍🎨", - ["firefighter"] = "🧑‍🚒", - ["firefighter_tone1"] = "🧑🏻‍🚒", - ["firefighter_light_skin_tone"] = "🧑🏻‍🚒", - ["firefighter_tone2"] = "🧑🏼‍🚒", - ["firefighter_medium_light_skin_tone"] = "🧑🏼‍🚒", - ["firefighter_tone3"] = "🧑🏽‍🚒", - ["firefighter_medium_skin_tone"] = "🧑🏽‍🚒", - ["firefighter_tone4"] = "🧑🏾‍🚒", - ["firefighter_medium_dark_skin_tone"] = "🧑🏾‍🚒", - ["firefighter_tone5"] = "🧑🏿‍🚒", - ["firefighter_dark_skin_tone"] = "🧑🏿‍🚒", - ["woman_firefighter"] = "👩‍🚒", - ["woman_firefighter_tone1"] = "👩🏻‍🚒", - ["woman_firefighter_light_skin_tone"] = "👩🏻‍🚒", - ["woman_firefighter_tone2"] = "👩🏼‍🚒", - ["woman_firefighter_medium_light_skin_tone"] = "👩🏼‍🚒", - ["woman_firefighter_tone3"] = "👩🏽‍🚒", - ["woman_firefighter_medium_skin_tone"] = "👩🏽‍🚒", - ["woman_firefighter_tone4"] = "👩🏾‍🚒", - ["woman_firefighter_medium_dark_skin_tone"] = "👩🏾‍🚒", - ["woman_firefighter_tone5"] = "👩🏿‍🚒", - ["woman_firefighter_dark_skin_tone"] = "👩🏿‍🚒", - ["man_firefighter"] = "👨‍🚒", - ["man_firefighter_tone1"] = "👨🏻‍🚒", - ["man_firefighter_light_skin_tone"] = "👨🏻‍🚒", - ["man_firefighter_tone2"] = "👨🏼‍🚒", - ["man_firefighter_medium_light_skin_tone"] = "👨🏼‍🚒", - ["man_firefighter_tone3"] = "👨🏽‍🚒", - ["man_firefighter_medium_skin_tone"] = "👨🏽‍🚒", - ["man_firefighter_tone4"] = "👨🏾‍🚒", - ["man_firefighter_medium_dark_skin_tone"] = "👨🏾‍🚒", - ["man_firefighter_tone5"] = "👨🏿‍🚒", - ["man_firefighter_dark_skin_tone"] = "👨🏿‍🚒", - ["pilot"] = "🧑‍✈️", - ["pilot_tone1"] = "🧑🏻‍✈️", - ["pilot_light_skin_tone"] = "🧑🏻‍✈️", - ["pilot_tone2"] = "🧑🏼‍✈️", - ["pilot_medium_light_skin_tone"] = "🧑🏼‍✈️", - ["pilot_tone3"] = "🧑🏽‍✈️", - ["pilot_medium_skin_tone"] = "🧑🏽‍✈️", - ["pilot_tone4"] = "🧑🏾‍✈️", - ["pilot_medium_dark_skin_tone"] = "🧑🏾‍✈️", - ["pilot_tone5"] = "🧑🏿‍✈️", - ["pilot_dark_skin_tone"] = "🧑🏿‍✈️", - ["woman_pilot"] = "👩‍✈️", - ["woman_pilot_tone1"] = "👩🏻‍✈️", - ["woman_pilot_light_skin_tone"] = "👩🏻‍✈️", - ["woman_pilot_tone2"] = "👩🏼‍✈️", - ["woman_pilot_medium_light_skin_tone"] = "👩🏼‍✈️", - ["woman_pilot_tone3"] = "👩🏽‍✈️", - ["woman_pilot_medium_skin_tone"] = "👩🏽‍✈️", - ["woman_pilot_tone4"] = "👩🏾‍✈️", - ["woman_pilot_medium_dark_skin_tone"] = "👩🏾‍✈️", - ["woman_pilot_tone5"] = "👩🏿‍✈️", - ["woman_pilot_dark_skin_tone"] = "👩🏿‍✈️", - ["man_pilot"] = "👨‍✈️", - ["man_pilot_tone1"] = "👨🏻‍✈️", - ["man_pilot_light_skin_tone"] = "👨🏻‍✈️", - ["man_pilot_tone2"] = "👨🏼‍✈️", - ["man_pilot_medium_light_skin_tone"] = "👨🏼‍✈️", - ["man_pilot_tone3"] = "👨🏽‍✈️", - ["man_pilot_medium_skin_tone"] = "👨🏽‍✈️", - ["man_pilot_tone4"] = "👨🏾‍✈️", - ["man_pilot_medium_dark_skin_tone"] = "👨🏾‍✈️", - ["man_pilot_tone5"] = "👨🏿‍✈️", - ["man_pilot_dark_skin_tone"] = "👨🏿‍✈️", - ["astronaut"] = "🧑‍🚀", - ["astronaut_tone1"] = "🧑🏻‍🚀", - ["astronaut_light_skin_tone"] = "🧑🏻‍🚀", - ["astronaut_tone2"] = "🧑🏼‍🚀", - ["astronaut_medium_light_skin_tone"] = "🧑🏼‍🚀", - ["astronaut_tone3"] = "🧑🏽‍🚀", - ["astronaut_medium_skin_tone"] = "🧑🏽‍🚀", - ["astronaut_tone4"] = "🧑🏾‍🚀", - ["astronaut_medium_dark_skin_tone"] = "🧑🏾‍🚀", - ["astronaut_tone5"] = "🧑🏿‍🚀", - ["astronaut_dark_skin_tone"] = "🧑🏿‍🚀", - ["woman_astronaut"] = "👩‍🚀", - ["woman_astronaut_tone1"] = "👩🏻‍🚀", - ["woman_astronaut_light_skin_tone"] = "👩🏻‍🚀", - ["woman_astronaut_tone2"] = "👩🏼‍🚀", - ["woman_astronaut_medium_light_skin_tone"] = "👩🏼‍🚀", - ["woman_astronaut_tone3"] = "👩🏽‍🚀", - ["woman_astronaut_medium_skin_tone"] = "👩🏽‍🚀", - ["woman_astronaut_tone4"] = "👩🏾‍🚀", - ["woman_astronaut_medium_dark_skin_tone"] = "👩🏾‍🚀", - ["woman_astronaut_tone5"] = "👩🏿‍🚀", - ["woman_astronaut_dark_skin_tone"] = "👩🏿‍🚀", - ["man_astronaut"] = "👨‍🚀", - ["man_astronaut_tone1"] = "👨🏻‍🚀", - ["man_astronaut_light_skin_tone"] = "👨🏻‍🚀", - ["man_astronaut_tone2"] = "👨🏼‍🚀", - ["man_astronaut_medium_light_skin_tone"] = "👨🏼‍🚀", - ["man_astronaut_tone3"] = "👨🏽‍🚀", - ["man_astronaut_medium_skin_tone"] = "👨🏽‍🚀", - ["man_astronaut_tone4"] = "👨🏾‍🚀", - ["man_astronaut_medium_dark_skin_tone"] = "👨🏾‍🚀", - ["man_astronaut_tone5"] = "👨🏿‍🚀", - ["man_astronaut_dark_skin_tone"] = "👨🏿‍🚀", - ["judge"] = "🧑‍⚖️", - ["judge_tone1"] = "🧑🏻‍⚖️", - ["judge_light_skin_tone"] = "🧑🏻‍⚖️", - ["judge_tone2"] = "🧑🏼‍⚖️", - ["judge_medium_light_skin_tone"] = "🧑🏼‍⚖️", - ["judge_tone3"] = "🧑🏽‍⚖️", - ["judge_medium_skin_tone"] = "🧑🏽‍⚖️", - ["judge_tone4"] = "🧑🏾‍⚖️", - ["judge_medium_dark_skin_tone"] = "🧑🏾‍⚖️", - ["judge_tone5"] = "🧑🏿‍⚖️", - ["judge_dark_skin_tone"] = "🧑🏿‍⚖️", - ["woman_judge"] = "👩‍⚖️", - ["woman_judge_tone1"] = "👩🏻‍⚖️", - ["woman_judge_light_skin_tone"] = "👩🏻‍⚖️", - ["woman_judge_tone2"] = "👩🏼‍⚖️", - ["woman_judge_medium_light_skin_tone"] = "👩🏼‍⚖️", - ["woman_judge_tone3"] = "👩🏽‍⚖️", - ["woman_judge_medium_skin_tone"] = "👩🏽‍⚖️", - ["woman_judge_tone4"] = "👩🏾‍⚖️", - ["woman_judge_medium_dark_skin_tone"] = "👩🏾‍⚖️", - ["woman_judge_tone5"] = "👩🏿‍⚖️", - ["woman_judge_dark_skin_tone"] = "👩🏿‍⚖️", - ["man_judge"] = "👨‍⚖️", - ["man_judge_tone1"] = "👨🏻‍⚖️", - ["man_judge_light_skin_tone"] = "👨🏻‍⚖️", - ["man_judge_tone2"] = "👨🏼‍⚖️", - ["man_judge_medium_light_skin_tone"] = "👨🏼‍⚖️", - ["man_judge_tone3"] = "👨🏽‍⚖️", - ["man_judge_medium_skin_tone"] = "👨🏽‍⚖️", - ["man_judge_tone4"] = "👨🏾‍⚖️", - ["man_judge_medium_dark_skin_tone"] = "👨🏾‍⚖️", - ["man_judge_tone5"] = "👨🏿‍⚖️", - ["man_judge_dark_skin_tone"] = "👨🏿‍⚖️", - ["person_with_veil"] = "👰", - ["person_with_veil_tone1"] = "👰🏻", - ["person_with_veil_tone2"] = "👰🏼", - ["person_with_veil_tone3"] = "👰🏽", - ["person_with_veil_tone4"] = "👰🏾", - ["person_with_veil_tone5"] = "👰🏿", - ["woman_with_veil"] = "👰‍♀️", - ["bride_with_veil"] = "👰‍♀️", - ["woman_with_veil_tone1"] = "👰🏻‍♀️", - ["woman_with_veil_light_skin_tone"] = "👰🏻‍♀️", - ["woman_with_veil_tone2"] = "👰🏼‍♀️", - ["woman_with_veil_medium_light_skin_tone"] = "👰🏼‍♀️", - ["woman_with_veil_tone3"] = "👰🏽‍♀️", - ["woman_with_veil_medium_skin_tone"] = "👰🏽‍♀️", - ["woman_with_veil_tone4"] = "👰🏾‍♀️", - ["woman_with_veil_medium_dark_skin_tone"] = "👰🏾‍♀️", - ["woman_with_veil_tone5"] = "👰🏿‍♀️", - ["woman_with_veil_dark_skin_tone"] = "👰🏿‍♀️", - ["man_with_veil"] = "👰‍♂️", - ["man_with_veil_tone1"] = "👰🏻‍♂️", - ["man_with_veil_light_skin_tone"] = "👰🏻‍♂️", - ["man_with_veil_tone2"] = "👰🏼‍♂️", - ["man_with_veil_medium_light_skin_tone"] = "👰🏼‍♂️", - ["man_with_veil_tone3"] = "👰🏽‍♂️", - ["man_with_veil_medium_skin_tone"] = "👰🏽‍♂️", - ["man_with_veil_tone4"] = "👰🏾‍♂️", - ["man_with_veil_medium_dark_skin_tone"] = "👰🏾‍♂️", - ["man_with_veil_tone5"] = "👰🏿‍♂️", - ["man_with_veil_dark_skin_tone"] = "👰🏿‍♂️", - ["person_in_tuxedo"] = "🤵", - ["person_in_tuxedo_tone1"] = "🤵🏻", - ["tuxedo_tone1"] = "🤵🏻", - ["person_in_tuxedo_tone2"] = "🤵🏼", - ["tuxedo_tone2"] = "🤵🏼", - ["person_in_tuxedo_tone3"] = "🤵🏽", - ["tuxedo_tone3"] = "🤵🏽", - ["person_in_tuxedo_tone4"] = "🤵🏾", - ["tuxedo_tone4"] = "🤵🏾", - ["person_in_tuxedo_tone5"] = "🤵🏿", - ["tuxedo_tone5"] = "🤵🏿", - ["woman_in_tuxedo"] = "🤵‍♀️", - ["woman_in_tuxedo_tone1"] = "🤵🏻‍♀️", - ["woman_in_tuxedo_light_skin_tone"] = "🤵🏻‍♀️", - ["woman_in_tuxedo_tone2"] = "🤵🏼‍♀️", - ["woman_in_tuxedo_medium_light_skin_tone"] = "🤵🏼‍♀️", - ["woman_in_tuxedo_tone3"] = "🤵🏽‍♀️", - ["woman_in_tuxedo_medium_skin_tone"] = "🤵🏽‍♀️", - ["woman_in_tuxedo_tone4"] = "🤵🏾‍♀️", - ["woman_in_tuxedo_medium_dark_skin_tone"] = "🤵🏾‍♀️", - ["woman_in_tuxedo_tone5"] = "🤵🏿‍♀️", - ["woman_in_tuxedo_dark_skin_tone"] = "🤵🏿‍♀️", - ["man_in_tuxedo"] = "🤵‍♂️", - ["man_in_tuxedo_tone1"] = "🤵🏻‍♂️", - ["man_in_tuxedo_light_skin_tone"] = "🤵🏻‍♂️", - ["man_in_tuxedo_tone2"] = "🤵🏼‍♂️", - ["man_in_tuxedo_medium_light_skin_tone"] = "🤵🏼‍♂️", - ["man_in_tuxedo_tone3"] = "🤵🏽‍♂️", - ["man_in_tuxedo_medium_skin_tone"] = "🤵🏽‍♂️", - ["man_in_tuxedo_tone4"] = "🤵🏾‍♂️", - ["man_in_tuxedo_medium_dark_skin_tone"] = "🤵🏾‍♂️", - ["man_in_tuxedo_tone5"] = "🤵🏿‍♂️", - ["man_in_tuxedo_dark_skin_tone"] = "🤵🏿‍♂️", - ["princess"] = "👸", - ["princess_tone1"] = "👸🏻", - ["princess_tone2"] = "👸🏼", - ["princess_tone3"] = "👸🏽", - ["princess_tone4"] = "👸🏾", - ["princess_tone5"] = "👸🏿", - ["prince"] = "🤴", - ["prince_tone1"] = "🤴🏻", - ["prince_tone2"] = "🤴🏼", - ["prince_tone3"] = "🤴🏽", - ["prince_tone4"] = "🤴🏾", - ["prince_tone5"] = "🤴🏿", - ["superhero"] = "🦸", - ["superhero_tone1"] = "🦸🏻", - ["superhero_light_skin_tone"] = "🦸🏻", - ["superhero_tone2"] = "🦸🏼", - ["superhero_medium_light_skin_tone"] = "🦸🏼", - ["superhero_tone3"] = "🦸🏽", - ["superhero_medium_skin_tone"] = "🦸🏽", - ["superhero_tone4"] = "🦸🏾", - ["superhero_medium_dark_skin_tone"] = "🦸🏾", - ["superhero_tone5"] = "🦸🏿", - ["superhero_dark_skin_tone"] = "🦸🏿", - ["woman_superhero"] = "🦸‍♀️", - ["woman_superhero_tone1"] = "🦸🏻‍♀️", - ["woman_superhero_light_skin_tone"] = "🦸🏻‍♀️", - ["woman_superhero_tone2"] = "🦸🏼‍♀️", - ["woman_superhero_medium_light_skin_tone"] = "🦸🏼‍♀️", - ["woman_superhero_tone3"] = "🦸🏽‍♀️", - ["woman_superhero_medium_skin_tone"] = "🦸🏽‍♀️", - ["woman_superhero_tone4"] = "🦸🏾‍♀️", - ["woman_superhero_medium_dark_skin_tone"] = "🦸🏾‍♀️", - ["woman_superhero_tone5"] = "🦸🏿‍♀️", - ["woman_superhero_dark_skin_tone"] = "🦸🏿‍♀️", - ["man_superhero"] = "🦸‍♂️", - ["man_superhero_tone1"] = "🦸🏻‍♂️", - ["man_superhero_light_skin_tone"] = "🦸🏻‍♂️", - ["man_superhero_tone2"] = "🦸🏼‍♂️", - ["man_superhero_medium_light_skin_tone"] = "🦸🏼‍♂️", - ["man_superhero_tone3"] = "🦸🏽‍♂️", - ["man_superhero_medium_skin_tone"] = "🦸🏽‍♂️", - ["man_superhero_tone4"] = "🦸🏾‍♂️", - ["man_superhero_medium_dark_skin_tone"] = "🦸🏾‍♂️", - ["man_superhero_tone5"] = "🦸🏿‍♂️", - ["man_superhero_dark_skin_tone"] = "🦸🏿‍♂️", - ["supervillain"] = "🦹", - ["supervillain_tone1"] = "🦹🏻", - ["supervillain_light_skin_tone"] = "🦹🏻", - ["supervillain_tone2"] = "🦹🏼", - ["supervillain_medium_light_skin_tone"] = "🦹🏼", - ["supervillain_tone3"] = "🦹🏽", - ["supervillain_medium_skin_tone"] = "🦹🏽", - ["supervillain_tone4"] = "🦹🏾", - ["supervillain_medium_dark_skin_tone"] = "🦹🏾", - ["supervillain_tone5"] = "🦹🏿", - ["supervillain_dark_skin_tone"] = "🦹🏿", - ["woman_supervillain"] = "🦹‍♀️", - ["woman_supervillain_tone1"] = "🦹🏻‍♀️", - ["woman_supervillain_light_skin_tone"] = "🦹🏻‍♀️", - ["woman_supervillain_tone2"] = "🦹🏼‍♀️", - ["woman_supervillain_medium_light_skin_tone"] = "🦹🏼‍♀️", - ["woman_supervillain_tone3"] = "🦹🏽‍♀️", - ["woman_supervillain_medium_skin_tone"] = "🦹🏽‍♀️", - ["woman_supervillain_tone4"] = "🦹🏾‍♀️", - ["woman_supervillain_medium_dark_skin_tone"] = "🦹🏾‍♀️", - ["woman_supervillain_tone5"] = "🦹🏿‍♀️", - ["woman_supervillain_dark_skin_tone"] = "🦹🏿‍♀️", - ["man_supervillain"] = "🦹‍♂️", - ["man_supervillain_tone1"] = "🦹🏻‍♂️", - ["man_supervillain_light_skin_tone"] = "🦹🏻‍♂️", - ["man_supervillain_tone2"] = "🦹🏼‍♂️", - ["man_supervillain_medium_light_skin_tone"] = "🦹🏼‍♂️", - ["man_supervillain_tone3"] = "🦹🏽‍♂️", - ["man_supervillain_medium_skin_tone"] = "🦹🏽‍♂️", - ["man_supervillain_tone4"] = "🦹🏾‍♂️", - ["man_supervillain_medium_dark_skin_tone"] = "🦹🏾‍♂️", - ["man_supervillain_tone5"] = "🦹🏿‍♂️", - ["man_supervillain_dark_skin_tone"] = "🦹🏿‍♂️", - ["ninja"] = "🥷", - ["ninja_tone1"] = "🥷🏻", - ["ninja_light_skin_tone"] = "🥷🏻", - ["ninja_tone2"] = "🥷🏼", - ["ninja_medium_light_skin_tone"] = "🥷🏼", - ["ninja_tone3"] = "🥷🏽", - ["ninja_medium_skin_tone"] = "🥷🏽", - ["ninja_tone4"] = "🥷🏾", - ["ninja_medium_dark_skin_tone"] = "🥷🏾", - ["ninja_tone5"] = "🥷🏿", - ["ninja_dark_skin_tone"] = "🥷🏿", - ["mx_claus"] = "🧑‍🎄", - ["mx_claus_tone1"] = "🧑🏻‍🎄", - ["mx_claus_light_skin_tone"] = "🧑🏻‍🎄", - ["mx_claus_tone2"] = "🧑🏼‍🎄", - ["mx_claus_medium_light_skin_tone"] = "🧑🏼‍🎄", - ["mx_claus_tone3"] = "🧑🏽‍🎄", - ["mx_claus_medium_skin_tone"] = "🧑🏽‍🎄", - ["mx_claus_tone4"] = "🧑🏾‍🎄", - ["mx_claus_medium_dark_skin_tone"] = "🧑🏾‍🎄", - ["mx_claus_tone5"] = "🧑🏿‍🎄", - ["mx_claus_dark_skin_tone"] = "🧑🏿‍🎄", - ["mrs_claus"] = "🤶", - ["mother_christmas"] = "🤶", - ["mrs_claus_tone1"] = "🤶🏻", - ["mother_christmas_tone1"] = "🤶🏻", - ["mrs_claus_tone2"] = "🤶🏼", - ["mother_christmas_tone2"] = "🤶🏼", - ["mrs_claus_tone3"] = "🤶🏽", - ["mother_christmas_tone3"] = "🤶🏽", - ["mrs_claus_tone4"] = "🤶🏾", - ["mother_christmas_tone4"] = "🤶🏾", - ["mrs_claus_tone5"] = "🤶🏿", - ["mother_christmas_tone5"] = "🤶🏿", - ["santa"] = "🎅", - ["santa_tone1"] = "🎅🏻", - ["santa_tone2"] = "🎅🏼", - ["santa_tone3"] = "🎅🏽", - ["santa_tone4"] = "🎅🏾", - ["santa_tone5"] = "🎅🏿", - ["mage"] = "🧙", - ["mage_tone1"] = "🧙🏻", - ["mage_light_skin_tone"] = "🧙🏻", - ["mage_tone2"] = "🧙🏼", - ["mage_medium_light_skin_tone"] = "🧙🏼", - ["mage_tone3"] = "🧙🏽", - ["mage_medium_skin_tone"] = "🧙🏽", - ["mage_tone4"] = "🧙🏾", - ["mage_medium_dark_skin_tone"] = "🧙🏾", - ["mage_tone5"] = "🧙🏿", - ["mage_dark_skin_tone"] = "🧙🏿", - ["woman_mage"] = "🧙‍♀️", - ["woman_mage_tone1"] = "🧙🏻‍♀️", - ["woman_mage_light_skin_tone"] = "🧙🏻‍♀️", - ["woman_mage_tone2"] = "🧙🏼‍♀️", - ["woman_mage_medium_light_skin_tone"] = "🧙🏼‍♀️", - ["woman_mage_tone3"] = "🧙🏽‍♀️", - ["woman_mage_medium_skin_tone"] = "🧙🏽‍♀️", - ["woman_mage_tone4"] = "🧙🏾‍♀️", - ["woman_mage_medium_dark_skin_tone"] = "🧙🏾‍♀️", - ["woman_mage_tone5"] = "🧙🏿‍♀️", - ["woman_mage_dark_skin_tone"] = "🧙🏿‍♀️", - ["man_mage"] = "🧙‍♂️", - ["man_mage_tone1"] = "🧙🏻‍♂️", - ["man_mage_light_skin_tone"] = "🧙🏻‍♂️", - ["man_mage_tone2"] = "🧙🏼‍♂️", - ["man_mage_medium_light_skin_tone"] = "🧙🏼‍♂️", - ["man_mage_tone3"] = "🧙🏽‍♂️", - ["man_mage_medium_skin_tone"] = "🧙🏽‍♂️", - ["man_mage_tone4"] = "🧙🏾‍♂️", - ["man_mage_medium_dark_skin_tone"] = "🧙🏾‍♂️", - ["man_mage_tone5"] = "🧙🏿‍♂️", - ["man_mage_dark_skin_tone"] = "🧙🏿‍♂️", - ["elf"] = "🧝", - ["elf_tone1"] = "🧝🏻", - ["elf_light_skin_tone"] = "🧝🏻", - ["elf_tone2"] = "🧝🏼", - ["elf_medium_light_skin_tone"] = "🧝🏼", - ["elf_tone3"] = "🧝🏽", - ["elf_medium_skin_tone"] = "🧝🏽", - ["elf_tone4"] = "🧝🏾", - ["elf_medium_dark_skin_tone"] = "🧝🏾", - ["elf_tone5"] = "🧝🏿", - ["elf_dark_skin_tone"] = "🧝🏿", - ["woman_elf"] = "🧝‍♀️", - ["woman_elf_tone1"] = "🧝🏻‍♀️", - ["woman_elf_light_skin_tone"] = "🧝🏻‍♀️", - ["woman_elf_tone2"] = "🧝🏼‍♀️", - ["woman_elf_medium_light_skin_tone"] = "🧝🏼‍♀️", - ["woman_elf_tone3"] = "🧝🏽‍♀️", - ["woman_elf_medium_skin_tone"] = "🧝🏽‍♀️", - ["woman_elf_tone4"] = "🧝🏾‍♀️", - ["woman_elf_medium_dark_skin_tone"] = "🧝🏾‍♀️", - ["woman_elf_tone5"] = "🧝🏿‍♀️", - ["woman_elf_dark_skin_tone"] = "🧝🏿‍♀️", - ["man_elf"] = "🧝‍♂️", - ["man_elf_tone1"] = "🧝🏻‍♂️", - ["man_elf_light_skin_tone"] = "🧝🏻‍♂️", - ["man_elf_tone2"] = "🧝🏼‍♂️", - ["man_elf_medium_light_skin_tone"] = "🧝🏼‍♂️", - ["man_elf_tone3"] = "🧝🏽‍♂️", - ["man_elf_medium_skin_tone"] = "🧝🏽‍♂️", - ["man_elf_tone4"] = "🧝🏾‍♂️", - ["man_elf_medium_dark_skin_tone"] = "🧝🏾‍♂️", - ["man_elf_tone5"] = "🧝🏿‍♂️", - ["man_elf_dark_skin_tone"] = "🧝🏿‍♂️", - ["vampire"] = "🧛", - ["vampire_tone1"] = "🧛🏻", - ["vampire_light_skin_tone"] = "🧛🏻", - ["vampire_tone2"] = "🧛🏼", - ["vampire_medium_light_skin_tone"] = "🧛🏼", - ["vampire_tone3"] = "🧛🏽", - ["vampire_medium_skin_tone"] = "🧛🏽", - ["vampire_tone4"] = "🧛🏾", - ["vampire_medium_dark_skin_tone"] = "🧛🏾", - ["vampire_tone5"] = "🧛🏿", - ["vampire_dark_skin_tone"] = "🧛🏿", - ["woman_vampire"] = "🧛‍♀️", - ["woman_vampire_tone1"] = "🧛🏻‍♀️", - ["woman_vampire_light_skin_tone"] = "🧛🏻‍♀️", - ["woman_vampire_tone2"] = "🧛🏼‍♀️", - ["woman_vampire_medium_light_skin_tone"] = "🧛🏼‍♀️", - ["woman_vampire_tone3"] = "🧛🏽‍♀️", - ["woman_vampire_medium_skin_tone"] = "🧛🏽‍♀️", - ["woman_vampire_tone4"] = "🧛🏾‍♀️", - ["woman_vampire_medium_dark_skin_tone"] = "🧛🏾‍♀️", - ["woman_vampire_tone5"] = "🧛🏿‍♀️", - ["woman_vampire_dark_skin_tone"] = "🧛🏿‍♀️", - ["man_vampire"] = "🧛‍♂️", - ["man_vampire_tone1"] = "🧛🏻‍♂️", - ["man_vampire_light_skin_tone"] = "🧛🏻‍♂️", - ["man_vampire_tone2"] = "🧛🏼‍♂️", - ["man_vampire_medium_light_skin_tone"] = "🧛🏼‍♂️", - ["man_vampire_tone3"] = "🧛🏽‍♂️", - ["man_vampire_medium_skin_tone"] = "🧛🏽‍♂️", - ["man_vampire_tone4"] = "🧛🏾‍♂️", - ["man_vampire_medium_dark_skin_tone"] = "🧛🏾‍♂️", - ["man_vampire_tone5"] = "🧛🏿‍♂️", - ["man_vampire_dark_skin_tone"] = "🧛🏿‍♂️", - ["zombie"] = "🧟", - ["woman_zombie"] = "🧟‍♀️", - ["man_zombie"] = "🧟‍♂️", - ["genie"] = "🧞", - ["woman_genie"] = "🧞‍♀️", - ["man_genie"] = "🧞‍♂️", - ["merperson"] = "🧜", - ["merperson_tone1"] = "🧜🏻", - ["merperson_light_skin_tone"] = "🧜🏻", - ["merperson_tone2"] = "🧜🏼", - ["merperson_medium_light_skin_tone"] = "🧜🏼", - ["merperson_tone3"] = "🧜🏽", - ["merperson_medium_skin_tone"] = "🧜🏽", - ["merperson_tone4"] = "🧜🏾", - ["merperson_medium_dark_skin_tone"] = "🧜🏾", - ["merperson_tone5"] = "🧜🏿", - ["merperson_dark_skin_tone"] = "🧜🏿", - ["mermaid"] = "🧜‍♀️", - ["mermaid_tone1"] = "🧜🏻‍♀️", - ["mermaid_light_skin_tone"] = "🧜🏻‍♀️", - ["mermaid_tone2"] = "🧜🏼‍♀️", - ["mermaid_medium_light_skin_tone"] = "🧜🏼‍♀️", - ["mermaid_tone3"] = "🧜🏽‍♀️", - ["mermaid_medium_skin_tone"] = "🧜🏽‍♀️", - ["mermaid_tone4"] = "🧜🏾‍♀️", - ["mermaid_medium_dark_skin_tone"] = "🧜🏾‍♀️", - ["mermaid_tone5"] = "🧜🏿‍♀️", - ["mermaid_dark_skin_tone"] = "🧜🏿‍♀️", - ["merman"] = "🧜‍♂️", - ["merman_tone1"] = "🧜🏻‍♂️", - ["merman_light_skin_tone"] = "🧜🏻‍♂️", - ["merman_tone2"] = "🧜🏼‍♂️", - ["merman_medium_light_skin_tone"] = "🧜🏼‍♂️", - ["merman_tone3"] = "🧜🏽‍♂️", - ["merman_medium_skin_tone"] = "🧜🏽‍♂️", - ["merman_tone4"] = "🧜🏾‍♂️", - ["merman_medium_dark_skin_tone"] = "🧜🏾‍♂️", - ["merman_tone5"] = "🧜🏿‍♂️", - ["merman_dark_skin_tone"] = "🧜🏿‍♂️", - ["fairy"] = "🧚", - ["fairy_tone1"] = "🧚🏻", - ["fairy_light_skin_tone"] = "🧚🏻", - ["fairy_tone2"] = "🧚🏼", - ["fairy_medium_light_skin_tone"] = "🧚🏼", - ["fairy_tone3"] = "🧚🏽", - ["fairy_medium_skin_tone"] = "🧚🏽", - ["fairy_tone4"] = "🧚🏾", - ["fairy_medium_dark_skin_tone"] = "🧚🏾", - ["fairy_tone5"] = "🧚🏿", - ["fairy_dark_skin_tone"] = "🧚🏿", - ["woman_fairy"] = "🧚‍♀️", - ["woman_fairy_tone1"] = "🧚🏻‍♀️", - ["woman_fairy_light_skin_tone"] = "🧚🏻‍♀️", - ["woman_fairy_tone2"] = "🧚🏼‍♀️", - ["woman_fairy_medium_light_skin_tone"] = "🧚🏼‍♀️", - ["woman_fairy_tone3"] = "🧚🏽‍♀️", - ["woman_fairy_medium_skin_tone"] = "🧚🏽‍♀️", - ["woman_fairy_tone4"] = "🧚🏾‍♀️", - ["woman_fairy_medium_dark_skin_tone"] = "🧚🏾‍♀️", - ["woman_fairy_tone5"] = "🧚🏿‍♀️", - ["woman_fairy_dark_skin_tone"] = "🧚🏿‍♀️", - ["man_fairy"] = "🧚‍♂️", - ["man_fairy_tone1"] = "🧚🏻‍♂️", - ["man_fairy_light_skin_tone"] = "🧚🏻‍♂️", - ["man_fairy_tone2"] = "🧚🏼‍♂️", - ["man_fairy_medium_light_skin_tone"] = "🧚🏼‍♂️", - ["man_fairy_tone3"] = "🧚🏽‍♂️", - ["man_fairy_medium_skin_tone"] = "🧚🏽‍♂️", - ["man_fairy_tone4"] = "🧚🏾‍♂️", - ["man_fairy_medium_dark_skin_tone"] = "🧚🏾‍♂️", - ["man_fairy_tone5"] = "🧚🏿‍♂️", - ["man_fairy_dark_skin_tone"] = "🧚🏿‍♂️", - ["angel"] = "👼", - ["angel_tone1"] = "👼🏻", - ["angel_tone2"] = "👼🏼", - ["angel_tone3"] = "👼🏽", - ["angel_tone4"] = "👼🏾", - ["angel_tone5"] = "👼🏿", - ["pregnant_woman"] = "🤰", - ["expecting_woman"] = "🤰", - ["pregnant_woman_tone1"] = "🤰🏻", - ["expecting_woman_tone1"] = "🤰🏻", - ["pregnant_woman_tone2"] = "🤰🏼", - ["expecting_woman_tone2"] = "🤰🏼", - ["pregnant_woman_tone3"] = "🤰🏽", - ["expecting_woman_tone3"] = "🤰🏽", - ["pregnant_woman_tone4"] = "🤰🏾", - ["expecting_woman_tone4"] = "🤰🏾", - ["pregnant_woman_tone5"] = "🤰🏿", - ["expecting_woman_tone5"] = "🤰🏿", - ["breast_feeding"] = "🤱", - ["breast_feeding_tone1"] = "🤱🏻", - ["breast_feeding_light_skin_tone"] = "🤱🏻", - ["breast_feeding_tone2"] = "🤱🏼", - ["breast_feeding_medium_light_skin_tone"] = "🤱🏼", - ["breast_feeding_tone3"] = "🤱🏽", - ["breast_feeding_medium_skin_tone"] = "🤱🏽", - ["breast_feeding_tone4"] = "🤱🏾", - ["breast_feeding_medium_dark_skin_tone"] = "🤱🏾", - ["breast_feeding_tone5"] = "🤱🏿", - ["breast_feeding_dark_skin_tone"] = "🤱🏿", - ["person_feeding_baby"] = "🧑‍🍼", - ["person_feeding_baby_tone1"] = "🧑🏻‍🍼", - ["person_feeding_baby_light_skin_tone"] = "🧑🏻‍🍼", - ["person_feeding_baby_tone2"] = "🧑🏼‍🍼", - ["person_feeding_baby_medium_light_skin_tone"] = "🧑🏼‍🍼", - ["person_feeding_baby_tone3"] = "🧑🏽‍🍼", - ["person_feeding_baby_medium_skin_tone"] = "🧑🏽‍🍼", - ["person_feeding_baby_tone4"] = "🧑🏾‍🍼", - ["person_feeding_baby_medium_dark_skin_tone"] = "🧑🏾‍🍼", - ["person_feeding_baby_tone5"] = "🧑🏿‍🍼", - ["person_feeding_baby_dark_skin_tone"] = "🧑🏿‍🍼", - ["woman_feeding_baby"] = "👩‍🍼", - ["woman_feeding_baby_tone1"] = "👩🏻‍🍼", - ["woman_feeding_baby_light_skin_tone"] = "👩🏻‍🍼", - ["woman_feeding_baby_tone2"] = "👩🏼‍🍼", - ["woman_feeding_baby_medium_light_skin_tone"] = "👩🏼‍🍼", - ["woman_feeding_baby_tone3"] = "👩🏽‍🍼", - ["woman_feeding_baby_medium_skin_tone"] = "👩🏽‍🍼", - ["woman_feeding_baby_tone4"] = "👩🏾‍🍼", - ["woman_feeding_baby_medium_dark_skin_tone"] = "👩🏾‍🍼", - ["woman_feeding_baby_tone5"] = "👩🏿‍🍼", - ["woman_feeding_baby_dark_skin_tone"] = "👩🏿‍🍼", - ["man_feeding_baby"] = "👨‍🍼", - ["man_feeding_baby_tone1"] = "👨🏻‍🍼", - ["man_feeding_baby_light_skin_tone"] = "👨🏻‍🍼", - ["man_feeding_baby_tone2"] = "👨🏼‍🍼", - ["man_feeding_baby_medium_light_skin_tone"] = "👨🏼‍🍼", - ["man_feeding_baby_tone3"] = "👨🏽‍🍼", - ["man_feeding_baby_medium_skin_tone"] = "👨🏽‍🍼", - ["man_feeding_baby_tone4"] = "👨🏾‍🍼", - ["man_feeding_baby_medium_dark_skin_tone"] = "👨🏾‍🍼", - ["man_feeding_baby_tone5"] = "👨🏿‍🍼", - ["man_feeding_baby_dark_skin_tone"] = "👨🏿‍🍼", - ["person_bowing"] = "🙇", - ["bow"] = "🙇", - ["person_bowing_tone1"] = "🙇🏻", - ["bow_tone1"] = "🙇🏻", - ["person_bowing_tone2"] = "🙇🏼", - ["bow_tone2"] = "🙇🏼", - ["person_bowing_tone3"] = "🙇🏽", - ["bow_tone3"] = "🙇🏽", - ["person_bowing_tone4"] = "🙇🏾", - ["bow_tone4"] = "🙇🏾", - ["person_bowing_tone5"] = "🙇🏿", - ["bow_tone5"] = "🙇🏿", - ["woman_bowing"] = "🙇‍♀️", - ["woman_bowing_tone1"] = "🙇🏻‍♀️", - ["woman_bowing_light_skin_tone"] = "🙇🏻‍♀️", - ["woman_bowing_tone2"] = "🙇🏼‍♀️", - ["woman_bowing_medium_light_skin_tone"] = "🙇🏼‍♀️", - ["woman_bowing_tone3"] = "🙇🏽‍♀️", - ["woman_bowing_medium_skin_tone"] = "🙇🏽‍♀️", - ["woman_bowing_tone4"] = "🙇🏾‍♀️", - ["woman_bowing_medium_dark_skin_tone"] = "🙇🏾‍♀️", - ["woman_bowing_tone5"] = "🙇🏿‍♀️", - ["woman_bowing_dark_skin_tone"] = "🙇🏿‍♀️", - ["man_bowing"] = "🙇‍♂️", - ["man_bowing_tone1"] = "🙇🏻‍♂️", - ["man_bowing_light_skin_tone"] = "🙇🏻‍♂️", - ["man_bowing_tone2"] = "🙇🏼‍♂️", - ["man_bowing_medium_light_skin_tone"] = "🙇🏼‍♂️", - ["man_bowing_tone3"] = "🙇🏽‍♂️", - ["man_bowing_medium_skin_tone"] = "🙇🏽‍♂️", - ["man_bowing_tone4"] = "🙇🏾‍♂️", - ["man_bowing_medium_dark_skin_tone"] = "🙇🏾‍♂️", - ["man_bowing_tone5"] = "🙇🏿‍♂️", - ["man_bowing_dark_skin_tone"] = "🙇🏿‍♂️", - ["person_tipping_hand"] = "💁", - ["information_desk_person"] = "💁", - ["person_tipping_hand_tone1"] = "💁🏻", - ["information_desk_person_tone1"] = "💁🏻", - ["person_tipping_hand_tone2"] = "💁🏼", - ["information_desk_person_tone2"] = "💁🏼", - ["person_tipping_hand_tone3"] = "💁🏽", - ["information_desk_person_tone3"] = "💁🏽", - ["person_tipping_hand_tone4"] = "💁🏾", - ["information_desk_person_tone4"] = "💁🏾", - ["person_tipping_hand_tone5"] = "💁🏿", - ["information_desk_person_tone5"] = "💁🏿", - ["woman_tipping_hand"] = "💁‍♀️", - ["woman_tipping_hand_tone1"] = "💁🏻‍♀️", - ["woman_tipping_hand_light_skin_tone"] = "💁🏻‍♀️", - ["woman_tipping_hand_tone2"] = "💁🏼‍♀️", - ["woman_tipping_hand_medium_light_skin_tone"] = "💁🏼‍♀️", - ["woman_tipping_hand_tone3"] = "💁🏽‍♀️", - ["woman_tipping_hand_medium_skin_tone"] = "💁🏽‍♀️", - ["woman_tipping_hand_tone4"] = "💁🏾‍♀️", - ["woman_tipping_hand_medium_dark_skin_tone"] = "💁🏾‍♀️", - ["woman_tipping_hand_tone5"] = "💁🏿‍♀️", - ["woman_tipping_hand_dark_skin_tone"] = "💁🏿‍♀️", - ["man_tipping_hand"] = "💁‍♂️", - ["man_tipping_hand_tone1"] = "💁🏻‍♂️", - ["man_tipping_hand_light_skin_tone"] = "💁🏻‍♂️", - ["man_tipping_hand_tone2"] = "💁🏼‍♂️", - ["man_tipping_hand_medium_light_skin_tone"] = "💁🏼‍♂️", - ["man_tipping_hand_tone3"] = "💁🏽‍♂️", - ["man_tipping_hand_medium_skin_tone"] = "💁🏽‍♂️", - ["man_tipping_hand_tone4"] = "💁🏾‍♂️", - ["man_tipping_hand_medium_dark_skin_tone"] = "💁🏾‍♂️", - ["man_tipping_hand_tone5"] = "💁🏿‍♂️", - ["man_tipping_hand_dark_skin_tone"] = "💁🏿‍♂️", - ["person_gesturing_no"] = "🙅", - ["no_good"] = "🙅", - ["person_gesturing_no_tone1"] = "🙅🏻", - ["no_good_tone1"] = "🙅🏻", - ["person_gesturing_no_tone2"] = "🙅🏼", - ["no_good_tone2"] = "🙅🏼", - ["person_gesturing_no_tone3"] = "🙅🏽", - ["no_good_tone3"] = "🙅🏽", - ["person_gesturing_no_tone4"] = "🙅🏾", - ["no_good_tone4"] = "🙅🏾", - ["person_gesturing_no_tone5"] = "🙅🏿", - ["no_good_tone5"] = "🙅🏿", - ["woman_gesturing_no"] = "🙅‍♀️", - ["woman_gesturing_no_tone1"] = "🙅🏻‍♀️", - ["woman_gesturing_no_light_skin_tone"] = "🙅🏻‍♀️", - ["woman_gesturing_no_tone2"] = "🙅🏼‍♀️", - ["woman_gesturing_no_medium_light_skin_tone"] = "🙅🏼‍♀️", - ["woman_gesturing_no_tone3"] = "🙅🏽‍♀️", - ["woman_gesturing_no_medium_skin_tone"] = "🙅🏽‍♀️", - ["woman_gesturing_no_tone4"] = "🙅🏾‍♀️", - ["woman_gesturing_no_medium_dark_skin_tone"] = "🙅🏾‍♀️", - ["woman_gesturing_no_tone5"] = "🙅🏿‍♀️", - ["woman_gesturing_no_dark_skin_tone"] = "🙅🏿‍♀️", - ["man_gesturing_no"] = "🙅‍♂️", - ["man_gesturing_no_tone1"] = "🙅🏻‍♂️", - ["man_gesturing_no_light_skin_tone"] = "🙅🏻‍♂️", - ["man_gesturing_no_tone2"] = "🙅🏼‍♂️", - ["man_gesturing_no_medium_light_skin_tone"] = "🙅🏼‍♂️", - ["man_gesturing_no_tone3"] = "🙅🏽‍♂️", - ["man_gesturing_no_medium_skin_tone"] = "🙅🏽‍♂️", - ["man_gesturing_no_tone4"] = "🙅🏾‍♂️", - ["man_gesturing_no_medium_dark_skin_tone"] = "🙅🏾‍♂️", - ["man_gesturing_no_tone5"] = "🙅🏿‍♂️", - ["man_gesturing_no_dark_skin_tone"] = "🙅🏿‍♂️", - ["person_gesturing_ok"] = "🙆", - ["ok_woman"] = "🙆", - ["person_gesturing_ok_tone1"] = "🙆🏻", - ["ok_woman_tone1"] = "🙆🏻", - ["person_gesturing_ok_tone2"] = "🙆🏼", - ["ok_woman_tone2"] = "🙆🏼", - ["person_gesturing_ok_tone3"] = "🙆🏽", - ["ok_woman_tone3"] = "🙆🏽", - ["person_gesturing_ok_tone4"] = "🙆🏾", - ["ok_woman_tone4"] = "🙆🏾", - ["person_gesturing_ok_tone5"] = "🙆🏿", - ["ok_woman_tone5"] = "🙆🏿", - ["woman_gesturing_ok"] = "🙆‍♀️", - ["woman_gesturing_ok_tone1"] = "🙆🏻‍♀️", - ["woman_gesturing_ok_light_skin_tone"] = "🙆🏻‍♀️", - ["woman_gesturing_ok_tone2"] = "🙆🏼‍♀️", - ["woman_gesturing_ok_medium_light_skin_tone"] = "🙆🏼‍♀️", - ["woman_gesturing_ok_tone3"] = "🙆🏽‍♀️", - ["woman_gesturing_ok_medium_skin_tone"] = "🙆🏽‍♀️", - ["woman_gesturing_ok_tone4"] = "🙆🏾‍♀️", - ["woman_gesturing_ok_medium_dark_skin_tone"] = "🙆🏾‍♀️", - ["woman_gesturing_ok_tone5"] = "🙆🏿‍♀️", - ["woman_gesturing_ok_dark_skin_tone"] = "🙆🏿‍♀️", - ["man_gesturing_ok"] = "🙆‍♂️", - ["man_gesturing_ok_tone1"] = "🙆🏻‍♂️", - ["man_gesturing_ok_light_skin_tone"] = "🙆🏻‍♂️", - ["man_gesturing_ok_tone2"] = "🙆🏼‍♂️", - ["man_gesturing_ok_medium_light_skin_tone"] = "🙆🏼‍♂️", - ["man_gesturing_ok_tone3"] = "🙆🏽‍♂️", - ["man_gesturing_ok_medium_skin_tone"] = "🙆🏽‍♂️", - ["man_gesturing_ok_tone4"] = "🙆🏾‍♂️", - ["man_gesturing_ok_medium_dark_skin_tone"] = "🙆🏾‍♂️", - ["man_gesturing_ok_tone5"] = "🙆🏿‍♂️", - ["man_gesturing_ok_dark_skin_tone"] = "🙆🏿‍♂️", - ["person_raising_hand"] = "🙋", - ["raising_hand"] = "🙋", - ["person_raising_hand_tone1"] = "🙋🏻", - ["raising_hand_tone1"] = "🙋🏻", - ["person_raising_hand_tone2"] = "🙋🏼", - ["raising_hand_tone2"] = "🙋🏼", - ["person_raising_hand_tone3"] = "🙋🏽", - ["raising_hand_tone3"] = "🙋🏽", - ["person_raising_hand_tone4"] = "🙋🏾", - ["raising_hand_tone4"] = "🙋🏾", - ["person_raising_hand_tone5"] = "🙋🏿", - ["raising_hand_tone5"] = "🙋🏿", - ["woman_raising_hand"] = "🙋‍♀️", - ["woman_raising_hand_tone1"] = "🙋🏻‍♀️", - ["woman_raising_hand_light_skin_tone"] = "🙋🏻‍♀️", - ["woman_raising_hand_tone2"] = "🙋🏼‍♀️", - ["woman_raising_hand_medium_light_skin_tone"] = "🙋🏼‍♀️", - ["woman_raising_hand_tone3"] = "🙋🏽‍♀️", - ["woman_raising_hand_medium_skin_tone"] = "🙋🏽‍♀️", - ["woman_raising_hand_tone4"] = "🙋🏾‍♀️", - ["woman_raising_hand_medium_dark_skin_tone"] = "🙋🏾‍♀️", - ["woman_raising_hand_tone5"] = "🙋🏿‍♀️", - ["woman_raising_hand_dark_skin_tone"] = "🙋🏿‍♀️", - ["man_raising_hand"] = "🙋‍♂️", - ["man_raising_hand_tone1"] = "🙋🏻‍♂️", - ["man_raising_hand_light_skin_tone"] = "🙋🏻‍♂️", - ["man_raising_hand_tone2"] = "🙋🏼‍♂️", - ["man_raising_hand_medium_light_skin_tone"] = "🙋🏼‍♂️", - ["man_raising_hand_tone3"] = "🙋🏽‍♂️", - ["man_raising_hand_medium_skin_tone"] = "🙋🏽‍♂️", - ["man_raising_hand_tone4"] = "🙋🏾‍♂️", - ["man_raising_hand_medium_dark_skin_tone"] = "🙋🏾‍♂️", - ["man_raising_hand_tone5"] = "🙋🏿‍♂️", - ["man_raising_hand_dark_skin_tone"] = "🙋🏿‍♂️", - ["deaf_person"] = "🧏", - ["deaf_person_tone1"] = "🧏🏻", - ["deaf_person_light_skin_tone"] = "🧏🏻", - ["deaf_person_tone2"] = "🧏🏼", - ["deaf_person_medium_light_skin_tone"] = "🧏🏼", - ["deaf_person_tone3"] = "🧏🏽", - ["deaf_person_medium_skin_tone"] = "🧏🏽", - ["deaf_person_tone4"] = "🧏🏾", - ["deaf_person_medium_dark_skin_tone"] = "🧏🏾", - ["deaf_person_tone5"] = "🧏🏿", - ["deaf_person_dark_skin_tone"] = "🧏🏿", - ["deaf_woman"] = "🧏‍♀️", - ["deaf_woman_tone1"] = "🧏🏻‍♀️", - ["deaf_woman_light_skin_tone"] = "🧏🏻‍♀️", - ["deaf_woman_tone2"] = "🧏🏼‍♀️", - ["deaf_woman_medium_light_skin_tone"] = "🧏🏼‍♀️", - ["deaf_woman_tone3"] = "🧏🏽‍♀️", - ["deaf_woman_medium_skin_tone"] = "🧏🏽‍♀️", - ["deaf_woman_tone4"] = "🧏🏾‍♀️", - ["deaf_woman_medium_dark_skin_tone"] = "🧏🏾‍♀️", - ["deaf_woman_tone5"] = "🧏🏿‍♀️", - ["deaf_woman_dark_skin_tone"] = "🧏🏿‍♀️", - ["deaf_man"] = "🧏‍♂️", - ["deaf_man_tone1"] = "🧏🏻‍♂️", - ["deaf_man_light_skin_tone"] = "🧏🏻‍♂️", - ["deaf_man_tone2"] = "🧏🏼‍♂️", - ["deaf_man_medium_light_skin_tone"] = "🧏🏼‍♂️", - ["deaf_man_tone3"] = "🧏🏽‍♂️", - ["deaf_man_medium_skin_tone"] = "🧏🏽‍♂️", - ["deaf_man_tone4"] = "🧏🏾‍♂️", - ["deaf_man_medium_dark_skin_tone"] = "🧏🏾‍♂️", - ["deaf_man_tone5"] = "🧏🏿‍♂️", - ["deaf_man_dark_skin_tone"] = "🧏🏿‍♂️", - ["person_facepalming"] = "🤦", - ["face_palm"] = "🤦", - ["facepalm"] = "🤦", - ["person_facepalming_tone1"] = "🤦🏻", - ["face_palm_tone1"] = "🤦🏻", - ["facepalm_tone1"] = "🤦🏻", - ["person_facepalming_tone2"] = "🤦🏼", - ["face_palm_tone2"] = "🤦🏼", - ["facepalm_tone2"] = "🤦🏼", - ["person_facepalming_tone3"] = "🤦🏽", - ["face_palm_tone3"] = "🤦🏽", - ["facepalm_tone3"] = "🤦🏽", - ["person_facepalming_tone4"] = "🤦🏾", - ["face_palm_tone4"] = "🤦🏾", - ["facepalm_tone4"] = "🤦🏾", - ["person_facepalming_tone5"] = "🤦🏿", - ["face_palm_tone5"] = "🤦🏿", - ["facepalm_tone5"] = "🤦🏿", - ["woman_facepalming"] = "🤦‍♀️", - ["woman_facepalming_tone1"] = "🤦🏻‍♀️", - ["woman_facepalming_light_skin_tone"] = "🤦🏻‍♀️", - ["woman_facepalming_tone2"] = "🤦🏼‍♀️", - ["woman_facepalming_medium_light_skin_tone"] = "🤦🏼‍♀️", - ["woman_facepalming_tone3"] = "🤦🏽‍♀️", - ["woman_facepalming_medium_skin_tone"] = "🤦🏽‍♀️", - ["woman_facepalming_tone4"] = "🤦🏾‍♀️", - ["woman_facepalming_medium_dark_skin_tone"] = "🤦🏾‍♀️", - ["woman_facepalming_tone5"] = "🤦🏿‍♀️", - ["woman_facepalming_dark_skin_tone"] = "🤦🏿‍♀️", - ["man_facepalming"] = "🤦‍♂️", - ["man_facepalming_tone1"] = "🤦🏻‍♂️", - ["man_facepalming_light_skin_tone"] = "🤦🏻‍♂️", - ["man_facepalming_tone2"] = "🤦🏼‍♂️", - ["man_facepalming_medium_light_skin_tone"] = "🤦🏼‍♂️", - ["man_facepalming_tone3"] = "🤦🏽‍♂️", - ["man_facepalming_medium_skin_tone"] = "🤦🏽‍♂️", - ["man_facepalming_tone4"] = "🤦🏾‍♂️", - ["man_facepalming_medium_dark_skin_tone"] = "🤦🏾‍♂️", - ["man_facepalming_tone5"] = "🤦🏿‍♂️", - ["man_facepalming_dark_skin_tone"] = "🤦🏿‍♂️", - ["person_shrugging"] = "🤷", - ["shrug"] = "🤷", - ["person_shrugging_tone1"] = "🤷🏻", - ["shrug_tone1"] = "🤷🏻", - ["person_shrugging_tone2"] = "🤷🏼", - ["shrug_tone2"] = "🤷🏼", - ["person_shrugging_tone3"] = "🤷🏽", - ["shrug_tone3"] = "🤷🏽", - ["person_shrugging_tone4"] = "🤷🏾", - ["shrug_tone4"] = "🤷🏾", - ["person_shrugging_tone5"] = "🤷🏿", - ["shrug_tone5"] = "🤷🏿", - ["woman_shrugging"] = "🤷‍♀️", - ["woman_shrugging_tone1"] = "🤷🏻‍♀️", - ["woman_shrugging_light_skin_tone"] = "🤷🏻‍♀️", - ["woman_shrugging_tone2"] = "🤷🏼‍♀️", - ["woman_shrugging_medium_light_skin_tone"] = "🤷🏼‍♀️", - ["woman_shrugging_tone3"] = "🤷🏽‍♀️", - ["woman_shrugging_medium_skin_tone"] = "🤷🏽‍♀️", - ["woman_shrugging_tone4"] = "🤷🏾‍♀️", - ["woman_shrugging_medium_dark_skin_tone"] = "🤷🏾‍♀️", - ["woman_shrugging_tone5"] = "🤷🏿‍♀️", - ["woman_shrugging_dark_skin_tone"] = "🤷🏿‍♀️", - ["man_shrugging"] = "🤷‍♂️", - ["man_shrugging_tone1"] = "🤷🏻‍♂️", - ["man_shrugging_light_skin_tone"] = "🤷🏻‍♂️", - ["man_shrugging_tone2"] = "🤷🏼‍♂️", - ["man_shrugging_medium_light_skin_tone"] = "🤷🏼‍♂️", - ["man_shrugging_tone3"] = "🤷🏽‍♂️", - ["man_shrugging_medium_skin_tone"] = "🤷🏽‍♂️", - ["man_shrugging_tone4"] = "🤷🏾‍♂️", - ["man_shrugging_medium_dark_skin_tone"] = "🤷🏾‍♂️", - ["man_shrugging_tone5"] = "🤷🏿‍♂️", - ["man_shrugging_dark_skin_tone"] = "🤷🏿‍♂️", - ["person_pouting"] = "🙎", - ["person_with_pouting_face"] = "🙎", - ["person_pouting_tone1"] = "🙎🏻", - ["person_with_pouting_face_tone1"] = "🙎🏻", - ["person_pouting_tone2"] = "🙎🏼", - ["person_with_pouting_face_tone2"] = "🙎🏼", - ["person_pouting_tone3"] = "🙎🏽", - ["person_with_pouting_face_tone3"] = "🙎🏽", - ["person_pouting_tone4"] = "🙎🏾", - ["person_with_pouting_face_tone4"] = "🙎🏾", - ["person_pouting_tone5"] = "🙎🏿", - ["person_with_pouting_face_tone5"] = "🙎🏿", - ["woman_pouting"] = "🙎‍♀️", - ["woman_pouting_tone1"] = "🙎🏻‍♀️", - ["woman_pouting_light_skin_tone"] = "🙎🏻‍♀️", - ["woman_pouting_tone2"] = "🙎🏼‍♀️", - ["woman_pouting_medium_light_skin_tone"] = "🙎🏼‍♀️", - ["woman_pouting_tone3"] = "🙎🏽‍♀️", - ["woman_pouting_medium_skin_tone"] = "🙎🏽‍♀️", - ["woman_pouting_tone4"] = "🙎🏾‍♀️", - ["woman_pouting_medium_dark_skin_tone"] = "🙎🏾‍♀️", - ["woman_pouting_tone5"] = "🙎🏿‍♀️", - ["woman_pouting_dark_skin_tone"] = "🙎🏿‍♀️", - ["man_pouting"] = "🙎‍♂️", - ["man_pouting_tone1"] = "🙎🏻‍♂️", - ["man_pouting_light_skin_tone"] = "🙎🏻‍♂️", - ["man_pouting_tone2"] = "🙎🏼‍♂️", - ["man_pouting_medium_light_skin_tone"] = "🙎🏼‍♂️", - ["man_pouting_tone3"] = "🙎🏽‍♂️", - ["man_pouting_medium_skin_tone"] = "🙎🏽‍♂️", - ["man_pouting_tone4"] = "🙎🏾‍♂️", - ["man_pouting_medium_dark_skin_tone"] = "🙎🏾‍♂️", - ["man_pouting_tone5"] = "🙎🏿‍♂️", - ["man_pouting_dark_skin_tone"] = "🙎🏿‍♂️", - ["person_frowning"] = "🙍", - ["person_frowning_tone1"] = "🙍🏻", - ["person_frowning_tone2"] = "🙍🏼", - ["person_frowning_tone3"] = "🙍🏽", - ["person_frowning_tone4"] = "🙍🏾", - ["person_frowning_tone5"] = "🙍🏿", - ["woman_frowning"] = "🙍‍♀️", - ["woman_frowning_tone1"] = "🙍🏻‍♀️", - ["woman_frowning_light_skin_tone"] = "🙍🏻‍♀️", - ["woman_frowning_tone2"] = "🙍🏼‍♀️", - ["woman_frowning_medium_light_skin_tone"] = "🙍🏼‍♀️", - ["woman_frowning_tone3"] = "🙍🏽‍♀️", - ["woman_frowning_medium_skin_tone"] = "🙍🏽‍♀️", - ["woman_frowning_tone4"] = "🙍🏾‍♀️", - ["woman_frowning_medium_dark_skin_tone"] = "🙍🏾‍♀️", - ["woman_frowning_tone5"] = "🙍🏿‍♀️", - ["woman_frowning_dark_skin_tone"] = "🙍🏿‍♀️", - ["man_frowning"] = "🙍‍♂️", - ["man_frowning_tone1"] = "🙍🏻‍♂️", - ["man_frowning_light_skin_tone"] = "🙍🏻‍♂️", - ["man_frowning_tone2"] = "🙍🏼‍♂️", - ["man_frowning_medium_light_skin_tone"] = "🙍🏼‍♂️", - ["man_frowning_tone3"] = "🙍🏽‍♂️", - ["man_frowning_medium_skin_tone"] = "🙍🏽‍♂️", - ["man_frowning_tone4"] = "🙍🏾‍♂️", - ["man_frowning_medium_dark_skin_tone"] = "🙍🏾‍♂️", - ["man_frowning_tone5"] = "🙍🏿‍♂️", - ["man_frowning_dark_skin_tone"] = "🙍🏿‍♂️", - ["person_getting_haircut"] = "💇", - ["haircut"] = "💇", - ["person_getting_haircut_tone1"] = "💇🏻", - ["haircut_tone1"] = "💇🏻", - ["person_getting_haircut_tone2"] = "💇🏼", - ["haircut_tone2"] = "💇🏼", - ["person_getting_haircut_tone3"] = "💇🏽", - ["haircut_tone3"] = "💇🏽", - ["person_getting_haircut_tone4"] = "💇🏾", - ["haircut_tone4"] = "💇🏾", - ["person_getting_haircut_tone5"] = "💇🏿", - ["haircut_tone5"] = "💇🏿", - ["woman_getting_haircut"] = "💇‍♀️", - ["woman_getting_haircut_tone1"] = "💇🏻‍♀️", - ["woman_getting_haircut_light_skin_tone"] = "💇🏻‍♀️", - ["woman_getting_haircut_tone2"] = "💇🏼‍♀️", - ["woman_getting_haircut_medium_light_skin_tone"] = "💇🏼‍♀️", - ["woman_getting_haircut_tone3"] = "💇🏽‍♀️", - ["woman_getting_haircut_medium_skin_tone"] = "💇🏽‍♀️", - ["woman_getting_haircut_tone4"] = "💇🏾‍♀️", - ["woman_getting_haircut_medium_dark_skin_tone"] = "💇🏾‍♀️", - ["woman_getting_haircut_tone5"] = "💇🏿‍♀️", - ["woman_getting_haircut_dark_skin_tone"] = "💇🏿‍♀️", - ["man_getting_haircut"] = "💇‍♂️", - ["man_getting_haircut_tone1"] = "💇🏻‍♂️", - ["man_getting_haircut_light_skin_tone"] = "💇🏻‍♂️", - ["man_getting_haircut_tone2"] = "💇🏼‍♂️", - ["man_getting_haircut_medium_light_skin_tone"] = "💇🏼‍♂️", - ["man_getting_haircut_tone3"] = "💇🏽‍♂️", - ["man_getting_haircut_medium_skin_tone"] = "💇🏽‍♂️", - ["man_getting_haircut_tone4"] = "💇🏾‍♂️", - ["man_getting_haircut_medium_dark_skin_tone"] = "💇🏾‍♂️", - ["man_getting_haircut_tone5"] = "💇🏿‍♂️", - ["man_getting_haircut_dark_skin_tone"] = "💇🏿‍♂️", - ["person_getting_massage"] = "💆", - ["massage"] = "💆", - ["person_getting_massage_tone1"] = "💆🏻", - ["massage_tone1"] = "💆🏻", - ["person_getting_massage_tone2"] = "💆🏼", - ["massage_tone2"] = "💆🏼", - ["person_getting_massage_tone3"] = "💆🏽", - ["massage_tone3"] = "💆🏽", - ["person_getting_massage_tone4"] = "💆🏾", - ["massage_tone4"] = "💆🏾", - ["person_getting_massage_tone5"] = "💆🏿", - ["massage_tone5"] = "💆🏿", - ["woman_getting_face_massage"] = "💆‍♀️", - ["woman_getting_face_massage_tone1"] = "💆🏻‍♀️", - ["woman_getting_face_massage_light_skin_tone"] = "💆🏻‍♀️", - ["woman_getting_face_massage_tone2"] = "💆🏼‍♀️", - ["woman_getting_face_massage_medium_light_skin_tone"] = "💆🏼‍♀️", - ["woman_getting_face_massage_tone3"] = "💆🏽‍♀️", - ["woman_getting_face_massage_medium_skin_tone"] = "💆🏽‍♀️", - ["woman_getting_face_massage_tone4"] = "💆🏾‍♀️", - ["woman_getting_face_massage_medium_dark_skin_tone"] = "💆🏾‍♀️", - ["woman_getting_face_massage_tone5"] = "💆🏿‍♀️", - ["woman_getting_face_massage_dark_skin_tone"] = "💆🏿‍♀️", - ["man_getting_face_massage"] = "💆‍♂️", - ["man_getting_face_massage_tone1"] = "💆🏻‍♂️", - ["man_getting_face_massage_light_skin_tone"] = "💆🏻‍♂️", - ["man_getting_face_massage_tone2"] = "💆🏼‍♂️", - ["man_getting_face_massage_medium_light_skin_tone"] = "💆🏼‍♂️", - ["man_getting_face_massage_tone3"] = "💆🏽‍♂️", - ["man_getting_face_massage_medium_skin_tone"] = "💆🏽‍♂️", - ["man_getting_face_massage_tone4"] = "💆🏾‍♂️", - ["man_getting_face_massage_medium_dark_skin_tone"] = "💆🏾‍♂️", - ["man_getting_face_massage_tone5"] = "💆🏿‍♂️", - ["man_getting_face_massage_dark_skin_tone"] = "💆🏿‍♂️", - ["person_in_steamy_room"] = "🧖", - ["person_in_steamy_room_tone1"] = "🧖🏻", - ["person_in_steamy_room_light_skin_tone"] = "🧖🏻", - ["person_in_steamy_room_tone2"] = "🧖🏼", - ["person_in_steamy_room_medium_light_skin_tone"] = "🧖🏼", - ["person_in_steamy_room_tone3"] = "🧖🏽", - ["person_in_steamy_room_medium_skin_tone"] = "🧖🏽", - ["person_in_steamy_room_tone4"] = "🧖🏾", - ["person_in_steamy_room_medium_dark_skin_tone"] = "🧖🏾", - ["person_in_steamy_room_tone5"] = "🧖🏿", - ["person_in_steamy_room_dark_skin_tone"] = "🧖🏿", - ["woman_in_steamy_room"] = "🧖‍♀️", - ["woman_in_steamy_room_tone1"] = "🧖🏻‍♀️", - ["woman_in_steamy_room_light_skin_tone"] = "🧖🏻‍♀️", - ["woman_in_steamy_room_tone2"] = "🧖🏼‍♀️", - ["woman_in_steamy_room_medium_light_skin_tone"] = "🧖🏼‍♀️", - ["woman_in_steamy_room_tone3"] = "🧖🏽‍♀️", - ["woman_in_steamy_room_medium_skin_tone"] = "🧖🏽‍♀️", - ["woman_in_steamy_room_tone4"] = "🧖🏾‍♀️", - ["woman_in_steamy_room_medium_dark_skin_tone"] = "🧖🏾‍♀️", - ["woman_in_steamy_room_tone5"] = "🧖🏿‍♀️", - ["woman_in_steamy_room_dark_skin_tone"] = "🧖🏿‍♀️", - ["man_in_steamy_room"] = "🧖‍♂️", - ["man_in_steamy_room_tone1"] = "🧖🏻‍♂️", - ["man_in_steamy_room_light_skin_tone"] = "🧖🏻‍♂️", - ["man_in_steamy_room_tone2"] = "🧖🏼‍♂️", - ["man_in_steamy_room_medium_light_skin_tone"] = "🧖🏼‍♂️", - ["man_in_steamy_room_tone3"] = "🧖🏽‍♂️", - ["man_in_steamy_room_medium_skin_tone"] = "🧖🏽‍♂️", - ["man_in_steamy_room_tone4"] = "🧖🏾‍♂️", - ["man_in_steamy_room_medium_dark_skin_tone"] = "🧖🏾‍♂️", - ["man_in_steamy_room_tone5"] = "🧖🏿‍♂️", - ["man_in_steamy_room_dark_skin_tone"] = "🧖🏿‍♂️", - ["nail_care"] = "💅", - ["nail_care_tone1"] = "💅🏻", - ["nail_care_tone2"] = "💅🏼", - ["nail_care_tone3"] = "💅🏽", - ["nail_care_tone4"] = "💅🏾", - ["nail_care_tone5"] = "💅🏿", - ["selfie"] = "🤳", - ["selfie_tone1"] = "🤳🏻", - ["selfie_tone2"] = "🤳🏼", - ["selfie_tone3"] = "🤳🏽", - ["selfie_tone4"] = "🤳🏾", - ["selfie_tone5"] = "🤳🏿", - ["dancer"] = "💃", - ["dancer_tone1"] = "💃🏻", - ["dancer_tone2"] = "💃🏼", - ["dancer_tone3"] = "💃🏽", - ["dancer_tone4"] = "💃🏾", - ["dancer_tone5"] = "💃🏿", - ["man_dancing"] = "🕺", - ["male_dancer"] = "🕺", - ["man_dancing_tone1"] = "🕺🏻", - ["male_dancer_tone1"] = "🕺🏻", - ["man_dancing_tone2"] = "🕺🏼", - ["male_dancer_tone2"] = "🕺🏼", - ["man_dancing_tone3"] = "🕺🏽", - ["male_dancer_tone3"] = "🕺🏽", - ["man_dancing_tone5"] = "🕺🏿", - ["male_dancer_tone5"] = "🕺🏿", - ["man_dancing_tone4"] = "🕺🏾", - ["male_dancer_tone4"] = "🕺🏾", - ["people_with_bunny_ears_partying"] = "👯", - ["dancers"] = "👯", - ["women_with_bunny_ears_partying"] = "👯‍♀️", - ["men_with_bunny_ears_partying"] = "👯‍♂️", - ["levitate"] = "🕴️", - ["man_in_business_suit_levitating"] = "🕴️", - ["levitate_tone1"] = "🕴🏻", - ["man_in_business_suit_levitating_tone1"] = "🕴🏻", - ["man_in_business_suit_levitating_light_skin_tone"] = "🕴🏻", - ["levitate_tone2"] = "🕴🏼", - ["man_in_business_suit_levitating_tone2"] = "🕴🏼", - ["man_in_business_suit_levitating_medium_light_skin_tone"] = "🕴🏼", - ["levitate_tone3"] = "🕴🏽", - ["man_in_business_suit_levitating_tone3"] = "🕴🏽", - ["man_in_business_suit_levitating_medium_skin_tone"] = "🕴🏽", - ["levitate_tone4"] = "🕴🏾", - ["man_in_business_suit_levitating_tone4"] = "🕴🏾", - ["man_in_business_suit_levitating_medium_dark_skin_tone"] = "🕴🏾", - ["levitate_tone5"] = "🕴🏿", - ["man_in_business_suit_levitating_tone5"] = "🕴🏿", - ["man_in_business_suit_levitating_dark_skin_tone"] = "🕴🏿", - ["person_in_manual_wheelchair"] = "🧑‍🦽", - ["person_in_manual_wheelchair_tone1"] = "🧑🏻‍🦽", - ["person_in_manual_wheelchair_light_skin_tone"] = "🧑🏻‍🦽", - ["person_in_manual_wheelchair_tone2"] = "🧑🏼‍🦽", - ["person_in_manual_wheelchair_medium_light_skin_tone"] = "🧑🏼‍🦽", - ["person_in_manual_wheelchair_tone3"] = "🧑🏽‍🦽", - ["person_in_manual_wheelchair_medium_skin_tone"] = "🧑🏽‍🦽", - ["person_in_manual_wheelchair_tone4"] = "🧑🏾‍🦽", - ["person_in_manual_wheelchair_medium_dark_skin_tone"] = "🧑🏾‍🦽", - ["person_in_manual_wheelchair_tone5"] = "🧑🏿‍🦽", - ["person_in_manual_wheelchair_dark_skin_tone"] = "🧑🏿‍🦽", - ["woman_in_manual_wheelchair"] = "👩‍🦽", - ["woman_in_manual_wheelchair_tone1"] = "👩🏻‍🦽", - ["woman_in_manual_wheelchair_light_skin_tone"] = "👩🏻‍🦽", - ["woman_in_manual_wheelchair_tone2"] = "👩🏼‍🦽", - ["woman_in_manual_wheelchair_medium_light_skin_tone"] = "👩🏼‍🦽", - ["woman_in_manual_wheelchair_tone3"] = "👩🏽‍🦽", - ["woman_in_manual_wheelchair_medium_skin_tone"] = "👩🏽‍🦽", - ["woman_in_manual_wheelchair_tone4"] = "👩🏾‍🦽", - ["woman_in_manual_wheelchair_medium_dark_skin_tone"] = "👩🏾‍🦽", - ["woman_in_manual_wheelchair_tone5"] = "👩🏿‍🦽", - ["woman_in_manual_wheelchair_dark_skin_tone"] = "👩🏿‍🦽", - ["man_in_manual_wheelchair"] = "👨‍🦽", - ["man_in_manual_wheelchair_tone1"] = "👨🏻‍🦽", - ["man_in_manual_wheelchair_light_skin_tone"] = "👨🏻‍🦽", - ["man_in_manual_wheelchair_tone2"] = "👨🏼‍🦽", - ["man_in_manual_wheelchair_medium_light_skin_tone"] = "👨🏼‍🦽", - ["man_in_manual_wheelchair_tone3"] = "👨🏽‍🦽", - ["man_in_manual_wheelchair_medium_skin_tone"] = "👨🏽‍🦽", - ["man_in_manual_wheelchair_tone4"] = "👨🏾‍🦽", - ["man_in_manual_wheelchair_medium_dark_skin_tone"] = "👨🏾‍🦽", - ["man_in_manual_wheelchair_tone5"] = "👨🏿‍🦽", - ["man_in_manual_wheelchair_dark_skin_tone"] = "👨🏿‍🦽", - ["person_in_motorized_wheelchair"] = "🧑‍🦼", - ["person_in_motorized_wheelchair_tone1"] = "🧑🏻‍🦼", - ["person_in_motorized_wheelchair_light_skin_tone"] = "🧑🏻‍🦼", - ["person_in_motorized_wheelchair_tone2"] = "🧑🏼‍🦼", - ["person_in_motorized_wheelchair_medium_light_skin_tone"] = "🧑🏼‍🦼", - ["person_in_motorized_wheelchair_tone3"] = "🧑🏽‍🦼", - ["person_in_motorized_wheelchair_medium_skin_tone"] = "🧑🏽‍🦼", - ["person_in_motorized_wheelchair_tone4"] = "🧑🏾‍🦼", - ["person_in_motorized_wheelchair_medium_dark_skin_tone"] = "🧑🏾‍🦼", - ["person_in_motorized_wheelchair_tone5"] = "🧑🏿‍🦼", - ["person_in_motorized_wheelchair_dark_skin_tone"] = "🧑🏿‍🦼", - ["woman_in_motorized_wheelchair"] = "👩‍🦼", - ["woman_in_motorized_wheelchair_tone1"] = "👩🏻‍🦼", - ["woman_in_motorized_wheelchair_light_skin_tone"] = "👩🏻‍🦼", - ["woman_in_motorized_wheelchair_tone2"] = "👩🏼‍🦼", - ["woman_in_motorized_wheelchair_medium_light_skin_tone"] = "👩🏼‍🦼", - ["woman_in_motorized_wheelchair_tone3"] = "👩🏽‍🦼", - ["woman_in_motorized_wheelchair_medium_skin_tone"] = "👩🏽‍🦼", - ["woman_in_motorized_wheelchair_tone4"] = "👩🏾‍🦼", - ["woman_in_motorized_wheelchair_medium_dark_skin_tone"] = "👩🏾‍🦼", - ["woman_in_motorized_wheelchair_tone5"] = "👩🏿‍🦼", - ["woman_in_motorized_wheelchair_dark_skin_tone"] = "👩🏿‍🦼", - ["man_in_motorized_wheelchair"] = "👨‍🦼", - ["man_in_motorized_wheelchair_tone1"] = "👨🏻‍🦼", - ["man_in_motorized_wheelchair_light_skin_tone"] = "👨🏻‍🦼", - ["man_in_motorized_wheelchair_tone2"] = "👨🏼‍🦼", - ["man_in_motorized_wheelchair_medium_light_skin_tone"] = "👨🏼‍🦼", - ["man_in_motorized_wheelchair_tone3"] = "👨🏽‍🦼", - ["man_in_motorized_wheelchair_medium_skin_tone"] = "👨🏽‍🦼", - ["man_in_motorized_wheelchair_tone4"] = "👨🏾‍🦼", - ["man_in_motorized_wheelchair_medium_dark_skin_tone"] = "👨🏾‍🦼", - ["man_in_motorized_wheelchair_tone5"] = "👨🏿‍🦼", - ["man_in_motorized_wheelchair_dark_skin_tone"] = "👨🏿‍🦼", - ["person_walking"] = "🚶", - ["walking"] = "🚶", - ["person_walking_tone1"] = "🚶🏻", - ["walking_tone1"] = "🚶🏻", - ["person_walking_tone2"] = "🚶🏼", - ["walking_tone2"] = "🚶🏼", - ["person_walking_tone3"] = "🚶🏽", - ["walking_tone3"] = "🚶🏽", - ["person_walking_tone4"] = "🚶🏾", - ["walking_tone4"] = "🚶🏾", - ["person_walking_tone5"] = "🚶🏿", - ["walking_tone5"] = "🚶🏿", - ["woman_walking"] = "🚶‍♀️", - ["woman_walking_tone1"] = "🚶🏻‍♀️", - ["woman_walking_light_skin_tone"] = "🚶🏻‍♀️", - ["woman_walking_tone2"] = "🚶🏼‍♀️", - ["woman_walking_medium_light_skin_tone"] = "🚶🏼‍♀️", - ["woman_walking_tone3"] = "🚶🏽‍♀️", - ["woman_walking_medium_skin_tone"] = "🚶🏽‍♀️", - ["woman_walking_tone4"] = "🚶🏾‍♀️", - ["woman_walking_medium_dark_skin_tone"] = "🚶🏾‍♀️", - ["woman_walking_tone5"] = "🚶🏿‍♀️", - ["woman_walking_dark_skin_tone"] = "🚶🏿‍♀️", - ["man_walking"] = "🚶‍♂️", - ["man_walking_tone1"] = "🚶🏻‍♂️", - ["man_walking_light_skin_tone"] = "🚶🏻‍♂️", - ["man_walking_tone2"] = "🚶🏼‍♂️", - ["man_walking_medium_light_skin_tone"] = "🚶🏼‍♂️", - ["man_walking_tone3"] = "🚶🏽‍♂️", - ["man_walking_medium_skin_tone"] = "🚶🏽‍♂️", - ["man_walking_tone4"] = "🚶🏾‍♂️", - ["man_walking_medium_dark_skin_tone"] = "🚶🏾‍♂️", - ["man_walking_tone5"] = "🚶🏿‍♂️", - ["man_walking_dark_skin_tone"] = "🚶🏿‍♂️", - ["person_with_probing_cane"] = "🧑‍🦯", - ["person_with_probing_cane_tone1"] = "🧑🏻‍🦯", - ["person_with_probing_cane_light_skin_tone"] = "🧑🏻‍🦯", - ["person_with_probing_cane_tone2"] = "🧑🏼‍🦯", - ["person_with_probing_cane_medium_light_skin_tone"] = "🧑🏼‍🦯", - ["person_with_probing_cane_tone3"] = "🧑🏽‍🦯", - ["person_with_probing_cane_medium_skin_tone"] = "🧑🏽‍🦯", - ["person_with_probing_cane_tone4"] = "🧑🏾‍🦯", - ["person_with_probing_cane_medium_dark_skin_tone"] = "🧑🏾‍🦯", - ["person_with_probing_cane_tone5"] = "🧑🏿‍🦯", - ["person_with_probing_cane_dark_skin_tone"] = "🧑🏿‍🦯", - ["woman_with_probing_cane"] = "👩‍🦯", - ["woman_with_probing_cane_tone1"] = "👩🏻‍🦯", - ["woman_with_probing_cane_light_skin_tone"] = "👩🏻‍🦯", - ["woman_with_probing_cane_tone2"] = "👩🏼‍🦯", - ["woman_with_probing_cane_medium_light_skin_tone"] = "👩🏼‍🦯", - ["woman_with_probing_cane_tone3"] = "👩🏽‍🦯", - ["woman_with_probing_cane_medium_skin_tone"] = "👩🏽‍🦯", - ["woman_with_probing_cane_tone4"] = "👩🏾‍🦯", - ["woman_with_probing_cane_medium_dark_skin_tone"] = "👩🏾‍🦯", - ["woman_with_probing_cane_tone5"] = "👩🏿‍🦯", - ["woman_with_probing_cane_dark_skin_tone"] = "👩🏿‍🦯", - ["man_with_probing_cane"] = "👨‍🦯", - ["man_with_probing_cane_tone1"] = "👨🏻‍🦯", - ["man_with_probing_cane_light_skin_tone"] = "👨🏻‍🦯", - ["man_with_probing_cane_tone3"] = "👨🏽‍🦯", - ["man_with_probing_cane_medium_skin_tone"] = "👨🏽‍🦯", - ["man_with_probing_cane_tone2"] = "👨🏼‍🦯", - ["man_with_probing_cane_medium_light_skin_tone"] = "👨🏼‍🦯", - ["man_with_probing_cane_tone4"] = "👨🏾‍🦯", - ["man_with_probing_cane_medium_dark_skin_tone"] = "👨🏾‍🦯", - ["man_with_probing_cane_tone5"] = "👨🏿‍🦯", - ["man_with_probing_cane_dark_skin_tone"] = "👨🏿‍🦯", - ["person_kneeling"] = "🧎", - ["person_kneeling_tone1"] = "🧎🏻", - ["person_kneeling_light_skin_tone"] = "🧎🏻", - ["person_kneeling_tone2"] = "🧎🏼", - ["person_kneeling_medium_light_skin_tone"] = "🧎🏼", - ["person_kneeling_tone3"] = "🧎🏽", - ["person_kneeling_medium_skin_tone"] = "🧎🏽", - ["person_kneeling_tone4"] = "🧎🏾", - ["person_kneeling_medium_dark_skin_tone"] = "🧎🏾", - ["person_kneeling_tone5"] = "🧎🏿", - ["person_kneeling_dark_skin_tone"] = "🧎🏿", - ["woman_kneeling"] = "🧎‍♀️", - ["woman_kneeling_tone1"] = "🧎🏻‍♀️", - ["woman_kneeling_light_skin_tone"] = "🧎🏻‍♀️", - ["woman_kneeling_tone2"] = "🧎🏼‍♀️", - ["woman_kneeling_medium_light_skin_tone"] = "🧎🏼‍♀️", - ["woman_kneeling_tone3"] = "🧎🏽‍♀️", - ["woman_kneeling_medium_skin_tone"] = "🧎🏽‍♀️", - ["woman_kneeling_tone4"] = "🧎🏾‍♀️", - ["woman_kneeling_medium_dark_skin_tone"] = "🧎🏾‍♀️", - ["woman_kneeling_tone5"] = "🧎🏿‍♀️", - ["woman_kneeling_dark_skin_tone"] = "🧎🏿‍♀️", - ["man_kneeling"] = "🧎‍♂️", - ["man_kneeling_tone1"] = "🧎🏻‍♂️", - ["man_kneeling_light_skin_tone"] = "🧎🏻‍♂️", - ["man_kneeling_tone2"] = "🧎🏼‍♂️", - ["man_kneeling_medium_light_skin_tone"] = "🧎🏼‍♂️", - ["man_kneeling_tone3"] = "🧎🏽‍♂️", - ["man_kneeling_medium_skin_tone"] = "🧎🏽‍♂️", - ["man_kneeling_tone4"] = "🧎🏾‍♂️", - ["man_kneeling_medium_dark_skin_tone"] = "🧎🏾‍♂️", - ["man_kneeling_tone5"] = "🧎🏿‍♂️", - ["man_kneeling_dark_skin_tone"] = "🧎🏿‍♂️", - ["person_running"] = "🏃", - ["runner"] = "🏃", - ["person_running_tone1"] = "🏃🏻", - ["runner_tone1"] = "🏃🏻", - ["person_running_tone2"] = "🏃🏼", - ["runner_tone2"] = "🏃🏼", - ["person_running_tone3"] = "🏃🏽", - ["runner_tone3"] = "🏃🏽", - ["person_running_tone4"] = "🏃🏾", - ["runner_tone4"] = "🏃🏾", - ["person_running_tone5"] = "🏃🏿", - ["runner_tone5"] = "🏃🏿", - ["woman_running"] = "🏃‍♀️", - ["woman_running_tone1"] = "🏃🏻‍♀️", - ["woman_running_light_skin_tone"] = "🏃🏻‍♀️", - ["woman_running_tone2"] = "🏃🏼‍♀️", - ["woman_running_medium_light_skin_tone"] = "🏃🏼‍♀️", - ["woman_running_tone3"] = "🏃🏽‍♀️", - ["woman_running_medium_skin_tone"] = "🏃🏽‍♀️", - ["woman_running_tone4"] = "🏃🏾‍♀️", - ["woman_running_medium_dark_skin_tone"] = "🏃🏾‍♀️", - ["woman_running_tone5"] = "🏃🏿‍♀️", - ["woman_running_dark_skin_tone"] = "🏃🏿‍♀️", - ["man_running"] = "🏃‍♂️", - ["man_running_tone1"] = "🏃🏻‍♂️", - ["man_running_light_skin_tone"] = "🏃🏻‍♂️", - ["man_running_tone2"] = "🏃🏼‍♂️", - ["man_running_medium_light_skin_tone"] = "🏃🏼‍♂️", - ["man_running_tone3"] = "🏃🏽‍♂️", - ["man_running_medium_skin_tone"] = "🏃🏽‍♂️", - ["man_running_tone4"] = "🏃🏾‍♂️", - ["man_running_medium_dark_skin_tone"] = "🏃🏾‍♂️", - ["man_running_tone5"] = "🏃🏿‍♂️", - ["man_running_dark_skin_tone"] = "🏃🏿‍♂️", - ["person_standing"] = "🧍", - ["person_standing_tone1"] = "🧍🏻", - ["person_standing_light_skin_tone"] = "🧍🏻", - ["person_standing_tone2"] = "🧍🏼", - ["person_standing_medium_light_skin_tone"] = "🧍🏼", - ["person_standing_tone3"] = "🧍🏽", - ["person_standing_medium_skin_tone"] = "🧍🏽", - ["person_standing_tone4"] = "🧍🏾", - ["person_standing_medium_dark_skin_tone"] = "🧍🏾", - ["person_standing_tone5"] = "🧍🏿", - ["person_standing_dark_skin_tone"] = "🧍🏿", - ["woman_standing"] = "🧍‍♀️", - ["woman_standing_tone1"] = "🧍🏻‍♀️", - ["woman_standing_light_skin_tone"] = "🧍🏻‍♀️", - ["woman_standing_tone2"] = "🧍🏼‍♀️", - ["woman_standing_medium_light_skin_tone"] = "🧍🏼‍♀️", - ["woman_standing_tone3"] = "🧍🏽‍♀️", - ["woman_standing_medium_skin_tone"] = "🧍🏽‍♀️", - ["woman_standing_tone4"] = "🧍🏾‍♀️", - ["woman_standing_medium_dark_skin_tone"] = "🧍🏾‍♀️", - ["woman_standing_tone5"] = "🧍🏿‍♀️", - ["woman_standing_dark_skin_tone"] = "🧍🏿‍♀️", - ["man_standing"] = "🧍‍♂️", - ["man_standing_tone1"] = "🧍🏻‍♂️", - ["man_standing_light_skin_tone"] = "🧍🏻‍♂️", - ["man_standing_tone2"] = "🧍🏼‍♂️", - ["man_standing_medium_light_skin_tone"] = "🧍🏼‍♂️", - ["man_standing_tone3"] = "🧍🏽‍♂️", - ["man_standing_medium_skin_tone"] = "🧍🏽‍♂️", - ["man_standing_tone4"] = "🧍🏾‍♂️", - ["man_standing_medium_dark_skin_tone"] = "🧍🏾‍♂️", - ["man_standing_tone5"] = "🧍🏿‍♂️", - ["man_standing_dark_skin_tone"] = "🧍🏿‍♂️", - ["people_holding_hands"] = "🧑‍🤝‍🧑", - ["people_holding_hands_tone1"] = "🧑🏻‍🤝‍🧑🏻", - ["people_holding_hands_light_skin_tone"] = "🧑🏻‍🤝‍🧑🏻", - ["people_holding_hands_tone1_tone2"] = "🧑🏻‍🤝‍🧑🏼", - ["people_holding_hands_light_skin_tone_medium_light_skin_tone"] = "🧑🏻‍🤝‍🧑🏼", - ["people_holding_hands_tone1_tone3"] = "🧑🏻‍🤝‍🧑🏽", - ["people_holding_hands_light_skin_tone_medium_skin_tone"] = "🧑🏻‍🤝‍🧑🏽", - ["people_holding_hands_tone1_tone4"] = "🧑🏻‍🤝‍🧑🏾", - ["people_holding_hands_light_skin_tone_medium_dark_skin_tone"] = "🧑🏻‍🤝‍🧑🏾", - ["people_holding_hands_tone1_tone5"] = "🧑🏻‍🤝‍🧑🏿", - ["people_holding_hands_light_skin_tone_dark_skin_tone"] = "🧑🏻‍🤝‍🧑🏿", - ["people_holding_hands_tone2_tone1"] = "🧑🏼‍🤝‍🧑🏻", - ["people_holding_hands_medium_light_skin_tone_light_skin_tone"] = "🧑🏼‍🤝‍🧑🏻", - ["people_holding_hands_tone2"] = "🧑🏼‍🤝‍🧑🏼", - ["people_holding_hands_medium_light_skin_tone"] = "🧑🏼‍🤝‍🧑🏼", - ["people_holding_hands_tone2_tone3"] = "🧑🏼‍🤝‍🧑🏽", - ["people_holding_hands_medium_light_skin_tone_medium_skin_tone"] = "🧑🏼‍🤝‍🧑🏽", - ["people_holding_hands_tone2_tone4"] = "🧑🏼‍🤝‍🧑🏾", - ["people_holding_hands_medium_light_skin_tone_medium_dark_skin_tone"] = "🧑🏼‍🤝‍🧑🏾", - ["people_holding_hands_tone2_tone5"] = "🧑🏼‍🤝‍🧑🏿", - ["people_holding_hands_medium_light_skin_tone_dark_skin_tone"] = "🧑🏼‍🤝‍🧑🏿", - ["people_holding_hands_tone3_tone1"] = "🧑🏽‍🤝‍🧑🏻", - ["people_holding_hands_medium_skin_tone_light_skin_tone"] = "🧑🏽‍🤝‍🧑🏻", - ["people_holding_hands_tone3_tone2"] = "🧑🏽‍🤝‍🧑🏼", - ["people_holding_hands_medium_skin_tone_medium_light_skin_tone"] = "🧑🏽‍🤝‍🧑🏼", - ["people_holding_hands_tone3"] = "🧑🏽‍🤝‍🧑🏽", - ["people_holding_hands_medium_skin_tone"] = "🧑🏽‍🤝‍🧑🏽", - ["people_holding_hands_tone3_tone4"] = "🧑🏽‍🤝‍🧑🏾", - ["people_holding_hands_medium_skin_tone_medium_dark_skin_tone"] = "🧑🏽‍🤝‍🧑🏾", - ["people_holding_hands_tone3_tone5"] = "🧑🏽‍🤝‍🧑🏿", - ["people_holding_hands_medium_skin_tone_dark_skin_tone"] = "🧑🏽‍🤝‍🧑🏿", - ["people_holding_hands_tone4_tone1"] = "🧑🏾‍🤝‍🧑🏻", - ["people_holding_hands_medium_dark_skin_tone_light_skin_tone"] = "🧑🏾‍🤝‍🧑🏻", - ["people_holding_hands_tone4_tone2"] = "🧑🏾‍🤝‍🧑🏼", - ["people_holding_hands_medium_dark_skin_tone_medium_light_skin_tone"] = "🧑🏾‍🤝‍🧑🏼", - ["people_holding_hands_tone4_tone3"] = "🧑🏾‍🤝‍🧑🏽", - ["people_holding_hands_medium_dark_skin_tone_medium_skin_tone"] = "🧑🏾‍🤝‍🧑🏽", - ["people_holding_hands_tone4"] = "🧑🏾‍🤝‍🧑🏾", - ["people_holding_hands_medium_dark_skin_tone"] = "🧑🏾‍🤝‍🧑🏾", - ["people_holding_hands_tone4_tone5"] = "🧑🏾‍🤝‍🧑🏿", - ["people_holding_hands_medium_dark_skin_tone_dark_skin_tone"] = "🧑🏾‍🤝‍🧑🏿", - ["people_holding_hands_tone5_tone1"] = "🧑🏿‍🤝‍🧑🏻", - ["people_holding_hands_dark_skin_tone_light_skin_tone"] = "🧑🏿‍🤝‍🧑🏻", - ["people_holding_hands_tone5_tone2"] = "🧑🏿‍🤝‍🧑🏼", - ["people_holding_hands_dark_skin_tone_medium_light_skin_tone"] = "🧑🏿‍🤝‍🧑🏼", - ["people_holding_hands_tone5_tone3"] = "🧑🏿‍🤝‍🧑🏽", - ["people_holding_hands_dark_skin_tone_medium_skin_tone"] = "🧑🏿‍🤝‍🧑🏽", - ["people_holding_hands_tone5_tone4"] = "🧑🏿‍🤝‍🧑🏾", - ["people_holding_hands_dark_skin_tone_medium_dark_skin_tone"] = "🧑🏿‍🤝‍🧑🏾", - ["people_holding_hands_tone5"] = "🧑🏿‍🤝‍🧑🏿", - ["people_holding_hands_dark_skin_tone"] = "🧑🏿‍🤝‍🧑🏿", - ["couple"] = "👫", - ["woman_and_man_holding_hands_tone1"] = "👫🏻", - ["woman_and_man_holding_hands_light_skin_tone"] = "👫🏻", - ["woman_and_man_holding_hands_tone1_tone2"] = "👩🏻‍🤝‍👨🏼", - ["woman_and_man_holding_hands_light_skin_tone_medium_light_skin_tone"] = "👩🏻‍🤝‍👨🏼", - ["woman_and_man_holding_hands_tone1_tone3"] = "👩🏻‍🤝‍👨🏽", - ["woman_and_man_holding_hands_light_skin_tone_medium_skin_tone"] = "👩🏻‍🤝‍👨🏽", - ["woman_and_man_holding_hands_tone1_tone4"] = "👩🏻‍🤝‍👨🏾", - ["woman_and_man_holding_hands_light_skin_tone_medium_dark_skin_tone"] = "👩🏻‍🤝‍👨🏾", - ["woman_and_man_holding_hands_tone1_tone5"] = "👩🏻‍🤝‍👨🏿", - ["woman_and_man_holding_hands_light_skin_tone_dark_skin_tone"] = "👩🏻‍🤝‍👨🏿", - ["woman_and_man_holding_hands_tone2_tone1"] = "👩🏼‍🤝‍👨🏻", - ["woman_and_man_holding_hands_medium_light_skin_tone_light_skin_tone"] = "👩🏼‍🤝‍👨🏻", - ["woman_and_man_holding_hands_tone2"] = "👫🏼", - ["woman_and_man_holding_hands_medium_light_skin_tone"] = "👫🏼", - ["woman_and_man_holding_hands_tone2_tone3"] = "👩🏼‍🤝‍👨🏽", - ["woman_and_man_holding_hands_medium_light_skin_tone_medium_skin_tone"] = - "👩🏼‍🤝‍👨🏽", - ["woman_and_man_holding_hands_tone2_tone4"] = "👩🏼‍🤝‍👨🏾", - ["woman_and_man_holding_hands_medium_light_skin_tone_medium_dark_skin_tone"] = - "👩🏼‍🤝‍👨🏾", - ["woman_and_man_holding_hands_tone2_tone5"] = "👩🏼‍🤝‍👨🏿", - ["woman_and_man_holding_hands_medium_light_skin_tone_dark_skin_tone"] = "👩🏼‍🤝‍👨🏿", - ["woman_and_man_holding_hands_tone3_tone1"] = "👩🏽‍🤝‍👨🏻", - ["woman_and_man_holding_hands_medium_skin_tone_light_skin_tone"] = "👩🏽‍🤝‍👨🏻", - ["woman_and_man_holding_hands_tone3_tone2"] = "👩🏽‍🤝‍👨🏼", - ["woman_and_man_holding_hands_medium_skin_tone_medium_light_skin_tone"] = - "👩🏽‍🤝‍👨🏼", - ["woman_and_man_holding_hands_tone3"] = "👫🏽", - ["woman_and_man_holding_hands_medium_skin_tone"] = "👫🏽", - ["woman_and_man_holding_hands_tone3_tone4"] = "👩🏽‍🤝‍👨🏾", - ["woman_and_man_holding_hands_medium_skin_tone_medium_dark_skin_tone"] = "👩🏽‍🤝‍👨🏾", - ["woman_and_man_holding_hands_tone3_tone5"] = "👩🏽‍🤝‍👨🏿", - ["woman_and_man_holding_hands_medium_skin_tone_dark_skin_tone"] = "👩🏽‍🤝‍👨🏿", - ["woman_and_man_holding_hands_tone4_tone1"] = "👩🏾‍🤝‍👨🏻", - ["woman_and_man_holding_hands_medium_dark_skin_tone_light_skin_tone"] = "👩🏾‍🤝‍👨🏻", - ["woman_and_man_holding_hands_tone4_tone2"] = "👩🏾‍🤝‍👨🏼", - ["woman_and_man_holding_hands_medium_dark_skin_tone_medium_light_skin_tone"] = - "👩🏾‍🤝‍👨🏼", - ["woman_and_man_holding_hands_tone4_tone3"] = "👩🏾‍🤝‍👨🏽", - ["woman_and_man_holding_hands_medium_dark_skin_tone_medium_skin_tone"] = "👩🏾‍🤝‍👨🏽", - ["woman_and_man_holding_hands_tone4"] = "👫🏾", - ["woman_and_man_holding_hands_medium_dark_skin_tone"] = "👫🏾", - ["woman_and_man_holding_hands_tone4_tone5"] = "👩🏾‍🤝‍👨🏿", - ["woman_and_man_holding_hands_medium_dark_skin_tone_dark_skin_tone"] = "👩🏾‍🤝‍👨🏿", - ["woman_and_man_holding_hands_tone5_tone1"] = "👩🏿‍🤝‍👨🏻", - ["woman_and_man_holding_hands_dark_skin_tone_light_skin_tone"] = "👩🏿‍🤝‍👨🏻", - ["woman_and_man_holding_hands_tone5_tone2"] = "👩🏿‍🤝‍👨🏼", - ["woman_and_man_holding_hands_dark_skin_tone_medium_light_skin_tone"] = "👩🏿‍🤝‍👨🏼", - ["woman_and_man_holding_hands_tone5_tone3"] = "👩🏿‍🤝‍👨🏽", - ["woman_and_man_holding_hands_dark_skin_tone_medium_skin_tone"] = "👩🏿‍🤝‍👨🏽", - ["woman_and_man_holding_hands_tone5_tone4"] = "👩🏿‍🤝‍👨🏾", - ["woman_and_man_holding_hands_dark_skin_tone_medium_dark_skin_tone"] = "👩🏿‍🤝‍👨🏾", - ["woman_and_man_holding_hands_tone5"] = "👫🏿", - ["woman_and_man_holding_hands_dark_skin_tone"] = "👫🏿", - ["two_women_holding_hands"] = "👭", - ["women_holding_hands_tone1"] = "👭🏻", - ["women_holding_hands_light_skin_tone"] = "👭🏻", - ["women_holding_hands_tone1_tone2"] = "👩🏻‍🤝‍👩🏼", - ["women_holding_hands_light_skin_tone_medium_light_skin_tone"] = "👩🏻‍🤝‍👩🏼", - ["women_holding_hands_tone1_tone3"] = "👩🏻‍🤝‍👩🏽", - ["women_holding_hands_light_skin_tone_medium_skin_tone"] = "👩🏻‍🤝‍👩🏽", - ["women_holding_hands_tone1_tone4"] = "👩🏻‍🤝‍👩🏾", - ["women_holding_hands_light_skin_tone_medium_dark_skin_tone"] = "👩🏻‍🤝‍👩🏾", - ["women_holding_hands_tone1_tone5"] = "👩🏻‍🤝‍👩🏿", - ["women_holding_hands_light_skin_tone_dark_skin_tone"] = "👩🏻‍🤝‍👩🏿", - ["women_holding_hands_tone2_tone1"] = "👩🏼‍🤝‍👩🏻", - ["women_holding_hands_medium_light_skin_tone_light_skin_tone"] = "👩🏼‍🤝‍👩🏻", - ["women_holding_hands_tone2"] = "👭🏼", - ["women_holding_hands_medium_light_skin_tone"] = "👭🏼", - ["women_holding_hands_tone2_tone3"] = "👩🏼‍🤝‍👩🏽", - ["women_holding_hands_medium_light_skin_tone_medium_skin_tone"] = "👩🏼‍🤝‍👩🏽", - ["women_holding_hands_tone2_tone4"] = "👩🏼‍🤝‍👩🏾", - ["women_holding_hands_medium_light_skin_tone_medium_dark_skin_tone"] = "👩🏼‍🤝‍👩🏾", - ["women_holding_hands_tone2_tone5"] = "👩🏼‍🤝‍👩🏿", - ["women_holding_hands_medium_light_skin_tone_dark_skin_tone"] = "👩🏼‍🤝‍👩🏿", - ["women_holding_hands_tone3_tone1"] = "👩🏽‍🤝‍👩🏻", - ["women_holding_hands_medium_skin_tone_light_skin_tone"] = "👩🏽‍🤝‍👩🏻", - ["women_holding_hands_tone3_tone2"] = "👩🏽‍🤝‍👩🏼", - ["women_holding_hands_medium_skin_tone_medium_light_skin_tone"] = "👩🏽‍🤝‍👩🏼", - ["women_holding_hands_tone3"] = "👭🏽", - ["women_holding_hands_medium_skin_tone"] = "👭🏽", - ["women_holding_hands_tone3_tone4"] = "👩🏽‍🤝‍👩🏾", - ["women_holding_hands_medium_skin_tone_medium_dark_skin_tone"] = "👩🏽‍🤝‍👩🏾", - ["women_holding_hands_tone3_tone5"] = "👩🏽‍🤝‍👩🏿", - ["women_holding_hands_medium_skin_tone_dark_skin_tone"] = "👩🏽‍🤝‍👩🏿", - ["women_holding_hands_tone4_tone1"] = "👩🏾‍🤝‍👩🏻", - ["women_holding_hands_medium_dark_skin_tone_light_skin_tone"] = "👩🏾‍🤝‍👩🏻", - ["women_holding_hands_tone4_tone2"] = "👩🏾‍🤝‍👩🏼", - ["women_holding_hands_medium_dark_skin_tone_medium_light_skin_tone"] = "👩🏾‍🤝‍👩🏼", - ["women_holding_hands_tone4_tone3"] = "👩🏾‍🤝‍👩🏽", - ["women_holding_hands_medium_dark_skin_tone_medium_skin_tone"] = "👩🏾‍🤝‍👩🏽", - ["women_holding_hands_tone4"] = "👭🏾", - ["women_holding_hands_medium_dark_skin_tone"] = "👭🏾", - ["women_holding_hands_tone4_tone5"] = "👩🏾‍🤝‍👩🏿", - ["women_holding_hands_medium_dark_skin_tone_dark_skin_tone"] = "👩🏾‍🤝‍👩🏿", - ["women_holding_hands_tone5_tone1"] = "👩🏿‍🤝‍👩🏻", - ["women_holding_hands_dark_skin_tone_light_skin_tone"] = "👩🏿‍🤝‍👩🏻", - ["women_holding_hands_tone5_tone2"] = "👩🏿‍🤝‍👩🏼", - ["women_holding_hands_dark_skin_tone_medium_light_skin_tone"] = "👩🏿‍🤝‍👩🏼", - ["women_holding_hands_tone5_tone3"] = "👩🏿‍🤝‍👩🏽", - ["women_holding_hands_dark_skin_tone_medium_skin_tone"] = "👩🏿‍🤝‍👩🏽", - ["women_holding_hands_tone5_tone4"] = "👩🏿‍🤝‍👩🏾", - ["women_holding_hands_dark_skin_tone_medium_dark_skin_tone"] = "👩🏿‍🤝‍👩🏾", - ["women_holding_hands_tone5"] = "👭🏿", - ["women_holding_hands_dark_skin_tone"] = "👭🏿", - ["two_men_holding_hands"] = "👬", - ["men_holding_hands_tone1"] = "👬🏻", - ["men_holding_hands_light_skin_tone"] = "👬🏻", - ["men_holding_hands_tone1_tone2"] = "👨🏻‍🤝‍👨🏼", - ["men_holding_hands_light_skin_tone_medium_light_skin_tone"] = "👨🏻‍🤝‍👨🏼", - ["men_holding_hands_tone1_tone3"] = "👨🏻‍🤝‍👨🏽", - ["men_holding_hands_light_skin_tone_medium_skin_tone"] = "👨🏻‍🤝‍👨🏽", - ["men_holding_hands_tone1_tone4"] = "👨🏻‍🤝‍👨🏾", - ["men_holding_hands_light_skin_tone_medium_dark_skin_tone"] = "👨🏻‍🤝‍👨🏾", - ["men_holding_hands_tone1_tone5"] = "👨🏻‍🤝‍👨🏿", - ["men_holding_hands_light_skin_tone_dark_skin_tone"] = "👨🏻‍🤝‍👨🏿", - ["men_holding_hands_tone2_tone1"] = "👨🏼‍🤝‍👨🏻", - ["men_holding_hands_medium_light_skin_tone_light_skin_tone"] = "👨🏼‍🤝‍👨🏻", - ["men_holding_hands_tone2"] = "👬🏼", - ["men_holding_hands_medium_light_skin_tone"] = "👬🏼", - ["men_holding_hands_tone2_tone3"] = "👨🏼‍🤝‍👨🏽", - ["men_holding_hands_medium_light_skin_tone_medium_skin_tone"] = "👨🏼‍🤝‍👨🏽", - ["men_holding_hands_tone2_tone4"] = "👨🏼‍🤝‍👨🏾", - ["men_holding_hands_medium_light_skin_tone_medium_dark_skin_tone"] = "👨🏼‍🤝‍👨🏾", - ["men_holding_hands_tone2_tone5"] = "👨🏼‍🤝‍👨🏿", - ["men_holding_hands_medium_light_skin_tone_dark_skin_tone"] = "👨🏼‍🤝‍👨🏿", - ["men_holding_hands_tone3_tone1"] = "👨🏽‍🤝‍👨🏻", - ["men_holding_hands_medium_skin_tone_light_skin_tone"] = "👨🏽‍🤝‍👨🏻", - ["men_holding_hands_tone3_tone2"] = "👨🏽‍🤝‍👨🏼", - ["men_holding_hands_medium_skin_tone_medium_light_skin_tone"] = "👨🏽‍🤝‍👨🏼", - ["men_holding_hands_tone3"] = "👬🏽", - ["men_holding_hands_medium_skin_tone"] = "👬🏽", - ["men_holding_hands_tone3_tone4"] = "👨🏽‍🤝‍👨🏾", - ["men_holding_hands_medium_skin_tone_medium_dark_skin_tone"] = "👨🏽‍🤝‍👨🏾", - ["men_holding_hands_tone3_tone5"] = "👨🏽‍🤝‍👨🏿", - ["men_holding_hands_medium_skin_tone_dark_skin_tone"] = "👨🏽‍🤝‍👨🏿", - ["men_holding_hands_tone4_tone1"] = "👨🏾‍🤝‍👨🏻", - ["men_holding_hands_medium_dark_skin_tone_light_skin_tone"] = "👨🏾‍🤝‍👨🏻", - ["men_holding_hands_tone4_tone2"] = "👨🏾‍🤝‍👨🏼", - ["men_holding_hands_medium_dark_skin_tone_medium_light_skin_tone"] = "👨🏾‍🤝‍👨🏼", - ["men_holding_hands_tone4_tone3"] = "👨🏾‍🤝‍👨🏽", - ["men_holding_hands_medium_dark_skin_tone_medium_skin_tone"] = "👨🏾‍🤝‍👨🏽", - ["men_holding_hands_tone4"] = "👬🏾", - ["men_holding_hands_medium_dark_skin_tone"] = "👬🏾", - ["men_holding_hands_tone4_tone5"] = "👨🏾‍🤝‍👨🏿", - ["men_holding_hands_medium_dark_skin_tone_dark_skin_tone"] = "👨🏾‍🤝‍👨🏿", - ["men_holding_hands_tone5_tone1"] = "👨🏿‍🤝‍👨🏻", - ["men_holding_hands_dark_skin_tone_light_skin_tone"] = "👨🏿‍🤝‍👨🏻", - ["men_holding_hands_tone5_tone2"] = "👨🏿‍🤝‍👨🏼", - ["men_holding_hands_dark_skin_tone_medium_light_skin_tone"] = "👨🏿‍🤝‍👨🏼", - ["men_holding_hands_tone5_tone3"] = "👨🏿‍🤝‍👨🏽", - ["men_holding_hands_dark_skin_tone_medium_skin_tone"] = "👨🏿‍🤝‍👨🏽", - ["men_holding_hands_tone5_tone4"] = "👨🏿‍🤝‍👨🏾", - ["men_holding_hands_dark_skin_tone_medium_dark_skin_tone"] = "👨🏿‍🤝‍👨🏾", - ["men_holding_hands_tone5"] = "👬🏿", - ["men_holding_hands_dark_skin_tone"] = "👬🏿", - ["couple_with_heart"] = "💑", - ["couple_with_heart_tone1"] = "💑🏻", - ["couple_with_heart_light_skin_tone"] = "💑🏻", - ["couple_with_heart_person_person_tone1_tone2"] = "🧑🏻‍❤️‍🧑🏼", - ["couple_with_heart_person_person_light_skin_tone_medium_light_skin_tone"] = - "🧑🏻‍❤️‍🧑🏼", - ["couple_with_heart_person_person_tone1_tone3"] = "🧑🏻‍❤️‍🧑🏽", - ["couple_with_heart_person_person_light_skin_tone_medium_skin_tone"] = "🧑🏻‍❤️‍🧑🏽", - ["couple_with_heart_person_person_tone1_tone4"] = "🧑🏻‍❤️‍🧑🏾", - ["couple_with_heart_person_person_light_skin_tone_medium_dark_skin_tone"] = - "🧑🏻‍❤️‍🧑🏾", - ["couple_with_heart_person_person_tone1_tone5"] = "🧑🏻‍❤️‍🧑🏿", - ["couple_with_heart_person_person_light_skin_tone_dark_skin_tone"] = "🧑🏻‍❤️‍🧑🏿", - ["couple_with_heart_person_person_tone2_tone1"] = "🧑🏼‍❤️‍🧑🏻", - ["couple_with_heart_person_person_medium_light_skin_tone_light_skin_tone"] = - "🧑🏼‍❤️‍🧑🏻", - ["couple_with_heart_tone2"] = "💑🏼", - ["couple_with_heart_medium_light_skin_tone"] = "💑🏼", - ["couple_with_heart_person_person_tone2_tone3"] = "🧑🏼‍❤️‍🧑🏽", - ["couple_with_heart_person_person_medium_light_skin_tone_medium_skin_tone"] = - "🧑🏼‍❤️‍🧑🏽", - ["couple_with_heart_person_person_tone2_tone4"] = "🧑🏼‍❤️‍🧑🏾", - ["couple_with_heart_person_person_medium_light_skin_tone_medium_dark_skin_tone"] = - "🧑🏼‍❤️‍🧑🏾", - ["couple_with_heart_person_person_tone2_tone5"] = "🧑🏼‍❤️‍🧑🏿", - ["couple_with_heart_person_person_medium_light_skin_tone_dark_skin_tone"] = - "🧑🏼‍❤️‍🧑🏿", - ["couple_with_heart_person_person_tone3_tone1"] = "🧑🏽‍❤️‍🧑🏻", - ["couple_with_heart_person_person_medium_skin_tone_light_skin_tone"] = "🧑🏽‍❤️‍🧑🏻", - ["couple_with_heart_person_person_tone3_tone2"] = "🧑🏽‍❤️‍🧑🏼", - ["couple_with_heart_person_person_medium_skin_tone_medium_light_skin_tone"] = - "🧑🏽‍❤️‍🧑🏼", - ["couple_with_heart_tone3"] = "💑🏽", - ["couple_with_heart_medium_skin_tone"] = "💑🏽", - ["couple_with_heart_person_person_tone3_tone4"] = "🧑🏽‍❤️‍🧑🏾", - ["couple_with_heart_person_person_medium_skin_tone_medium_dark_skin_tone"] = - "🧑🏽‍❤️‍🧑🏾", - ["couple_with_heart_person_person_tone3_tone5"] = "🧑🏽‍❤️‍🧑🏿", - ["couple_with_heart_person_person_medium_skin_tone_dark_skin_tone"] = "🧑🏽‍❤️‍🧑🏿", - ["couple_with_heart_person_person_tone4_tone1"] = "🧑🏾‍❤️‍🧑🏻", - ["couple_with_heart_person_person_medium_dark_skin_tone_light_skin_tone"] = - "🧑🏾‍❤️‍🧑🏻", - ["couple_with_heart_person_person_tone4_tone2"] = "🧑🏾‍❤️‍🧑🏼", - ["couple_with_heart_person_person_medium_dark_skin_tone_medium_light_skin_tone"] = - "🧑🏾‍❤️‍🧑🏼", - ["couple_with_heart_person_person_tone4_tone3"] = "🧑🏾‍❤️‍🧑🏽", - ["couple_with_heart_person_person_medium_dark_skin_tone_medium_skin_tone"] = - "🧑🏾‍❤️‍🧑🏽", - ["couple_with_heart_tone4"] = "💑🏾", - ["couple_with_heart_medium_dark_skin_tone"] = "💑🏾", - ["couple_with_heart_person_person_tone4_tone5"] = "🧑🏾‍❤️‍🧑🏿", - ["couple_with_heart_person_person_medium_dark_skin_tone_dark_skin_tone"] = - "🧑🏾‍❤️‍🧑🏿", - ["couple_with_heart_person_person_tone5_tone1"] = "🧑🏿‍❤️‍🧑🏻", - ["couple_with_heart_person_person_dark_skin_tone_light_skin_tone"] = "🧑🏿‍❤️‍🧑🏻", - ["couple_with_heart_person_person_tone5_tone2"] = "🧑🏿‍❤️‍🧑🏼", - ["couple_with_heart_person_person_dark_skin_tone_medium_light_skin_tone"] = - "🧑🏿‍❤️‍🧑🏼", - ["couple_with_heart_person_person_tone5_tone3"] = "🧑🏿‍❤️‍🧑🏽", - ["couple_with_heart_person_person_dark_skin_tone_medium_skin_tone"] = "🧑🏿‍❤️‍🧑🏽", - ["couple_with_heart_person_person_tone5_tone4"] = "🧑🏿‍❤️‍🧑🏾", - ["couple_with_heart_person_person_dark_skin_tone_medium_dark_skin_tone"] = - "🧑🏿‍❤️‍🧑🏾", - ["couple_with_heart_tone5"] = "💑🏿", - ["couple_with_heart_dark_skin_tone"] = "💑🏿", - ["couple_with_heart_woman_man"] = "👩‍❤️‍👨", - ["couple_with_heart_woman_man_tone1"] = "👩🏻‍❤️‍👨🏻", - ["couple_with_heart_woman_man_light_skin_tone"] = "👩🏻‍❤️‍👨🏻", - ["couple_with_heart_woman_man_tone1_tone2"] = "👩🏻‍❤️‍👨🏼", - ["couple_with_heart_woman_man_light_skin_tone_medium_light_skin_tone"] = "👩🏻‍❤️‍👨🏼", - ["couple_with_heart_woman_man_tone1_tone3"] = "👩🏻‍❤️‍👨🏽", - ["couple_with_heart_woman_man_light_skin_tone_medium_skin_tone"] = "👩🏻‍❤️‍👨🏽", - ["couple_with_heart_woman_man_tone1_tone4"] = "👩🏻‍❤️‍👨🏾", - ["couple_with_heart_woman_man_light_skin_tone_medium_dark_skin_tone"] = "👩🏻‍❤️‍👨🏾", - ["couple_with_heart_woman_man_tone1_tone5"] = "👩🏻‍❤️‍👨🏿", - ["couple_with_heart_woman_man_light_skin_tone_dark_skin_tone"] = "👩🏻‍❤️‍👨🏿", - ["couple_with_heart_woman_man_tone2_tone1"] = "👩🏼‍❤️‍👨🏻", - ["couple_with_heart_woman_man_medium_light_skin_tone_light_skin_tone"] = "👩🏼‍❤️‍👨🏻", - ["couple_with_heart_woman_man_tone2"] = "👩🏼‍❤️‍👨🏼", - ["couple_with_heart_woman_man_medium_light_skin_tone"] = "👩🏼‍❤️‍👨🏼", - ["couple_with_heart_woman_man_tone2_tone3"] = "👩🏼‍❤️‍👨🏽", - ["couple_with_heart_woman_man_medium_light_skin_tone_medium_skin_tone"] = - "👩🏼‍❤️‍👨🏽", - ["couple_with_heart_woman_man_tone2_tone4"] = "👩🏼‍❤️‍👨🏾", - ["couple_with_heart_woman_man_medium_light_skin_tone_medium_dark_skin_tone"] = - "👩🏼‍❤️‍👨🏾", - ["couple_with_heart_woman_man_tone2_tone5"] = "👩🏼‍❤️‍👨🏿", - ["couple_with_heart_woman_man_medium_light_skin_tone_dark_skin_tone"] = "👩🏼‍❤️‍👨🏿", - ["couple_with_heart_woman_man_tone3_tone1"] = "👩🏽‍❤️‍👨🏻", - ["couple_with_heart_woman_man_medium_skin_tone_light_skin_tone"] = "👩🏽‍❤️‍👨🏻", - ["couple_with_heart_woman_man_tone3_tone2"] = "👩🏽‍❤️‍👨🏼", - ["couple_with_heart_woman_man_medium_skin_tone_medium_light_skin_tone"] = - "👩🏽‍❤️‍👨🏼", - ["couple_with_heart_woman_man_tone3"] = "👩🏽‍❤️‍👨🏽", - ["couple_with_heart_woman_man_medium_skin_tone"] = "👩🏽‍❤️‍👨🏽", - ["couple_with_heart_woman_man_tone3_tone4"] = "👩🏽‍❤️‍👨🏾", - ["couple_with_heart_woman_man_medium_skin_tone_medium_dark_skin_tone"] = "👩🏽‍❤️‍👨🏾", - ["couple_with_heart_woman_man_tone3_tone5"] = "👩🏽‍❤️‍👨🏿", - ["couple_with_heart_woman_man_medium_skin_tone_dark_skin_tone"] = "👩🏽‍❤️‍👨🏿", - ["couple_with_heart_woman_man_tone4_tone1"] = "👩🏾‍❤️‍👨🏻", - ["couple_with_heart_woman_man_medium_dark_skin_tone_light_skin_tone"] = "👩🏾‍❤️‍👨🏻", - ["couple_with_heart_woman_man_tone4_tone2"] = "👩🏾‍❤️‍👨🏼", - ["couple_with_heart_woman_man_medium_dark_skin_tone_medium_light_skin_tone"] = - "👩🏾‍❤️‍👨🏼", - ["couple_with_heart_woman_man_tone4_tone3"] = "👩🏾‍❤️‍👨🏽", - ["couple_with_heart_woman_man_medium_dark_skin_tone_medium_skin_tone"] = "👩🏾‍❤️‍👨🏽", - ["couple_with_heart_woman_man_tone4"] = "👩🏾‍❤️‍👨🏾", - ["couple_with_heart_woman_man_medium_dark_skin_tone"] = "👩🏾‍❤️‍👨🏾", - ["couple_with_heart_woman_man_tone4_tone5"] = "👩🏾‍❤️‍👨🏿", - ["couple_with_heart_woman_man_medium_dark_skin_tone_dark_skin_tone"] = "👩🏾‍❤️‍👨🏿", - ["couple_with_heart_woman_man_tone5_tone1"] = "👩🏿‍❤️‍👨🏻", - ["couple_with_heart_woman_man_dark_skin_tone_light_skin_tone"] = "👩🏿‍❤️‍👨🏻", - ["couple_with_heart_woman_man_tone5_tone2"] = "👩🏿‍❤️‍👨🏼", - ["couple_with_heart_woman_man_dark_skin_tone_medium_light_skin_tone"] = "👩🏿‍❤️‍👨🏼", - ["couple_with_heart_woman_man_tone5_tone3"] = "👩🏿‍❤️‍👨🏽", - ["couple_with_heart_woman_man_dark_skin_tone_medium_skin_tone"] = "👩🏿‍❤️‍👨🏽", - ["couple_with_heart_woman_man_tone5_tone4"] = "👩🏿‍❤️‍👨🏾", - ["couple_with_heart_woman_man_dark_skin_tone_medium_dark_skin_tone"] = "👩🏿‍❤️‍👨🏾", - ["couple_with_heart_woman_man_tone5"] = "👩🏿‍❤️‍👨🏿", - ["couple_with_heart_woman_man_dark_skin_tone"] = "👩🏿‍❤️‍👨🏿", - ["couple_ww"] = "👩‍❤️‍👩", - ["couple_with_heart_ww"] = "👩‍❤️‍👩", - ["couple_with_heart_woman_woman_tone1"] = "👩🏻‍❤️‍👩🏻", - ["couple_with_heart_woman_woman_light_skin_tone"] = "👩🏻‍❤️‍👩🏻", - ["couple_with_heart_woman_woman_tone1_tone2"] = "👩🏻‍❤️‍👩🏼", - ["couple_with_heart_woman_woman_light_skin_tone_medium_light_skin_tone"] = - "👩🏻‍❤️‍👩🏼", - ["couple_with_heart_woman_woman_tone1_tone3"] = "👩🏻‍❤️‍👩🏽", - ["couple_with_heart_woman_woman_light_skin_tone_medium_skin_tone"] = "👩🏻‍❤️‍👩🏽", - ["couple_with_heart_woman_woman_tone1_tone4"] = "👩🏻‍❤️‍👩🏾", - ["couple_with_heart_woman_woman_light_skin_tone_medium_dark_skin_tone"] = - "👩🏻‍❤️‍👩🏾", - ["couple_with_heart_woman_woman_tone1_tone5"] = "👩🏻‍❤️‍👩🏿", - ["couple_with_heart_woman_woman_light_skin_tone_dark_skin_tone"] = "👩🏻‍❤️‍👩🏿", - ["couple_with_heart_woman_woman_tone2_tone1"] = "👩🏼‍❤️‍👩🏻", - ["couple_with_heart_woman_woman_medium_light_skin_tone_light_skin_tone"] = - "👩🏼‍❤️‍👩🏻", - ["couple_with_heart_woman_woman_tone2"] = "👩🏼‍❤️‍👩🏼", - ["couple_with_heart_woman_woman_medium_light_skin_tone"] = "👩🏼‍❤️‍👩🏼", - ["couple_with_heart_woman_woman_tone2_tone3"] = "👩🏼‍❤️‍👩🏽", - ["couple_with_heart_woman_woman_medium_light_skin_tone_medium_skin_tone"] = - "👩🏼‍❤️‍👩🏽", - ["couple_with_heart_woman_woman_tone2_tone4"] = "👩🏼‍❤️‍👩🏾", - ["couple_with_heart_woman_woman_medium_light_skin_tone_medium_dark_skin_tone"] = - "👩🏼‍❤️‍👩🏾", - ["couple_with_heart_woman_woman_tone2_tone5"] = "👩🏼‍❤️‍👩🏿", - ["couple_with_heart_woman_woman_medium_light_skin_tone_dark_skin_tone"] = - "👩🏼‍❤️‍👩🏿", - ["couple_with_heart_woman_woman_tone3_tone1"] = "👩🏽‍❤️‍👩🏻", - ["couple_with_heart_woman_woman_medium_skin_tone_light_skin_tone"] = "👩🏽‍❤️‍👩🏻", - ["couple_with_heart_woman_woman_tone3_tone2"] = "👩🏽‍❤️‍👩🏼", - ["couple_with_heart_woman_woman_medium_skin_tone_medium_light_skin_tone"] = - "👩🏽‍❤️‍👩🏼", - ["couple_with_heart_woman_woman_tone3"] = "👩🏽‍❤️‍👩🏽", - ["couple_with_heart_woman_woman_medium_skin_tone"] = "👩🏽‍❤️‍👩🏽", - ["couple_with_heart_woman_woman_tone3_tone4"] = "👩🏽‍❤️‍👩🏾", - ["couple_with_heart_woman_woman_medium_skin_tone_medium_dark_skin_tone"] = - "👩🏽‍❤️‍👩🏾", - ["couple_with_heart_woman_woman_tone3_tone5"] = "👩🏽‍❤️‍👩🏿", - ["couple_with_heart_woman_woman_medium_skin_tone_dark_skin_tone"] = "👩🏽‍❤️‍👩🏿", - ["couple_with_heart_woman_woman_tone4_tone1"] = "👩🏾‍❤️‍👩🏻", - ["couple_with_heart_woman_woman_medium_dark_skin_tone_light_skin_tone"] = - "👩🏾‍❤️‍👩🏻", - ["couple_with_heart_woman_woman_tone4_tone2"] = "👩🏾‍❤️‍👩🏼", - ["couple_with_heart_woman_woman_medium_dark_skin_tone_medium_light_skin_tone"] = - "👩🏾‍❤️‍👩🏼", - ["couple_with_heart_woman_woman_tone4_tone3"] = "👩🏾‍❤️‍👩🏽", - ["couple_with_heart_woman_woman_medium_dark_skin_tone_medium_skin_tone"] = - "👩🏾‍❤️‍👩🏽", - ["couple_with_heart_woman_woman_tone4"] = "👩🏾‍❤️‍👩🏾", - ["couple_with_heart_woman_woman_medium_dark_skin_tone"] = "👩🏾‍❤️‍👩🏾", - ["couple_with_heart_woman_woman_tone4_tone5"] = "👩🏾‍❤️‍👩🏿", - ["couple_with_heart_woman_woman_medium_dark_skin_tone_dark_skin_tone"] = "👩🏾‍❤️‍👩🏿", - ["couple_with_heart_woman_woman_tone5_tone1"] = "👩🏿‍❤️‍👩🏻", - ["couple_with_heart_woman_woman_dark_skin_tone_light_skin_tone"] = "👩🏿‍❤️‍👩🏻", - ["couple_with_heart_woman_woman_tone5_tone2"] = "👩🏿‍❤️‍👩🏼", - ["couple_with_heart_woman_woman_dark_skin_tone_medium_light_skin_tone"] = - "👩🏿‍❤️‍👩🏼", - ["couple_with_heart_woman_woman_tone5_tone3"] = "👩🏿‍❤️‍👩🏽", - ["couple_with_heart_woman_woman_dark_skin_tone_medium_skin_tone"] = "👩🏿‍❤️‍👩🏽", - ["couple_with_heart_woman_woman_tone5_tone4"] = "👩🏿‍❤️‍👩🏾", - ["couple_with_heart_woman_woman_dark_skin_tone_medium_dark_skin_tone"] = "👩🏿‍❤️‍👩🏾", - ["couple_with_heart_woman_woman_tone5"] = "👩🏿‍❤️‍👩🏿", - ["couple_with_heart_woman_woman_dark_skin_tone"] = "👩🏿‍❤️‍👩🏿", - ["couple_mm"] = "👨‍❤️‍👨", - ["couple_with_heart_mm"] = "👨‍❤️‍👨", - ["couple_with_heart_man_man_tone1"] = "👨🏻‍❤️‍👨🏻", - ["couple_with_heart_man_man_light_skin_tone"] = "👨🏻‍❤️‍👨🏻", - ["couple_with_heart_man_man_tone1_tone2"] = "👨🏻‍❤️‍👨🏼", - ["couple_with_heart_man_man_light_skin_tone_medium_light_skin_tone"] = "👨🏻‍❤️‍👨🏼", - ["couple_with_heart_man_man_tone1_tone3"] = "👨🏻‍❤️‍👨🏽", - ["couple_with_heart_man_man_light_skin_tone_medium_skin_tone"] = "👨🏻‍❤️‍👨🏽", - ["couple_with_heart_man_man_tone1_tone4"] = "👨🏻‍❤️‍👨🏾", - ["couple_with_heart_man_man_light_skin_tone_medium_dark_skin_tone"] = "👨🏻‍❤️‍👨🏾", - ["couple_with_heart_man_man_tone1_tone5"] = "👨🏻‍❤️‍👨🏿", - ["couple_with_heart_man_man_light_skin_tone_dark_skin_tone"] = "👨🏻‍❤️‍👨🏿", - ["couple_with_heart_man_man_tone2_tone1"] = "👨🏼‍❤️‍👨🏻", - ["couple_with_heart_man_man_medium_light_skin_tone_light_skin_tone"] = "👨🏼‍❤️‍👨🏻", - ["couple_with_heart_man_man_tone2"] = "👨🏼‍❤️‍👨🏼", - ["couple_with_heart_man_man_medium_light_skin_tone"] = "👨🏼‍❤️‍👨🏼", - ["couple_with_heart_man_man_tone2_tone3"] = "👨🏼‍❤️‍👨🏽", - ["couple_with_heart_man_man_medium_light_skin_tone_medium_skin_tone"] = "👨🏼‍❤️‍👨🏽", - ["couple_with_heart_man_man_tone2_tone4"] = "👨🏼‍❤️‍👨🏾", - ["couple_with_heart_man_man_medium_light_skin_tone_medium_dark_skin_tone"] = - "👨🏼‍❤️‍👨🏾", - ["couple_with_heart_man_man_tone2_tone5"] = "👨🏼‍❤️‍👨🏿", - ["couple_with_heart_man_man_medium_light_skin_tone_dark_skin_tone"] = "👨🏼‍❤️‍👨🏿", - ["couple_with_heart_man_man_tone3_tone1"] = "👨🏽‍❤️‍👨🏻", - ["couple_with_heart_man_man_medium_skin_tone_light_skin_tone"] = "👨🏽‍❤️‍👨🏻", - ["couple_with_heart_man_man_tone3_tone2"] = "👨🏽‍❤️‍👨🏼", - ["couple_with_heart_man_man_medium_skin_tone_medium_light_skin_tone"] = "👨🏽‍❤️‍👨🏼", - ["couple_with_heart_man_man_tone3"] = "👨🏽‍❤️‍👨🏽", - ["couple_with_heart_man_man_medium_skin_tone"] = "👨🏽‍❤️‍👨🏽", - ["couple_with_heart_man_man_tone3_tone4"] = "👨🏽‍❤️‍👨🏾", - ["couple_with_heart_man_man_medium_skin_tone_medium_dark_skin_tone"] = "👨🏽‍❤️‍👨🏾", - ["couple_with_heart_man_man_tone3_tone5"] = "👨🏽‍❤️‍👨🏿", - ["couple_with_heart_man_man_medium_skin_tone_dark_skin_tone"] = "👨🏽‍❤️‍👨🏿", - ["couple_with_heart_man_man_tone4_tone1"] = "👨🏾‍❤️‍👨🏻", - ["couple_with_heart_man_man_medium_dark_skin_tone_light_skin_tone"] = "👨🏾‍❤️‍👨🏻", - ["couple_with_heart_man_man_tone4_tone2"] = "👨🏾‍❤️‍👨🏼", - ["couple_with_heart_man_man_medium_dark_skin_tone_medium_light_skin_tone"] = - "👨🏾‍❤️‍👨🏼", - ["couple_with_heart_man_man_tone4_tone3"] = "👨🏾‍❤️‍👨🏽", - ["couple_with_heart_man_man_medium_dark_skin_tone_medium_skin_tone"] = "👨🏾‍❤️‍👨🏽", - ["couple_with_heart_man_man_tone4"] = "👨🏾‍❤️‍👨🏾", - ["couple_with_heart_man_man_medium_dark_skin_tone"] = "👨🏾‍❤️‍👨🏾", - ["couple_with_heart_man_man_tone4_tone5"] = "👨🏾‍❤️‍👨🏿", - ["couple_with_heart_man_man_medium_dark_skin_tone_dark_skin_tone"] = "👨🏾‍❤️‍👨🏿", - ["couple_with_heart_man_man_tone5_tone1"] = "👨🏿‍❤️‍👨🏻", - ["couple_with_heart_man_man_dark_skin_tone_light_skin_tone"] = "👨🏿‍❤️‍👨🏻", - ["couple_with_heart_man_man_tone5_tone2"] = "👨🏿‍❤️‍👨🏼", - ["couple_with_heart_man_man_dark_skin_tone_medium_light_skin_tone"] = "👨🏿‍❤️‍👨🏼", - ["couple_with_heart_man_man_tone5_tone3"] = "👨🏿‍❤️‍👨🏽", - ["couple_with_heart_man_man_dark_skin_tone_medium_skin_tone"] = "👨🏿‍❤️‍👨🏽", - ["couple_with_heart_man_man_tone5_tone4"] = "👨🏿‍❤️‍👨🏾", - ["couple_with_heart_man_man_dark_skin_tone_medium_dark_skin_tone"] = "👨🏿‍❤️‍👨🏾", - ["couple_with_heart_man_man_tone5"] = "👨🏿‍❤️‍👨🏿", - ["couple_with_heart_man_man_dark_skin_tone"] = "👨🏿‍❤️‍👨🏿", - ["couplekiss"] = "💏", - ["kiss_person_person_tone5_tone4"] = "🧑🏿‍❤️‍💋‍🧑🏾", - ["kiss_person_person_dark_skin_tone_medium_dark_skin_tone"] = "🧑🏿‍❤️‍💋‍🧑🏾", - ["kiss_tone1"] = "💏🏻", - ["kiss_light_skin_tone"] = "💏🏻", - ["kiss_person_person_tone1_tone2"] = "🧑🏻‍❤️‍💋‍🧑🏼", - ["kiss_person_person_light_skin_tone_medium_light_skin_tone"] = "🧑🏻‍❤️‍💋‍🧑🏼", - ["kiss_person_person_tone1_tone3"] = "🧑🏻‍❤️‍💋‍🧑🏽", - ["kiss_person_person_light_skin_tone_medium_skin_tone"] = "🧑🏻‍❤️‍💋‍🧑🏽", - ["kiss_person_person_tone1_tone4"] = "🧑🏻‍❤️‍💋‍🧑🏾", - ["kiss_person_person_light_skin_tone_medium_dark_skin_tone"] = "🧑🏻‍❤️‍💋‍🧑🏾", - ["kiss_person_person_tone1_tone5"] = "🧑🏻‍❤️‍💋‍🧑🏿", - ["kiss_person_person_light_skin_tone_dark_skin_tone"] = "🧑🏻‍❤️‍💋‍🧑🏿", - ["kiss_person_person_tone2_tone1"] = "🧑🏼‍❤️‍💋‍🧑🏻", - ["kiss_person_person_medium_light_skin_tone_light_skin_tone"] = "🧑🏼‍❤️‍💋‍🧑🏻", - ["kiss_tone2"] = "💏🏼", - ["kiss_medium_light_skin_tone"] = "💏🏼", - ["kiss_person_person_tone2_tone3"] = "🧑🏼‍❤️‍💋‍🧑🏽", - ["kiss_person_person_medium_light_skin_tone_medium_skin_tone"] = "🧑🏼‍❤️‍💋‍🧑🏽", - ["kiss_person_person_tone2_tone4"] = "🧑🏼‍❤️‍💋‍🧑🏾", - ["kiss_person_person_medium_light_skin_tone_medium_dark_skin_tone"] = "🧑🏼‍❤️‍💋‍🧑🏾", - ["kiss_person_person_tone2_tone5"] = "🧑🏼‍❤️‍💋‍🧑🏿", - ["kiss_person_person_medium_light_skin_tone_dark_skin_tone"] = "🧑🏼‍❤️‍💋‍🧑🏿", - ["kiss_person_person_tone3_tone1"] = "🧑🏽‍❤️‍💋‍🧑🏻", - ["kiss_person_person_medium_skin_tone_light_skin_tone"] = "🧑🏽‍❤️‍💋‍🧑🏻", - ["kiss_person_person_tone3_tone2"] = "🧑🏽‍❤️‍💋‍🧑🏼", - ["kiss_person_person_medium_skin_tone_medium_light_skin_tone"] = "🧑🏽‍❤️‍💋‍🧑🏼", - ["kiss_tone3"] = "💏🏽", - ["kiss_medium_skin_tone"] = "💏🏽", - ["kiss_person_person_tone3_tone4"] = "🧑🏽‍❤️‍💋‍🧑🏾", - ["kiss_person_person_medium_skin_tone_medium_dark_skin_tone"] = "🧑🏽‍❤️‍💋‍🧑🏾", - ["kiss_person_person_tone3_tone5"] = "🧑🏽‍❤️‍💋‍🧑🏿", - ["kiss_person_person_medium_skin_tone_dark_skin_tone"] = "🧑🏽‍❤️‍💋‍🧑🏿", - ["kiss_person_person_tone4_tone1"] = "🧑🏾‍❤️‍💋‍🧑🏻", - ["kiss_person_person_medium_dark_skin_tone_light_skin_tone"] = "🧑🏾‍❤️‍💋‍🧑🏻", - ["kiss_person_person_tone4_tone2"] = "🧑🏾‍❤️‍💋‍🧑🏼", - ["kiss_person_person_medium_dark_skin_tone_medium_light_skin_tone"] = "🧑🏾‍❤️‍💋‍🧑🏼", - ["kiss_person_person_tone4_tone3"] = "🧑🏾‍❤️‍💋‍🧑🏽", - ["kiss_person_person_medium_dark_skin_tone_medium_skin_tone"] = "🧑🏾‍❤️‍💋‍🧑🏽", - ["kiss_tone4"] = "💏🏾", - ["kiss_medium_dark_skin_tone"] = "💏🏾", - ["kiss_person_person_tone4_tone5"] = "🧑🏾‍❤️‍💋‍🧑🏿", - ["kiss_person_person_medium_dark_skin_tone_dark_skin_tone"] = "🧑🏾‍❤️‍💋‍🧑🏿", - ["kiss_person_person_tone5_tone1"] = "🧑🏿‍❤️‍💋‍🧑🏻", - ["kiss_person_person_dark_skin_tone_light_skin_tone"] = "🧑🏿‍❤️‍💋‍🧑🏻", - ["kiss_person_person_tone5_tone2"] = "🧑🏿‍❤️‍💋‍🧑🏼", - ["kiss_person_person_dark_skin_tone_medium_light_skin_tone"] = "🧑🏿‍❤️‍💋‍🧑🏼", - ["kiss_person_person_tone5_tone3"] = "🧑🏿‍❤️‍💋‍🧑🏽", - ["kiss_person_person_dark_skin_tone_medium_skin_tone"] = "🧑🏿‍❤️‍💋‍🧑🏽", - ["kiss_tone5"] = "💏🏿", - ["kiss_dark_skin_tone"] = "💏🏿", - ["kiss_woman_man"] = "👩‍❤️‍💋‍👨", - ["kiss_woman_man_tone1"] = "👩🏻‍❤️‍💋‍👨🏻", - ["kiss_woman_man_light_skin_tone"] = "👩🏻‍❤️‍💋‍👨🏻", - ["kiss_woman_man_tone1_tone2"] = "👩🏻‍❤️‍💋‍👨🏼", - ["kiss_woman_man_light_skin_tone_medium_light_skin_tone"] = "👩🏻‍❤️‍💋‍👨🏼", - ["kiss_woman_man_tone1_tone3"] = "👩🏻‍❤️‍💋‍👨🏽", - ["kiss_woman_man_light_skin_tone_medium_skin_tone"] = "👩🏻‍❤️‍💋‍👨🏽", - ["kiss_woman_man_tone1_tone4"] = "👩🏻‍❤️‍💋‍👨🏾", - ["kiss_woman_man_light_skin_tone_medium_dark_skin_tone"] = "👩🏻‍❤️‍💋‍👨🏾", - ["kiss_woman_man_tone1_tone5"] = "👩🏻‍❤️‍💋‍👨🏿", - ["kiss_woman_man_light_skin_tone_dark_skin_tone"] = "👩🏻‍❤️‍💋‍👨🏿", - ["kiss_woman_man_tone2_tone1"] = "👩🏼‍❤️‍💋‍👨🏻", - ["kiss_woman_man_medium_light_skin_tone_light_skin_tone"] = "👩🏼‍❤️‍💋‍👨🏻", - ["kiss_woman_man_tone2"] = "👩🏼‍❤️‍💋‍👨🏼", - ["kiss_woman_man_medium_light_skin_tone"] = "👩🏼‍❤️‍💋‍👨🏼", - ["kiss_woman_man_tone2_tone3"] = "👩🏼‍❤️‍💋‍👨🏽", - ["kiss_woman_man_medium_light_skin_tone_medium_skin_tone"] = "👩🏼‍❤️‍💋‍👨🏽", - ["kiss_woman_man_tone2_tone4"] = "👩🏼‍❤️‍💋‍👨🏾", - ["kiss_woman_man_medium_light_skin_tone_medium_dark_skin_tone"] = "👩🏼‍❤️‍💋‍👨🏾", - ["kiss_woman_man_tone2_tone5"] = "👩🏼‍❤️‍💋‍👨🏿", - ["kiss_woman_man_medium_light_skin_tone_dark_skin_tone"] = "👩🏼‍❤️‍💋‍👨🏿", - ["kiss_woman_man_tone3_tone1"] = "👩🏽‍❤️‍💋‍👨🏻", - ["kiss_woman_man_medium_skin_tone_light_skin_tone"] = "👩🏽‍❤️‍💋‍👨🏻", - ["kiss_woman_man_tone3_tone2"] = "👩🏽‍❤️‍💋‍👨🏼", - ["kiss_woman_man_medium_skin_tone_medium_light_skin_tone"] = "👩🏽‍❤️‍💋‍👨🏼", - ["kiss_woman_man_tone3"] = "👩🏽‍❤️‍💋‍👨🏽", - ["kiss_woman_man_medium_skin_tone"] = "👩🏽‍❤️‍💋‍👨🏽", - ["kiss_woman_man_tone3_tone4"] = "👩🏽‍❤️‍💋‍👨🏾", - ["kiss_woman_man_medium_skin_tone_medium_dark_skin_tone"] = "👩🏽‍❤️‍💋‍👨🏾", - ["kiss_woman_man_tone3_tone5"] = "👩🏽‍❤️‍💋‍👨🏿", - ["kiss_woman_man_medium_skin_tone_dark_skin_tone"] = "👩🏽‍❤️‍💋‍👨🏿", - ["kiss_woman_man_tone4_tone1"] = "👩🏾‍❤️‍💋‍👨🏻", - ["kiss_woman_man_medium_dark_skin_tone_light_skin_tone"] = "👩🏾‍❤️‍💋‍👨🏻", - ["kiss_woman_man_tone4_tone2"] = "👩🏾‍❤️‍💋‍👨🏼", - ["kiss_woman_man_medium_dark_skin_tone_medium_light_skin_tone"] = "👩🏾‍❤️‍💋‍👨🏼", - ["kiss_woman_man_tone4_tone3"] = "👩🏾‍❤️‍💋‍👨🏽", - ["kiss_woman_man_medium_dark_skin_tone_medium_skin_tone"] = "👩🏾‍❤️‍💋‍👨🏽", - ["kiss_woman_man_tone4"] = "👩🏾‍❤️‍💋‍👨🏾", - ["kiss_woman_man_medium_dark_skin_tone"] = "👩🏾‍❤️‍💋‍👨🏾", - ["kiss_woman_man_tone4_tone5"] = "👩🏾‍❤️‍💋‍👨🏿", - ["kiss_woman_man_medium_dark_skin_tone_dark_skin_tone"] = "👩🏾‍❤️‍💋‍👨🏿", - ["kiss_woman_man_tone5_tone1"] = "👩🏿‍❤️‍💋‍👨🏻", - ["kiss_woman_man_dark_skin_tone_light_skin_tone"] = "👩🏿‍❤️‍💋‍👨🏻", - ["kiss_woman_man_tone5_tone2"] = "👩🏿‍❤️‍💋‍👨🏼", - ["kiss_woman_man_dark_skin_tone_medium_light_skin_tone"] = "👩🏿‍❤️‍💋‍👨🏼", - ["kiss_woman_man_tone5_tone3"] = "👩🏿‍❤️‍💋‍👨🏽", - ["kiss_woman_man_dark_skin_tone_medium_skin_tone"] = "👩🏿‍❤️‍💋‍👨🏽", - ["kiss_woman_man_tone5_tone4"] = "👩🏿‍❤️‍💋‍👨🏾", - ["kiss_woman_man_dark_skin_tone_medium_dark_skin_tone"] = "👩🏿‍❤️‍💋‍👨🏾", - ["kiss_woman_man_tone5"] = "👩🏿‍❤️‍💋‍👨🏿", - ["kiss_woman_man_dark_skin_tone"] = "👩🏿‍❤️‍💋‍👨🏿", - ["kiss_ww"] = "👩‍❤️‍💋‍👩", - ["couplekiss_ww"] = "👩‍❤️‍💋‍👩", - ["kiss_woman_woman_tone1"] = "👩🏻‍❤️‍💋‍👩🏻", - ["kiss_woman_woman_light_skin_tone"] = "👩🏻‍❤️‍💋‍👩🏻", - ["kiss_woman_woman_tone1_tone2"] = "👩🏻‍❤️‍💋‍👩🏼", - ["kiss_woman_woman_light_skin_tone_medium_light_skin_tone"] = "👩🏻‍❤️‍💋‍👩🏼", - ["kiss_woman_woman_tone1_tone3"] = "👩🏻‍❤️‍💋‍👩🏽", - ["kiss_woman_woman_light_skin_tone_medium_skin_tone"] = "👩🏻‍❤️‍💋‍👩🏽", - ["kiss_woman_woman_tone1_tone4"] = "👩🏻‍❤️‍💋‍👩🏾", - ["kiss_woman_woman_light_skin_tone_medium_dark_skin_tone"] = "👩🏻‍❤️‍💋‍👩🏾", - ["kiss_woman_woman_tone1_tone5"] = "👩🏻‍❤️‍💋‍👩🏿", - ["kiss_woman_woman_light_skin_tone_dark_skin_tone"] = "👩🏻‍❤️‍💋‍👩🏿", - ["kiss_woman_woman_tone2_tone1"] = "👩🏼‍❤️‍💋‍👩🏻", - ["kiss_woman_woman_medium_light_skin_tone_light_skin_tone"] = "👩🏼‍❤️‍💋‍👩🏻", - ["kiss_woman_woman_tone2"] = "👩🏼‍❤️‍💋‍👩🏼", - ["kiss_woman_woman_medium_light_skin_tone"] = "👩🏼‍❤️‍💋‍👩🏼", - ["kiss_woman_woman_tone2_tone3"] = "👩🏼‍❤️‍💋‍👩🏽", - ["kiss_woman_woman_medium_light_skin_tone_medium_skin_tone"] = "👩🏼‍❤️‍💋‍👩🏽", - ["kiss_woman_woman_tone2_tone4"] = "👩🏼‍❤️‍💋‍👩🏾", - ["kiss_woman_woman_medium_light_skin_tone_medium_dark_skin_tone"] = "👩🏼‍❤️‍💋‍👩🏾", - ["kiss_woman_woman_tone2_tone5"] = "👩🏼‍❤️‍💋‍👩🏿", - ["kiss_woman_woman_medium_light_skin_tone_dark_skin_tone"] = "👩🏼‍❤️‍💋‍👩🏿", - ["kiss_woman_woman_tone3_tone1"] = "👩🏽‍❤️‍💋‍👩🏻", - ["kiss_woman_woman_medium_skin_tone_light_skin_tone"] = "👩🏽‍❤️‍💋‍👩🏻", - ["kiss_woman_woman_tone3_tone2"] = "👩🏽‍❤️‍💋‍👩🏼", - ["kiss_woman_woman_medium_skin_tone_medium_light_skin_tone"] = "👩🏽‍❤️‍💋‍👩🏼", - ["kiss_woman_woman_tone3"] = "👩🏽‍❤️‍💋‍👩🏽", - ["kiss_woman_woman_medium_skin_tone"] = "👩🏽‍❤️‍💋‍👩🏽", - ["kiss_woman_woman_tone3_tone4"] = "👩🏽‍❤️‍💋‍👩🏾", - ["kiss_woman_woman_medium_skin_tone_medium_dark_skin_tone"] = "👩🏽‍❤️‍💋‍👩🏾", - ["kiss_woman_woman_tone3_tone5"] = "👩🏽‍❤️‍💋‍👩🏿", - ["kiss_woman_woman_medium_skin_tone_dark_skin_tone"] = "👩🏽‍❤️‍💋‍👩🏿", - ["kiss_woman_woman_tone4_tone1"] = "👩🏾‍❤️‍💋‍👩🏻", - ["kiss_woman_woman_medium_dark_skin_tone_light_skin_tone"] = "👩🏾‍❤️‍💋‍👩🏻", - ["kiss_woman_woman_tone4_tone2"] = "👩🏾‍❤️‍💋‍👩🏼", - ["kiss_woman_woman_medium_dark_skin_tone_medium_light_skin_tone"] = "👩🏾‍❤️‍💋‍👩🏼", - ["kiss_woman_woman_tone4_tone3"] = "👩🏾‍❤️‍💋‍👩🏽", - ["kiss_woman_woman_medium_dark_skin_tone_medium_skin_tone"] = "👩🏾‍❤️‍💋‍👩🏽", - ["kiss_woman_woman_tone4"] = "👩🏾‍❤️‍💋‍👩🏾", - ["kiss_woman_woman_medium_dark_skin_tone"] = "👩🏾‍❤️‍💋‍👩🏾", - ["kiss_woman_woman_tone4_tone5"] = "👩🏾‍❤️‍💋‍👩🏿", - ["kiss_woman_woman_medium_dark_skin_tone_dark_skin_tone"] = "👩🏾‍❤️‍💋‍👩🏿", - ["kiss_woman_woman_tone5_tone1"] = "👩🏿‍❤️‍💋‍👩🏻", - ["kiss_woman_woman_dark_skin_tone_light_skin_tone"] = "👩🏿‍❤️‍💋‍👩🏻", - ["kiss_woman_woman_tone5_tone2"] = "👩🏿‍❤️‍💋‍👩🏼", - ["kiss_woman_woman_dark_skin_tone_medium_light_skin_tone"] = "👩🏿‍❤️‍💋‍👩🏼", - ["kiss_woman_woman_tone5_tone3"] = "👩🏿‍❤️‍💋‍👩🏽", - ["kiss_woman_woman_dark_skin_tone_medium_skin_tone"] = "👩🏿‍❤️‍💋‍👩🏽", - ["kiss_woman_woman_tone5_tone4"] = "👩🏿‍❤️‍💋‍👩🏾", - ["kiss_woman_woman_dark_skin_tone_medium_dark_skin_tone"] = "👩🏿‍❤️‍💋‍👩🏾", - ["kiss_woman_woman_tone5"] = "👩🏿‍❤️‍💋‍👩🏿", - ["kiss_woman_woman_dark_skin_tone"] = "👩🏿‍❤️‍💋‍👩🏿", - ["kiss_mm"] = "👨‍❤️‍💋‍👨", - ["couplekiss_mm"] = "👨‍❤️‍💋‍👨", - ["kiss_man_man_tone1"] = "👨🏻‍❤️‍💋‍👨🏻", - ["kiss_man_man_light_skin_tone"] = "👨🏻‍❤️‍💋‍👨🏻", - ["kiss_man_man_tone1_tone2"] = "👨🏻‍❤️‍💋‍👨🏼", - ["kiss_man_man_light_skin_tone_medium_light_skin_tone"] = "👨🏻‍❤️‍💋‍👨🏼", - ["kiss_man_man_tone1_tone3"] = "👨🏻‍❤️‍💋‍👨🏽", - ["kiss_man_man_light_skin_tone_medium_skin_tone"] = "👨🏻‍❤️‍💋‍👨🏽", - ["kiss_man_man_tone1_tone4"] = "👨🏻‍❤️‍💋‍👨🏾", - ["kiss_man_man_light_skin_tone_medium_dark_skin_tone"] = "👨🏻‍❤️‍💋‍👨🏾", - ["kiss_man_man_tone1_tone5"] = "👨🏻‍❤️‍💋‍👨🏿", - ["kiss_man_man_light_skin_tone_dark_skin_tone"] = "👨🏻‍❤️‍💋‍👨🏿", - ["kiss_man_man_tone2_tone1"] = "👨🏼‍❤️‍💋‍👨🏻", - ["kiss_man_man_medium_light_skin_tone_light_skin_tone"] = "👨🏼‍❤️‍💋‍👨🏻", - ["kiss_man_man_tone2"] = "👨🏼‍❤️‍💋‍👨🏼", - ["kiss_man_man_medium_light_skin_tone"] = "👨🏼‍❤️‍💋‍👨🏼", - ["kiss_man_man_tone2_tone3"] = "👨🏼‍❤️‍💋‍👨🏽", - ["kiss_man_man_medium_light_skin_tone_medium_skin_tone"] = "👨🏼‍❤️‍💋‍👨🏽", - ["kiss_man_man_tone2_tone4"] = "👨🏼‍❤️‍💋‍👨🏾", - ["kiss_man_man_medium_light_skin_tone_medium_dark_skin_tone"] = "👨🏼‍❤️‍💋‍👨🏾", - ["kiss_man_man_tone2_tone5"] = "👨🏼‍❤️‍💋‍👨🏿", - ["kiss_man_man_medium_light_skin_tone_dark_skin_tone"] = "👨🏼‍❤️‍💋‍👨🏿", - ["kiss_man_man_tone3_tone1"] = "👨🏽‍❤️‍💋‍👨🏻", - ["kiss_man_man_medium_skin_tone_light_skin_tone"] = "👨🏽‍❤️‍💋‍👨🏻", - ["kiss_man_man_tone3_tone2"] = "👨🏽‍❤️‍💋‍👨🏼", - ["kiss_man_man_medium_skin_tone_medium_light_skin_tone"] = "👨🏽‍❤️‍💋‍👨🏼", - ["kiss_man_man_tone3"] = "👨🏽‍❤️‍💋‍👨🏽", - ["kiss_man_man_medium_skin_tone"] = "👨🏽‍❤️‍💋‍👨🏽", - ["kiss_man_man_tone3_tone4"] = "👨🏽‍❤️‍💋‍👨🏾", - ["kiss_man_man_medium_skin_tone_medium_dark_skin_tone"] = "👨🏽‍❤️‍💋‍👨🏾", - ["kiss_man_man_tone3_tone5"] = "👨🏽‍❤️‍💋‍👨🏿", - ["kiss_man_man_medium_skin_tone_dark_skin_tone"] = "👨🏽‍❤️‍💋‍👨🏿", - ["kiss_man_man_tone4_tone1"] = "👨🏾‍❤️‍💋‍👨🏻", - ["kiss_man_man_medium_dark_skin_tone_light_skin_tone"] = "👨🏾‍❤️‍💋‍👨🏻", - ["kiss_man_man_tone4_tone2"] = "👨🏾‍❤️‍💋‍👨🏼", - ["kiss_man_man_medium_dark_skin_tone_medium_light_skin_tone"] = "👨🏾‍❤️‍💋‍👨🏼", - ["kiss_man_man_tone4_tone3"] = "👨🏾‍❤️‍💋‍👨🏽", - ["kiss_man_man_medium_dark_skin_tone_medium_skin_tone"] = "👨🏾‍❤️‍💋‍👨🏽", - ["kiss_man_man_tone4"] = "👨🏾‍❤️‍💋‍👨🏾", - ["kiss_man_man_medium_dark_skin_tone"] = "👨🏾‍❤️‍💋‍👨🏾", - ["kiss_man_man_tone4_tone5"] = "👨🏾‍❤️‍💋‍👨🏿", - ["kiss_man_man_medium_dark_skin_tone_dark_skin_tone"] = "👨🏾‍❤️‍💋‍👨🏿", - ["kiss_man_man_tone5_tone1"] = "👨🏿‍❤️‍💋‍👨🏻", - ["kiss_man_man_dark_skin_tone_light_skin_tone"] = "👨🏿‍❤️‍💋‍👨🏻", - ["kiss_man_man_tone5_tone2"] = "👨🏿‍❤️‍💋‍👨🏼", - ["kiss_man_man_dark_skin_tone_medium_light_skin_tone"] = "👨🏿‍❤️‍💋‍👨🏼", - ["kiss_man_man_tone5_tone3"] = "👨🏿‍❤️‍💋‍👨🏽", - ["kiss_man_man_dark_skin_tone_medium_skin_tone"] = "👨🏿‍❤️‍💋‍👨🏽", - ["kiss_man_man_tone5_tone4"] = "👨🏿‍❤️‍💋‍👨🏾", - ["kiss_man_man_dark_skin_tone_medium_dark_skin_tone"] = "👨🏿‍❤️‍💋‍👨🏾", - ["kiss_man_man_tone5"] = "👨🏿‍❤️‍💋‍👨🏿", - ["kiss_man_man_dark_skin_tone"] = "👨🏿‍❤️‍💋‍👨🏿", - ["family"] = "👪", - ["family_man_woman_boy"] = "👨‍👩‍👦", - ["family_mwg"] = "👨‍👩‍👧", - ["family_mwgb"] = "👨‍👩‍👧‍👦", - ["family_mwbb"] = "👨‍👩‍👦‍👦", - ["family_mwgg"] = "👨‍👩‍👧‍👧", - ["family_wwb"] = "👩‍👩‍👦", - ["family_wwg"] = "👩‍👩‍👧", - ["family_wwgb"] = "👩‍👩‍👧‍👦", - ["family_wwbb"] = "👩‍👩‍👦‍👦", - ["family_wwgg"] = "👩‍👩‍👧‍👧", - ["family_mmb"] = "👨‍👨‍👦", - ["family_mmg"] = "👨‍👨‍👧", - ["family_mmgb"] = "👨‍👨‍👧‍👦", - ["family_mmbb"] = "👨‍👨‍👦‍👦", - ["family_mmgg"] = "👨‍👨‍👧‍👧", - ["family_woman_boy"] = "👩‍👦", - ["family_woman_girl"] = "👩‍👧", - ["family_woman_girl_boy"] = "👩‍👧‍👦", - ["family_woman_boy_boy"] = "👩‍👦‍👦", - ["family_woman_girl_girl"] = "👩‍👧‍👧", - ["family_man_boy"] = "👨‍👦", - ["family_man_girl"] = "👨‍👧", - ["family_man_girl_boy"] = "👨‍👧‍👦", - ["family_man_boy_boy"] = "👨‍👦‍👦", - ["family_man_girl_girl"] = "👨‍👧‍👧", - ["yarn"] = "🧶", - ["thread"] = "🧵", - ["coat"] = "🧥", - ["lab_coat"] = "🥼", - ["safety_vest"] = "🦺", - ["womans_clothes"] = "👚", - ["shirt"] = "👕", - ["jeans"] = "👖", - ["briefs"] = "🩲", - ["shorts"] = "🩳", - ["necktie"] = "👔", - ["dress"] = "👗", - ["bikini"] = "👙", - ["one_piece_swimsuit"] = "🩱", - ["kimono"] = "👘", - ["sari"] = "🥻", - ["womans_flat_shoe"] = "🥿", - ["high_heel"] = "👠", - ["sandal"] = "👡", - ["boot"] = "👢", - ["mans_shoe"] = "👞", - ["athletic_shoe"] = "👟", - ["hiking_boot"] = "🥾", - ["thong_sandal"] = "🩴", - ["socks"] = "🧦", - ["gloves"] = "🧤", - ["scarf"] = "🧣", - ["tophat"] = "🎩", - ["billed_cap"] = "🧢", - ["womans_hat"] = "👒", - ["mortar_board"] = "🎓", - ["helmet_with_cross"] = "⛑️", - ["helmet_with_white_cross"] = "⛑️", - ["military_helmet"] = "🪖", - ["crown"] = "👑", - ["ring"] = "💍", - ["pouch"] = "👝", - ["purse"] = "👛", - ["handbag"] = "👜", - ["briefcase"] = "💼", - ["school_satchel"] = "🎒", - ["luggage"] = "🧳", - ["eyeglasses"] = "👓", - ["dark_sunglasses"] = "🕶️", - ["goggles"] = "🥽", - ["closed_umbrella"] = "🌂", - ["dog"] = "🐶", - ["cat"] = "🐱", - ["mouse"] = "🐭", - ["hamster"] = "🐹", - ["rabbit"] = "🐰", - ["fox"] = "🦊", - ["fox_face"] = "🦊", - ["bear"] = "🐻", - ["panda_face"] = "🐼", - ["polar_bear"] = "🐻‍❄️", - ["koala"] = "🐨", - ["tiger"] = "🐯", - ["lion_face"] = "🦁", - ["lion"] = "🦁", - ["cow"] = "🐮", - ["pig"] = "🐷", - ["pig_nose"] = "🐽", - ["frog"] = "🐸", - ["monkey_face"] = "🐵", - ["see_no_evil"] = "🙈", - ["hear_no_evil"] = "🙉", - ["speak_no_evil"] = "🙊", - ["monkey"] = "🐒", - ["chicken"] = "🐔", - ["penguin"] = "🐧", - ["bird"] = "🐦", - ["baby_chick"] = "🐤", - ["hatching_chick"] = "🐣", - ["hatched_chick"] = "🐥", - ["duck"] = "🦆", - ["dodo"] = "🦤", - ["eagle"] = "🦅", - ["owl"] = "🦉", - ["bat"] = "🦇", - ["wolf"] = "🐺", - ["boar"] = "🐗", - ["horse"] = "🐴", - ["unicorn"] = "🦄", - ["unicorn_face"] = "🦄", - ["bee"] = "🐝", - ["bug"] = "🐛", - ["butterfly"] = "🦋", - ["snail"] = "🐌", - ["worm"] = "🪱", - ["lady_beetle"] = "🐞", - ["ant"] = "🐜", - ["fly"] = "🪰", - ["mosquito"] = "🦟", - ["cockroach"] = "🪳", - ["beetle"] = "🪲", - ["cricket"] = "🦗", - ["spider"] = "🕷️", - ["spider_web"] = "🕸️", - ["scorpion"] = "🦂", - ["turtle"] = "🐢", - ["snake"] = "🐍", - ["lizard"] = "🦎", - ["t_rex"] = "🦖", - ["sauropod"] = "🦕", - ["octopus"] = "🐙", - ["squid"] = "🦑", - ["shrimp"] = "🦐", - ["lobster"] = "🦞", - ["crab"] = "🦀", - ["blowfish"] = "🐡", - ["tropical_fish"] = "🐠", - ["fish"] = "🐟", - ["seal"] = "🦭", - ["dolphin"] = "🐬", - ["whale"] = "🐳", - ["whale2"] = "🐋", - ["shark"] = "🦈", - ["crocodile"] = "🐊", - ["tiger2"] = "🐅", - ["leopard"] = "🐆", - ["zebra"] = "🦓", - ["gorilla"] = "🦍", - ["orangutan"] = "🦧", - ["elephant"] = "🐘", - ["mammoth"] = "🦣", - ["bison"] = "🦬", - ["hippopotamus"] = "🦛", - ["rhino"] = "🦏", - ["rhinoceros"] = "🦏", - ["dromedary_camel"] = "🐪", - ["camel"] = "🐫", - ["giraffe"] = "🦒", - ["kangaroo"] = "🦘", - ["water_buffalo"] = "🐃", - ["ox"] = "🐂", - ["cow2"] = "🐄", - ["racehorse"] = "🐎", - ["pig2"] = "🐖", - ["ram"] = "🐏", - ["sheep"] = "🐑", - ["llama"] = "🦙", - ["goat"] = "🐐", - ["deer"] = "🦌", - ["dog2"] = "🐕", - ["poodle"] = "🐩", - ["guide_dog"] = "🦮", - ["service_dog"] = "🐕‍🦺", - ["cat2"] = "🐈", - ["black_cat"] = "🐈‍⬛", - ["rooster"] = "🐓", - ["turkey"] = "🦃", - ["peacock"] = "🦚", - ["parrot"] = "🦜", - ["swan"] = "🦢", - ["flamingo"] = "🦩", - ["dove"] = "🕊️", - ["dove_of_peace"] = "🕊️", - ["rabbit2"] = "🐇", - ["raccoon"] = "🦝", - ["skunk"] = "🦨", - ["badger"] = "🦡", - ["beaver"] = "🦫", - ["otter"] = "🦦", - ["sloth"] = "🦥", - ["mouse2"] = "🐁", - ["rat"] = "🐀", - ["chipmunk"] = "🐿️", - ["hedgehog"] = "🦔", - ["feet"] = "🐾", - ["paw_prints"] = "🐾", - ["dragon"] = "🐉", - ["dragon_face"] = "🐲", - ["cactus"] = "🌵", - ["christmas_tree"] = "🎄", - ["evergreen_tree"] = "🌲", - ["deciduous_tree"] = "🌳", - ["palm_tree"] = "🌴", - ["seedling"] = "🌱", - ["herb"] = "🌿", - ["shamrock"] = "☘️", - ["four_leaf_clover"] = "🍀", - ["bamboo"] = "🎍", - ["tanabata_tree"] = "🎋", - ["leaves"] = "🍃", - ["fallen_leaf"] = "🍂", - ["maple_leaf"] = "🍁", - ["feather"] = "🪶", - ["mushroom"] = "🍄", - ["shell"] = "🐚", - ["rock"] = "🪨", - ["wood"] = "🪵", - ["ear_of_rice"] = "🌾", - ["potted_plant"] = "🪴", - ["bouquet"] = "💐", - ["tulip"] = "🌷", - ["rose"] = "🌹", - ["wilted_rose"] = "🥀", - ["wilted_flower"] = "🥀", - ["hibiscus"] = "🌺", - ["cherry_blossom"] = "🌸", - ["blossom"] = "🌼", - ["sunflower"] = "🌻", - ["sun_with_face"] = "🌞", - ["full_moon_with_face"] = "🌝", - ["first_quarter_moon_with_face"] = "🌛", - ["last_quarter_moon_with_face"] = "🌜", - ["new_moon_with_face"] = "🌚", - ["full_moon"] = "🌕", - ["waning_gibbous_moon"] = "🌖", - ["last_quarter_moon"] = "🌗", - ["waning_crescent_moon"] = "🌘", - ["new_moon"] = "🌑", - ["waxing_crescent_moon"] = "🌒", - ["first_quarter_moon"] = "🌓", - ["waxing_gibbous_moon"] = "🌔", - ["crescent_moon"] = "🌙", - ["earth_americas"] = "🌎", - ["earth_africa"] = "🌍", - ["earth_asia"] = "🌏", - ["ringed_planet"] = "🪐", - ["dizzy"] = "💫", - ["star"] = "⭐", - ["star2"] = "🌟", - ["sparkles"] = "✨", - ["zap"] = "⚡", - ["comet"] = "☄️", - ["boom"] = "💥", - ["fire"] = "🔥", - ["flame"] = "🔥", - ["cloud_tornado"] = "🌪️", - ["cloud_with_tornado"] = "🌪️", - ["rainbow"] = "🌈", - ["sunny"] = "☀️", - ["white_sun_small_cloud"] = "🌤️", - ["white_sun_with_small_cloud"] = "🌤️", - ["partly_sunny"] = "⛅", - ["white_sun_cloud"] = "🌥️", - ["white_sun_behind_cloud"] = "🌥️", - ["cloud"] = "☁️", - ["white_sun_rain_cloud"] = "🌦️", - ["white_sun_behind_cloud_with_rain"] = "🌦️", - ["cloud_rain"] = "🌧️", - ["cloud_with_rain"] = "🌧️", - ["thunder_cloud_rain"] = "⛈️", - ["thunder_cloud_and_rain"] = "⛈️", - ["cloud_lightning"] = "🌩️", - ["cloud_with_lightning"] = "🌩️", - ["cloud_snow"] = "🌨️", - ["cloud_with_snow"] = "🌨️", - ["snowflake"] = "❄️", - ["snowman2"] = "☃️", - ["snowman"] = "⛄", - ["wind_blowing_face"] = "🌬️", - ["dash"] = "💨", - ["droplet"] = "💧", - ["sweat_drops"] = "💦", - ["umbrella"] = "☔", - ["umbrella2"] = "☂️", - ["ocean"] = "🌊", - ["fog"] = "🌫️", - ["green_apple"] = "🍏", - ["apple"] = "🍎", - ["pear"] = "🍐", - ["tangerine"] = "🍊", - ["lemon"] = "🍋", - ["banana"] = "🍌", - ["watermelon"] = "🍉", - ["grapes"] = "🍇", - ["blueberries"] = "🫐", - ["strawberry"] = "🍓", - ["melon"] = "🍈", - ["cherries"] = "🍒", - ["peach"] = "🍑", - ["mango"] = "🥭", - ["pineapple"] = "🍍", - ["coconut"] = "🥥", - ["kiwi"] = "🥝", - ["kiwifruit"] = "🥝", - ["tomato"] = "🍅", - ["eggplant"] = "🍆", - ["avocado"] = "🥑", - ["olive"] = "🫒", - ["broccoli"] = "🥦", - ["leafy_green"] = "🥬", - ["bell_pepper"] = "🫑", - ["cucumber"] = "🥒", - ["hot_pepper"] = "🌶️", - ["corn"] = "🌽", - ["carrot"] = "🥕", - ["garlic"] = "🧄", - ["onion"] = "🧅", - ["potato"] = "🥔", - ["sweet_potato"] = "🍠", - ["croissant"] = "🥐", - ["bagel"] = "🥯", - ["bread"] = "🍞", - ["french_bread"] = "🥖", - ["baguette_bread"] = "🥖", - ["flatbread"] = "🫓", - ["pretzel"] = "🥨", - ["cheese"] = "🧀", - ["cheese_wedge"] = "🧀", - ["egg"] = "🥚", - ["cooking"] = "🍳", - ["butter"] = "🧈", - ["pancakes"] = "🥞", - ["waffle"] = "🧇", - ["bacon"] = "🥓", - ["cut_of_meat"] = "🥩", - ["poultry_leg"] = "🍗", - ["meat_on_bone"] = "🍖", - ["hotdog"] = "🌭", - ["hot_dog"] = "🌭", - ["hamburger"] = "🍔", - ["fries"] = "🍟", - ["pizza"] = "🍕", - ["sandwich"] = "🥪", - ["stuffed_flatbread"] = "🥙", - ["stuffed_pita"] = "🥙", - ["falafel"] = "🧆", - ["taco"] = "🌮", - ["burrito"] = "🌯", - ["tamale"] = "🫔", - ["salad"] = "🥗", - ["green_salad"] = "🥗", - ["shallow_pan_of_food"] = "🥘", - ["paella"] = "🥘", - ["fondue"] = "🫕", - ["canned_food"] = "🥫", - ["spaghetti"] = "🍝", - ["ramen"] = "🍜", - ["stew"] = "🍲", - ["curry"] = "🍛", - ["sushi"] = "🍣", - ["bento"] = "🍱", - ["dumpling"] = "🥟", - ["oyster"] = "🦪", - ["fried_shrimp"] = "🍤", - ["rice_ball"] = "🍙", - ["rice"] = "🍚", - ["rice_cracker"] = "🍘", - ["fish_cake"] = "🍥", - ["fortune_cookie"] = "🥠", - ["moon_cake"] = "🥮", - ["oden"] = "🍢", - ["dango"] = "🍡", - ["shaved_ice"] = "🍧", - ["ice_cream"] = "🍨", - ["icecream"] = "🍦", - ["pie"] = "🥧", - ["cupcake"] = "🧁", - ["cake"] = "🍰", - ["birthday"] = "🎂", - ["custard"] = "🍮", - ["pudding"] = "🍮", - ["flan"] = "🍮", - ["lollipop"] = "🍭", - ["candy"] = "🍬", - ["chocolate_bar"] = "🍫", - ["popcorn"] = "🍿", - ["doughnut"] = "🍩", - ["cookie"] = "🍪", - ["chestnut"] = "🌰", - ["peanuts"] = "🥜", - ["shelled_peanut"] = "🥜", - ["honey_pot"] = "🍯", - ["milk"] = "🥛", - ["glass_of_milk"] = "🥛", - ["baby_bottle"] = "🍼", - ["coffee"] = "☕", - ["tea"] = "🍵", - ["teapot"] = "🫖", - ["mate"] = "🧉", - ["bubble_tea"] = "🧋", - ["beverage_box"] = "🧃", - ["cup_with_straw"] = "🥤", - ["sake"] = "🍶", - ["beer"] = "🍺", - ["beers"] = "🍻", - ["champagne_glass"] = "🥂", - ["clinking_glass"] = "🥂", - ["wine_glass"] = "🍷", - ["tumbler_glass"] = "🥃", - ["whisky"] = "🥃", - ["cocktail"] = "🍸", - ["tropical_drink"] = "🍹", - ["champagne"] = "🍾", - ["bottle_with_popping_cork"] = "🍾", - ["ice_cube"] = "🧊", - ["spoon"] = "🥄", - ["fork_and_knife"] = "🍴", - ["fork_knife_plate"] = "🍽️", - ["fork_and_knife_with_plate"] = "🍽️", - ["bowl_with_spoon"] = "🥣", - ["takeout_box"] = "🥡", - ["chopsticks"] = "🥢", - ["salt"] = "🧂", - ["soccer"] = "⚽", - ["basketball"] = "🏀", - ["football"] = "🏈", - ["baseball"] = "⚾", - ["softball"] = "🥎", - ["tennis"] = "🎾", - ["volleyball"] = "🏐", - ["rugby_football"] = "🏉", - ["flying_disc"] = "🥏", - ["boomerang"] = "🪃", - ["8ball"] = "🎱", - ["yo_yo"] = "🪀", - ["ping_pong"] = "🏓", - ["table_tennis"] = "🏓", - ["badminton"] = "🏸", - ["hockey"] = "🏒", - ["field_hockey"] = "🏑", - ["lacrosse"] = "🥍", - ["cricket_game"] = "🏏", - ["cricket_bat_ball"] = "🏏", - ["goal"] = "🥅", - ["goal_net"] = "🥅", - ["golf"] = "⛳", - ["kite"] = "🪁", - ["bow_and_arrow"] = "🏹", - ["archery"] = "🏹", - ["fishing_pole_and_fish"] = "🎣", - ["diving_mask"] = "🤿", - ["boxing_glove"] = "🥊", - ["boxing_gloves"] = "🥊", - ["martial_arts_uniform"] = "🥋", - ["karate_uniform"] = "🥋", - ["running_shirt_with_sash"] = "🎽", - ["skateboard"] = "🛹", - ["roller_skate"] = "🛼", - ["sled"] = "🛷", - ["ice_skate"] = "⛸️", - ["curling_stone"] = "🥌", - ["ski"] = "🎿", - ["skier"] = "⛷️", - ["snowboarder"] = "🏂", - ["snowboarder_tone1"] = "🏂🏻", - ["snowboarder_light_skin_tone"] = "🏂🏻", - ["snowboarder_tone2"] = "🏂🏼", - ["snowboarder_medium_light_skin_tone"] = "🏂🏼", - ["snowboarder_tone3"] = "🏂🏽", - ["snowboarder_medium_skin_tone"] = "🏂🏽", - ["snowboarder_tone4"] = "🏂🏾", - ["snowboarder_medium_dark_skin_tone"] = "🏂🏾", - ["snowboarder_tone5"] = "🏂🏿", - ["snowboarder_dark_skin_tone"] = "🏂🏿", - ["parachute"] = "🪂", - ["person_lifting_weights"] = "🏋️", - ["lifter"] = "🏋️", - ["weight_lifter"] = "🏋️", - ["person_lifting_weights_tone1"] = "🏋🏻", - ["lifter_tone1"] = "🏋🏻", - ["weight_lifter_tone1"] = "🏋🏻", - ["person_lifting_weights_tone2"] = "🏋🏼", - ["lifter_tone2"] = "🏋🏼", - ["weight_lifter_tone2"] = "🏋🏼", - ["person_lifting_weights_tone3"] = "🏋🏽", - ["lifter_tone3"] = "🏋🏽", - ["weight_lifter_tone3"] = "🏋🏽", - ["person_lifting_weights_tone4"] = "🏋🏾", - ["lifter_tone4"] = "🏋🏾", - ["weight_lifter_tone4"] = "🏋🏾", - ["person_lifting_weights_tone5"] = "🏋🏿", - ["lifter_tone5"] = "🏋🏿", - ["weight_lifter_tone5"] = "🏋🏿", - ["woman_lifting_weights"] = "🏋️‍♀️", - ["woman_lifting_weights_tone1"] = "🏋🏻‍♀️", - ["woman_lifting_weights_light_skin_tone"] = "🏋🏻‍♀️", - ["woman_lifting_weights_tone2"] = "🏋🏼‍♀️", - ["woman_lifting_weights_medium_light_skin_tone"] = "🏋🏼‍♀️", - ["woman_lifting_weights_tone3"] = "🏋🏽‍♀️", - ["woman_lifting_weights_medium_skin_tone"] = "🏋🏽‍♀️", - ["woman_lifting_weights_tone4"] = "🏋🏾‍♀️", - ["woman_lifting_weights_medium_dark_skin_tone"] = "🏋🏾‍♀️", - ["woman_lifting_weights_tone5"] = "🏋🏿‍♀️", - ["woman_lifting_weights_dark_skin_tone"] = "🏋🏿‍♀️", - ["man_lifting_weights"] = "🏋️‍♂️", - ["man_lifting_weights_tone1"] = "🏋🏻‍♂️", - ["man_lifting_weights_light_skin_tone"] = "🏋🏻‍♂️", - ["man_lifting_weights_tone2"] = "🏋🏼‍♂️", - ["man_lifting_weights_medium_light_skin_tone"] = "🏋🏼‍♂️", - ["man_lifting_weights_tone3"] = "🏋🏽‍♂️", - ["man_lifting_weights_medium_skin_tone"] = "🏋🏽‍♂️", - ["man_lifting_weights_tone4"] = "🏋🏾‍♂️", - ["man_lifting_weights_medium_dark_skin_tone"] = "🏋🏾‍♂️", - ["man_lifting_weights_tone5"] = "🏋🏿‍♂️", - ["man_lifting_weights_dark_skin_tone"] = "🏋🏿‍♂️", - ["people_wrestling"] = "🤼", - ["wrestlers"] = "🤼", - ["wrestling"] = "🤼", - ["women_wrestling"] = "🤼‍♀️", - ["men_wrestling"] = "🤼‍♂️", - ["person_doing_cartwheel"] = "🤸", - ["cartwheel"] = "🤸", - ["person_doing_cartwheel_tone1"] = "🤸🏻", - ["cartwheel_tone1"] = "🤸🏻", - ["person_doing_cartwheel_tone2"] = "🤸🏼", - ["cartwheel_tone2"] = "🤸🏼", - ["person_doing_cartwheel_tone3"] = "🤸🏽", - ["cartwheel_tone3"] = "🤸🏽", - ["person_doing_cartwheel_tone4"] = "🤸🏾", - ["cartwheel_tone4"] = "🤸🏾", - ["person_doing_cartwheel_tone5"] = "🤸🏿", - ["cartwheel_tone5"] = "🤸🏿", - ["woman_cartwheeling"] = "🤸‍♀️", - ["woman_cartwheeling_tone1"] = "🤸🏻‍♀️", - ["woman_cartwheeling_light_skin_tone"] = "🤸🏻‍♀️", - ["woman_cartwheeling_tone2"] = "🤸🏼‍♀️", - ["woman_cartwheeling_medium_light_skin_tone"] = "🤸🏼‍♀️", - ["woman_cartwheeling_tone3"] = "🤸🏽‍♀️", - ["woman_cartwheeling_medium_skin_tone"] = "🤸🏽‍♀️", - ["woman_cartwheeling_tone4"] = "🤸🏾‍♀️", - ["woman_cartwheeling_medium_dark_skin_tone"] = "🤸🏾‍♀️", - ["woman_cartwheeling_tone5"] = "🤸🏿‍♀️", - ["woman_cartwheeling_dark_skin_tone"] = "🤸🏿‍♀️", - ["man_cartwheeling"] = "🤸‍♂️", - ["man_cartwheeling_tone1"] = "🤸🏻‍♂️", - ["man_cartwheeling_light_skin_tone"] = "🤸🏻‍♂️", - ["man_cartwheeling_tone2"] = "🤸🏼‍♂️", - ["man_cartwheeling_medium_light_skin_tone"] = "🤸🏼‍♂️", - ["man_cartwheeling_tone3"] = "🤸🏽‍♂️", - ["man_cartwheeling_medium_skin_tone"] = "🤸🏽‍♂️", - ["man_cartwheeling_tone4"] = "🤸🏾‍♂️", - ["man_cartwheeling_medium_dark_skin_tone"] = "🤸🏾‍♂️", - ["man_cartwheeling_tone5"] = "🤸🏿‍♂️", - ["man_cartwheeling_dark_skin_tone"] = "🤸🏿‍♂️", - ["person_bouncing_ball"] = "⛹️", - ["basketball_player"] = "⛹️", - ["person_with_ball"] = "⛹️", - ["person_bouncing_ball_tone1"] = "⛹🏻", - ["basketball_player_tone1"] = "⛹🏻", - ["person_with_ball_tone1"] = "⛹🏻", - ["person_bouncing_ball_tone2"] = "⛹🏼", - ["basketball_player_tone2"] = "⛹🏼", - ["person_with_ball_tone2"] = "⛹🏼", - ["person_bouncing_ball_tone3"] = "⛹🏽", - ["basketball_player_tone3"] = "⛹🏽", - ["person_with_ball_tone3"] = "⛹🏽", - ["person_bouncing_ball_tone4"] = "⛹🏾", - ["basketball_player_tone4"] = "⛹🏾", - ["person_with_ball_tone4"] = "⛹🏾", - ["person_bouncing_ball_tone5"] = "⛹🏿", - ["basketball_player_tone5"] = "⛹🏿", - ["person_with_ball_tone5"] = "⛹🏿", - ["woman_bouncing_ball"] = "⛹️‍♀️", - ["woman_bouncing_ball_tone1"] = "⛹🏻‍♀️", - ["woman_bouncing_ball_light_skin_tone"] = "⛹🏻‍♀️", - ["woman_bouncing_ball_tone2"] = "⛹🏼‍♀️", - ["woman_bouncing_ball_medium_light_skin_tone"] = "⛹🏼‍♀️", - ["woman_bouncing_ball_tone3"] = "⛹🏽‍♀️", - ["woman_bouncing_ball_medium_skin_tone"] = "⛹🏽‍♀️", - ["woman_bouncing_ball_tone4"] = "⛹🏾‍♀️", - ["woman_bouncing_ball_medium_dark_skin_tone"] = "⛹🏾‍♀️", - ["woman_bouncing_ball_tone5"] = "⛹🏿‍♀️", - ["woman_bouncing_ball_dark_skin_tone"] = "⛹🏿‍♀️", - ["man_bouncing_ball"] = "⛹️‍♂️", - ["man_bouncing_ball_tone1"] = "⛹🏻‍♂️", - ["man_bouncing_ball_light_skin_tone"] = "⛹🏻‍♂️", - ["man_bouncing_ball_tone2"] = "⛹🏼‍♂️", - ["man_bouncing_ball_medium_light_skin_tone"] = "⛹🏼‍♂️", - ["man_bouncing_ball_tone3"] = "⛹🏽‍♂️", - ["man_bouncing_ball_medium_skin_tone"] = "⛹🏽‍♂️", - ["man_bouncing_ball_tone4"] = "⛹🏾‍♂️", - ["man_bouncing_ball_medium_dark_skin_tone"] = "⛹🏾‍♂️", - ["man_bouncing_ball_tone5"] = "⛹🏿‍♂️", - ["man_bouncing_ball_dark_skin_tone"] = "⛹🏿‍♂️", - ["person_fencing"] = "🤺", - ["fencer"] = "🤺", - ["fencing"] = "🤺", - ["person_playing_handball"] = "🤾", - ["handball"] = "🤾", - ["person_playing_handball_tone1"] = "🤾🏻", - ["handball_tone1"] = "🤾🏻", - ["person_playing_handball_tone2"] = "🤾🏼", - ["handball_tone2"] = "🤾🏼", - ["person_playing_handball_tone3"] = "🤾🏽", - ["handball_tone3"] = "🤾🏽", - ["person_playing_handball_tone4"] = "🤾🏾", - ["handball_tone4"] = "🤾🏾", - ["person_playing_handball_tone5"] = "🤾🏿", - ["handball_tone5"] = "🤾🏿", - ["woman_playing_handball"] = "🤾‍♀️", - ["woman_playing_handball_tone1"] = "🤾🏻‍♀️", - ["woman_playing_handball_light_skin_tone"] = "🤾🏻‍♀️", - ["woman_playing_handball_tone2"] = "🤾🏼‍♀️", - ["woman_playing_handball_medium_light_skin_tone"] = "🤾🏼‍♀️", - ["woman_playing_handball_tone3"] = "🤾🏽‍♀️", - ["woman_playing_handball_medium_skin_tone"] = "🤾🏽‍♀️", - ["woman_playing_handball_tone4"] = "🤾🏾‍♀️", - ["woman_playing_handball_medium_dark_skin_tone"] = "🤾🏾‍♀️", - ["woman_playing_handball_tone5"] = "🤾🏿‍♀️", - ["woman_playing_handball_dark_skin_tone"] = "🤾🏿‍♀️", - ["man_playing_handball"] = "🤾‍♂️", - ["man_playing_handball_tone1"] = "🤾🏻‍♂️", - ["man_playing_handball_light_skin_tone"] = "🤾🏻‍♂️", - ["man_playing_handball_tone2"] = "🤾🏼‍♂️", - ["man_playing_handball_medium_light_skin_tone"] = "🤾🏼‍♂️", - ["man_playing_handball_tone3"] = "🤾🏽‍♂️", - ["man_playing_handball_medium_skin_tone"] = "🤾🏽‍♂️", - ["man_playing_handball_tone4"] = "🤾🏾‍♂️", - ["man_playing_handball_medium_dark_skin_tone"] = "🤾🏾‍♂️", - ["man_playing_handball_tone5"] = "🤾🏿‍♂️", - ["man_playing_handball_dark_skin_tone"] = "🤾🏿‍♂️", - ["person_golfing"] = "🏌️", - ["golfer"] = "🏌️", - ["person_golfing_tone1"] = "🏌🏻", - ["person_golfing_light_skin_tone"] = "🏌🏻", - ["person_golfing_tone2"] = "🏌🏼", - ["person_golfing_medium_light_skin_tone"] = "🏌🏼", - ["person_golfing_tone3"] = "🏌🏽", - ["person_golfing_medium_skin_tone"] = "🏌🏽", - ["person_golfing_tone4"] = "🏌🏾", - ["person_golfing_medium_dark_skin_tone"] = "🏌🏾", - ["person_golfing_tone5"] = "🏌🏿", - ["person_golfing_dark_skin_tone"] = "🏌🏿", - ["woman_golfing"] = "🏌️‍♀️", - ["woman_golfing_tone1"] = "🏌🏻‍♀️", - ["woman_golfing_light_skin_tone"] = "🏌🏻‍♀️", - ["woman_golfing_tone2"] = "🏌🏼‍♀️", - ["woman_golfing_medium_light_skin_tone"] = "🏌🏼‍♀️", - ["woman_golfing_tone3"] = "🏌🏽‍♀️", - ["woman_golfing_medium_skin_tone"] = "🏌🏽‍♀️", - ["woman_golfing_tone4"] = "🏌🏾‍♀️", - ["woman_golfing_medium_dark_skin_tone"] = "🏌🏾‍♀️", - ["woman_golfing_tone5"] = "🏌🏿‍♀️", - ["woman_golfing_dark_skin_tone"] = "🏌🏿‍♀️", - ["man_golfing"] = "🏌️‍♂️", - ["man_golfing_tone1"] = "🏌🏻‍♂️", - ["man_golfing_light_skin_tone"] = "🏌🏻‍♂️", - ["man_golfing_tone2"] = "🏌🏼‍♂️", - ["man_golfing_medium_light_skin_tone"] = "🏌🏼‍♂️", - ["man_golfing_tone3"] = "🏌🏽‍♂️", - ["man_golfing_medium_skin_tone"] = "🏌🏽‍♂️", - ["man_golfing_tone4"] = "🏌🏾‍♂️", - ["man_golfing_medium_dark_skin_tone"] = "🏌🏾‍♂️", - ["man_golfing_tone5"] = "🏌🏿‍♂️", - ["man_golfing_dark_skin_tone"] = "🏌🏿‍♂️", - ["horse_racing"] = "🏇", - ["horse_racing_tone1"] = "🏇🏻", - ["horse_racing_tone2"] = "🏇🏼", - ["horse_racing_tone3"] = "🏇🏽", - ["horse_racing_tone4"] = "🏇🏾", - ["horse_racing_tone5"] = "🏇🏿", - ["person_in_lotus_position"] = "🧘", - ["person_in_lotus_position_tone1"] = "🧘🏻", - ["person_in_lotus_position_light_skin_tone"] = "🧘🏻", - ["person_in_lotus_position_tone2"] = "🧘🏼", - ["person_in_lotus_position_medium_light_skin_tone"] = "🧘🏼", - ["person_in_lotus_position_tone3"] = "🧘🏽", - ["person_in_lotus_position_medium_skin_tone"] = "🧘🏽", - ["person_in_lotus_position_tone4"] = "🧘🏾", - ["person_in_lotus_position_medium_dark_skin_tone"] = "🧘🏾", - ["person_in_lotus_position_tone5"] = "🧘🏿", - ["person_in_lotus_position_dark_skin_tone"] = "🧘🏿", - ["woman_in_lotus_position"] = "🧘‍♀️", - ["woman_in_lotus_position_tone1"] = "🧘🏻‍♀️", - ["woman_in_lotus_position_light_skin_tone"] = "🧘🏻‍♀️", - ["woman_in_lotus_position_tone2"] = "🧘🏼‍♀️", - ["woman_in_lotus_position_medium_light_skin_tone"] = "🧘🏼‍♀️", - ["woman_in_lotus_position_tone3"] = "🧘🏽‍♀️", - ["woman_in_lotus_position_medium_skin_tone"] = "🧘🏽‍♀️", - ["woman_in_lotus_position_tone4"] = "🧘🏾‍♀️", - ["woman_in_lotus_position_medium_dark_skin_tone"] = "🧘🏾‍♀️", - ["woman_in_lotus_position_tone5"] = "🧘🏿‍♀️", - ["woman_in_lotus_position_dark_skin_tone"] = "🧘🏿‍♀️", - ["man_in_lotus_position"] = "🧘‍♂️", - ["man_in_lotus_position_tone1"] = "🧘🏻‍♂️", - ["man_in_lotus_position_light_skin_tone"] = "🧘🏻‍♂️", - ["man_in_lotus_position_tone2"] = "🧘🏼‍♂️", - ["man_in_lotus_position_medium_light_skin_tone"] = "🧘🏼‍♂️", - ["man_in_lotus_position_tone3"] = "🧘🏽‍♂️", - ["man_in_lotus_position_medium_skin_tone"] = "🧘🏽‍♂️", - ["man_in_lotus_position_tone4"] = "🧘🏾‍♂️", - ["man_in_lotus_position_medium_dark_skin_tone"] = "🧘🏾‍♂️", - ["man_in_lotus_position_tone5"] = "🧘🏿‍♂️", - ["man_in_lotus_position_dark_skin_tone"] = "🧘🏿‍♂️", - ["person_surfing"] = "🏄", - ["surfer"] = "🏄", - ["person_surfing_tone1"] = "🏄🏻", - ["surfer_tone1"] = "🏄🏻", - ["person_surfing_tone2"] = "🏄🏼", - ["surfer_tone2"] = "🏄🏼", - ["person_surfing_tone3"] = "🏄🏽", - ["surfer_tone3"] = "🏄🏽", - ["person_surfing_tone4"] = "🏄🏾", - ["surfer_tone4"] = "🏄🏾", - ["person_surfing_tone5"] = "🏄🏿", - ["surfer_tone5"] = "🏄🏿", - ["woman_surfing"] = "🏄‍♀️", - ["woman_surfing_tone1"] = "🏄🏻‍♀️", - ["woman_surfing_light_skin_tone"] = "🏄🏻‍♀️", - ["woman_surfing_tone2"] = "🏄🏼‍♀️", - ["woman_surfing_medium_light_skin_tone"] = "🏄🏼‍♀️", - ["woman_surfing_tone3"] = "🏄🏽‍♀️", - ["woman_surfing_medium_skin_tone"] = "🏄🏽‍♀️", - ["woman_surfing_tone4"] = "🏄🏾‍♀️", - ["woman_surfing_medium_dark_skin_tone"] = "🏄🏾‍♀️", - ["woman_surfing_tone5"] = "🏄🏿‍♀️", - ["woman_surfing_dark_skin_tone"] = "🏄🏿‍♀️", - ["man_surfing"] = "🏄‍♂️", - ["man_surfing_tone1"] = "🏄🏻‍♂️", - ["man_surfing_light_skin_tone"] = "🏄🏻‍♂️", - ["man_surfing_tone2"] = "🏄🏼‍♂️", - ["man_surfing_medium_light_skin_tone"] = "🏄🏼‍♂️", - ["man_surfing_tone3"] = "🏄🏽‍♂️", - ["man_surfing_medium_skin_tone"] = "🏄🏽‍♂️", - ["man_surfing_tone4"] = "🏄🏾‍♂️", - ["man_surfing_medium_dark_skin_tone"] = "🏄🏾‍♂️", - ["man_surfing_tone5"] = "🏄🏿‍♂️", - ["man_surfing_dark_skin_tone"] = "🏄🏿‍♂️", - ["person_swimming"] = "🏊", - ["swimmer"] = "🏊", - ["person_swimming_tone1"] = "🏊🏻", - ["swimmer_tone1"] = "🏊🏻", - ["person_swimming_tone2"] = "🏊🏼", - ["swimmer_tone2"] = "🏊🏼", - ["person_swimming_tone3"] = "🏊🏽", - ["swimmer_tone3"] = "🏊🏽", - ["person_swimming_tone4"] = "🏊🏾", - ["swimmer_tone4"] = "🏊🏾", - ["person_swimming_tone5"] = "🏊🏿", - ["swimmer_tone5"] = "🏊🏿", - ["woman_swimming"] = "🏊‍♀️", - ["woman_swimming_tone1"] = "🏊🏻‍♀️", - ["woman_swimming_light_skin_tone"] = "🏊🏻‍♀️", - ["woman_swimming_tone2"] = "🏊🏼‍♀️", - ["woman_swimming_medium_light_skin_tone"] = "🏊🏼‍♀️", - ["woman_swimming_tone3"] = "🏊🏽‍♀️", - ["woman_swimming_medium_skin_tone"] = "🏊🏽‍♀️", - ["woman_swimming_tone4"] = "🏊🏾‍♀️", - ["woman_swimming_medium_dark_skin_tone"] = "🏊🏾‍♀️", - ["woman_swimming_tone5"] = "🏊🏿‍♀️", - ["woman_swimming_dark_skin_tone"] = "🏊🏿‍♀️", - ["man_swimming"] = "🏊‍♂️", - ["man_swimming_tone1"] = "🏊🏻‍♂️", - ["man_swimming_light_skin_tone"] = "🏊🏻‍♂️", - ["man_swimming_tone2"] = "🏊🏼‍♂️", - ["man_swimming_medium_light_skin_tone"] = "🏊🏼‍♂️", - ["man_swimming_tone3"] = "🏊🏽‍♂️", - ["man_swimming_medium_skin_tone"] = "🏊🏽‍♂️", - ["man_swimming_tone4"] = "🏊🏾‍♂️", - ["man_swimming_medium_dark_skin_tone"] = "🏊🏾‍♂️", - ["man_swimming_tone5"] = "🏊🏿‍♂️", - ["man_swimming_dark_skin_tone"] = "🏊🏿‍♂️", - ["person_playing_water_polo"] = "🤽", - ["water_polo"] = "🤽", - ["person_playing_water_polo_tone1"] = "🤽🏻", - ["water_polo_tone1"] = "🤽🏻", - ["person_playing_water_polo_tone2"] = "🤽🏼", - ["water_polo_tone2"] = "🤽🏼", - ["person_playing_water_polo_tone3"] = "🤽🏽", - ["water_polo_tone3"] = "🤽🏽", - ["person_playing_water_polo_tone4"] = "🤽🏾", - ["water_polo_tone4"] = "🤽🏾", - ["person_playing_water_polo_tone5"] = "🤽🏿", - ["water_polo_tone5"] = "🤽🏿", - ["woman_playing_water_polo"] = "🤽‍♀️", - ["woman_playing_water_polo_tone1"] = "🤽🏻‍♀️", - ["woman_playing_water_polo_light_skin_tone"] = "🤽🏻‍♀️", - ["woman_playing_water_polo_tone2"] = "🤽🏼‍♀️", - ["woman_playing_water_polo_medium_light_skin_tone"] = "🤽🏼‍♀️", - ["woman_playing_water_polo_tone3"] = "🤽🏽‍♀️", - ["woman_playing_water_polo_medium_skin_tone"] = "🤽🏽‍♀️", - ["woman_playing_water_polo_tone4"] = "🤽🏾‍♀️", - ["woman_playing_water_polo_medium_dark_skin_tone"] = "🤽🏾‍♀️", - ["woman_playing_water_polo_tone5"] = "🤽🏿‍♀️", - ["woman_playing_water_polo_dark_skin_tone"] = "🤽🏿‍♀️", - ["man_playing_water_polo"] = "🤽‍♂️", - ["man_playing_water_polo_tone1"] = "🤽🏻‍♂️", - ["man_playing_water_polo_light_skin_tone"] = "🤽🏻‍♂️", - ["man_playing_water_polo_tone2"] = "🤽🏼‍♂️", - ["man_playing_water_polo_medium_light_skin_tone"] = "🤽🏼‍♂️", - ["man_playing_water_polo_tone3"] = "🤽🏽‍♂️", - ["man_playing_water_polo_medium_skin_tone"] = "🤽🏽‍♂️", - ["man_playing_water_polo_tone4"] = "🤽🏾‍♂️", - ["man_playing_water_polo_medium_dark_skin_tone"] = "🤽🏾‍♂️", - ["man_playing_water_polo_tone5"] = "🤽🏿‍♂️", - ["man_playing_water_polo_dark_skin_tone"] = "🤽🏿‍♂️", - ["person_rowing_boat"] = "🚣", - ["rowboat"] = "🚣", - ["person_rowing_boat_tone1"] = "🚣🏻", - ["rowboat_tone1"] = "🚣🏻", - ["person_rowing_boat_tone2"] = "🚣🏼", - ["rowboat_tone2"] = "🚣🏼", - ["person_rowing_boat_tone3"] = "🚣🏽", - ["rowboat_tone3"] = "🚣🏽", - ["person_rowing_boat_tone4"] = "🚣🏾", - ["rowboat_tone4"] = "🚣🏾", - ["person_rowing_boat_tone5"] = "🚣🏿", - ["rowboat_tone5"] = "🚣🏿", - ["woman_rowing_boat"] = "🚣‍♀️", - ["woman_rowing_boat_tone1"] = "🚣🏻‍♀️", - ["woman_rowing_boat_light_skin_tone"] = "🚣🏻‍♀️", - ["woman_rowing_boat_tone2"] = "🚣🏼‍♀️", - ["woman_rowing_boat_medium_light_skin_tone"] = "🚣🏼‍♀️", - ["woman_rowing_boat_tone3"] = "🚣🏽‍♀️", - ["woman_rowing_boat_medium_skin_tone"] = "🚣🏽‍♀️", - ["woman_rowing_boat_tone4"] = "🚣🏾‍♀️", - ["woman_rowing_boat_medium_dark_skin_tone"] = "🚣🏾‍♀️", - ["woman_rowing_boat_tone5"] = "🚣🏿‍♀️", - ["woman_rowing_boat_dark_skin_tone"] = "🚣🏿‍♀️", - ["man_rowing_boat"] = "🚣‍♂️", - ["man_rowing_boat_tone1"] = "🚣🏻‍♂️", - ["man_rowing_boat_light_skin_tone"] = "🚣🏻‍♂️", - ["man_rowing_boat_tone2"] = "🚣🏼‍♂️", - ["man_rowing_boat_medium_light_skin_tone"] = "🚣🏼‍♂️", - ["man_rowing_boat_tone3"] = "🚣🏽‍♂️", - ["man_rowing_boat_medium_skin_tone"] = "🚣🏽‍♂️", - ["man_rowing_boat_tone4"] = "🚣🏾‍♂️", - ["man_rowing_boat_medium_dark_skin_tone"] = "🚣🏾‍♂️", - ["man_rowing_boat_tone5"] = "🚣🏿‍♂️", - ["man_rowing_boat_dark_skin_tone"] = "🚣🏿‍♂️", - ["person_climbing"] = "🧗", - ["person_climbing_tone1"] = "🧗🏻", - ["person_climbing_light_skin_tone"] = "🧗🏻", - ["person_climbing_tone2"] = "🧗🏼", - ["person_climbing_medium_light_skin_tone"] = "🧗🏼", - ["person_climbing_tone3"] = "🧗🏽", - ["person_climbing_medium_skin_tone"] = "🧗🏽", - ["person_climbing_tone4"] = "🧗🏾", - ["person_climbing_medium_dark_skin_tone"] = "🧗🏾", - ["person_climbing_tone5"] = "🧗🏿", - ["person_climbing_dark_skin_tone"] = "🧗🏿", - ["woman_climbing"] = "🧗‍♀️", - ["woman_climbing_tone1"] = "🧗🏻‍♀️", - ["woman_climbing_light_skin_tone"] = "🧗🏻‍♀️", - ["woman_climbing_tone2"] = "🧗🏼‍♀️", - ["woman_climbing_medium_light_skin_tone"] = "🧗🏼‍♀️", - ["woman_climbing_tone3"] = "🧗🏽‍♀️", - ["woman_climbing_medium_skin_tone"] = "🧗🏽‍♀️", - ["woman_climbing_tone4"] = "🧗🏾‍♀️", - ["woman_climbing_medium_dark_skin_tone"] = "🧗🏾‍♀️", - ["woman_climbing_tone5"] = "🧗🏿‍♀️", - ["woman_climbing_dark_skin_tone"] = "🧗🏿‍♀️", - ["man_climbing"] = "🧗‍♂️", - ["man_climbing_tone1"] = "🧗🏻‍♂️", - ["man_climbing_light_skin_tone"] = "🧗🏻‍♂️", - ["man_climbing_tone2"] = "🧗🏼‍♂️", - ["man_climbing_medium_light_skin_tone"] = "🧗🏼‍♂️", - ["man_climbing_tone3"] = "🧗🏽‍♂️", - ["man_climbing_medium_skin_tone"] = "🧗🏽‍♂️", - ["man_climbing_tone4"] = "🧗🏾‍♂️", - ["man_climbing_medium_dark_skin_tone"] = "🧗🏾‍♂️", - ["man_climbing_tone5"] = "🧗🏿‍♂️", - ["man_climbing_dark_skin_tone"] = "🧗🏿‍♂️", - ["person_mountain_biking"] = "🚵", - ["mountain_bicyclist"] = "🚵", - ["person_mountain_biking_tone1"] = "🚵🏻", - ["mountain_bicyclist_tone1"] = "🚵🏻", - ["person_mountain_biking_tone2"] = "🚵🏼", - ["mountain_bicyclist_tone2"] = "🚵🏼", - ["person_mountain_biking_tone3"] = "🚵🏽", - ["mountain_bicyclist_tone3"] = "🚵🏽", - ["person_mountain_biking_tone4"] = "🚵🏾", - ["mountain_bicyclist_tone4"] = "🚵🏾", - ["person_mountain_biking_tone5"] = "🚵🏿", - ["mountain_bicyclist_tone5"] = "🚵🏿", - ["woman_mountain_biking"] = "🚵‍♀️", - ["woman_mountain_biking_tone1"] = "🚵🏻‍♀️", - ["woman_mountain_biking_light_skin_tone"] = "🚵🏻‍♀️", - ["woman_mountain_biking_tone2"] = "🚵🏼‍♀️", - ["woman_mountain_biking_medium_light_skin_tone"] = "🚵🏼‍♀️", - ["woman_mountain_biking_tone3"] = "🚵🏽‍♀️", - ["woman_mountain_biking_medium_skin_tone"] = "🚵🏽‍♀️", - ["woman_mountain_biking_tone4"] = "🚵🏾‍♀️", - ["woman_mountain_biking_medium_dark_skin_tone"] = "🚵🏾‍♀️", - ["woman_mountain_biking_tone5"] = "🚵🏿‍♀️", - ["woman_mountain_biking_dark_skin_tone"] = "🚵🏿‍♀️", - ["man_mountain_biking"] = "🚵‍♂️", - ["man_mountain_biking_tone1"] = "🚵🏻‍♂️", - ["man_mountain_biking_light_skin_tone"] = "🚵🏻‍♂️", - ["man_mountain_biking_tone2"] = "🚵🏼‍♂️", - ["man_mountain_biking_medium_light_skin_tone"] = "🚵🏼‍♂️", - ["man_mountain_biking_tone3"] = "🚵🏽‍♂️", - ["man_mountain_biking_medium_skin_tone"] = "🚵🏽‍♂️", - ["man_mountain_biking_tone4"] = "🚵🏾‍♂️", - ["man_mountain_biking_medium_dark_skin_tone"] = "🚵🏾‍♂️", - ["man_mountain_biking_tone5"] = "🚵🏿‍♂️", - ["man_mountain_biking_dark_skin_tone"] = "🚵🏿‍♂️", - ["person_biking"] = "🚴", - ["bicyclist"] = "🚴", - ["person_biking_tone1"] = "🚴🏻", - ["bicyclist_tone1"] = "🚴🏻", - ["person_biking_tone2"] = "🚴🏼", - ["bicyclist_tone2"] = "🚴🏼", - ["person_biking_tone3"] = "🚴🏽", - ["bicyclist_tone3"] = "🚴🏽", - ["person_biking_tone4"] = "🚴🏾", - ["bicyclist_tone4"] = "🚴🏾", - ["person_biking_tone5"] = "🚴🏿", - ["bicyclist_tone5"] = "🚴🏿", - ["woman_biking"] = "🚴‍♀️", - ["woman_biking_tone1"] = "🚴🏻‍♀️", - ["woman_biking_light_skin_tone"] = "🚴🏻‍♀️", - ["woman_biking_tone2"] = "🚴🏼‍♀️", - ["woman_biking_medium_light_skin_tone"] = "🚴🏼‍♀️", - ["woman_biking_tone3"] = "🚴🏽‍♀️", - ["woman_biking_medium_skin_tone"] = "🚴🏽‍♀️", - ["woman_biking_tone4"] = "🚴🏾‍♀️", - ["woman_biking_medium_dark_skin_tone"] = "🚴🏾‍♀️", - ["woman_biking_tone5"] = "🚴🏿‍♀️", - ["woman_biking_dark_skin_tone"] = "🚴🏿‍♀️", - ["man_biking"] = "🚴‍♂️", - ["man_biking_tone1"] = "🚴🏻‍♂️", - ["man_biking_light_skin_tone"] = "🚴🏻‍♂️", - ["man_biking_tone2"] = "🚴🏼‍♂️", - ["man_biking_medium_light_skin_tone"] = "🚴🏼‍♂️", - ["man_biking_tone3"] = "🚴🏽‍♂️", - ["man_biking_medium_skin_tone"] = "🚴🏽‍♂️", - ["man_biking_tone4"] = "🚴🏾‍♂️", - ["man_biking_medium_dark_skin_tone"] = "🚴🏾‍♂️", - ["man_biking_tone5"] = "🚴🏿‍♂️", - ["man_biking_dark_skin_tone"] = "🚴🏿‍♂️", - ["trophy"] = "🏆", - ["first_place"] = "🥇", - ["first_place_medal"] = "🥇", - ["second_place"] = "🥈", - ["second_place_medal"] = "🥈", - ["third_place"] = "🥉", - ["third_place_medal"] = "🥉", - ["medal"] = "🏅", - ["sports_medal"] = "🏅", - ["military_medal"] = "🎖️", - ["rosette"] = "🏵️", - ["reminder_ribbon"] = "🎗️", - ["ticket"] = "🎫", - ["tickets"] = "🎟️", - ["admission_tickets"] = "🎟️", - ["circus_tent"] = "🎪", - ["person_juggling"] = "🤹", - ["juggling"] = "🤹", - ["juggler"] = "🤹", - ["person_juggling_tone1"] = "🤹🏻", - ["juggling_tone1"] = "🤹🏻", - ["juggler_tone1"] = "🤹🏻", - ["person_juggling_tone2"] = "🤹🏼", - ["juggling_tone2"] = "🤹🏼", - ["juggler_tone2"] = "🤹🏼", - ["person_juggling_tone3"] = "🤹🏽", - ["juggling_tone3"] = "🤹🏽", - ["juggler_tone3"] = "🤹🏽", - ["person_juggling_tone4"] = "🤹🏾", - ["juggling_tone4"] = "🤹🏾", - ["juggler_tone4"] = "🤹🏾", - ["person_juggling_tone5"] = "🤹🏿", - ["juggling_tone5"] = "🤹🏿", - ["juggler_tone5"] = "🤹🏿", - ["woman_juggling"] = "🤹‍♀️", - ["woman_juggling_tone1"] = "🤹🏻‍♀️", - ["woman_juggling_light_skin_tone"] = "🤹🏻‍♀️", - ["woman_juggling_tone2"] = "🤹🏼‍♀️", - ["woman_juggling_medium_light_skin_tone"] = "🤹🏼‍♀️", - ["woman_juggling_tone3"] = "🤹🏽‍♀️", - ["woman_juggling_medium_skin_tone"] = "🤹🏽‍♀️", - ["woman_juggling_tone4"] = "🤹🏾‍♀️", - ["woman_juggling_medium_dark_skin_tone"] = "🤹🏾‍♀️", - ["woman_juggling_tone5"] = "🤹🏿‍♀️", - ["woman_juggling_dark_skin_tone"] = "🤹🏿‍♀️", - ["man_juggling"] = "🤹‍♂️", - ["man_juggling_tone1"] = "🤹🏻‍♂️", - ["man_juggling_light_skin_tone"] = "🤹🏻‍♂️", - ["man_juggling_tone2"] = "🤹🏼‍♂️", - ["man_juggling_medium_light_skin_tone"] = "🤹🏼‍♂️", - ["man_juggling_tone3"] = "🤹🏽‍♂️", - ["man_juggling_medium_skin_tone"] = "🤹🏽‍♂️", - ["man_juggling_tone4"] = "🤹🏾‍♂️", - ["man_juggling_medium_dark_skin_tone"] = "🤹🏾‍♂️", - ["man_juggling_tone5"] = "🤹🏿‍♂️", - ["man_juggling_dark_skin_tone"] = "🤹🏿‍♂️", - ["performing_arts"] = "🎭", - ["ballet_shoes"] = "🩰", - ["art"] = "🎨", - ["clapper"] = "🎬", - ["microphone"] = "🎤", - ["headphones"] = "🎧", - ["musical_score"] = "🎼", - ["musical_keyboard"] = "🎹", - ["drum"] = "🥁", - ["drum_with_drumsticks"] = "🥁", - ["long_drum"] = "🪘", - ["saxophone"] = "🎷", - ["trumpet"] = "🎺", - ["guitar"] = "🎸", - ["banjo"] = "🪕", - ["violin"] = "🎻", - ["accordion"] = "🪗", - ["game_die"] = "🎲", - ["chess_pawn"] = "♟️", - ["dart"] = "🎯", - ["bowling"] = "🎳", - ["video_game"] = "🎮", - ["slot_machine"] = "🎰", - ["jigsaw"] = "🧩", - ["red_car"] = "🚗", - ["taxi"] = "🚕", - ["blue_car"] = "🚙", - ["pickup_truck"] = "🛻", - ["bus"] = "🚌", - ["trolleybus"] = "🚎", - ["race_car"] = "🏎️", - ["racing_car"] = "🏎️", - ["police_car"] = "🚓", - ["ambulance"] = "🚑", - ["fire_engine"] = "🚒", - ["minibus"] = "🚐", - ["truck"] = "🚚", - ["articulated_lorry"] = "🚛", - ["tractor"] = "🚜", - ["probing_cane"] = "🦯", - ["manual_wheelchair"] = "🦽", - ["motorized_wheelchair"] = "🦼", - ["scooter"] = "🛴", - ["bike"] = "🚲", - ["motor_scooter"] = "🛵", - ["motorbike"] = "🛵", - ["motorcycle"] = "🏍️", - ["racing_motorcycle"] = "🏍️", - ["auto_rickshaw"] = "🛺", - ["rotating_light"] = "🚨", - ["oncoming_police_car"] = "🚔", - ["oncoming_bus"] = "🚍", - ["oncoming_automobile"] = "🚘", - ["oncoming_taxi"] = "🚖", - ["aerial_tramway"] = "🚡", - ["mountain_cableway"] = "🚠", - ["suspension_railway"] = "🚟", - ["railway_car"] = "🚃", - ["train"] = "🚋", - ["mountain_railway"] = "🚞", - ["monorail"] = "🚝", - ["bullettrain_side"] = "🚄", - ["bullettrain_front"] = "🚅", - ["light_rail"] = "🚈", - ["steam_locomotive"] = "🚂", - ["train2"] = "🚆", - ["metro"] = "🚇", - ["tram"] = "🚊", - ["station"] = "🚉", - ["airplane"] = "✈️", - ["airplane_departure"] = "🛫", - ["airplane_arriving"] = "🛬", - ["airplane_small"] = "🛩️", - ["small_airplane"] = "🛩️", - ["seat"] = "💺", - ["satellite_orbital"] = "🛰️", - ["rocket"] = "🚀", - ["flying_saucer"] = "🛸", - ["helicopter"] = "🚁", - ["canoe"] = "🛶", - ["kayak"] = "🛶", - ["sailboat"] = "⛵", - ["speedboat"] = "🚤", - ["motorboat"] = "🛥️", - ["cruise_ship"] = "🛳️", - ["passenger_ship"] = "🛳️", - ["ferry"] = "⛴️", - ["ship"] = "🚢", - ["anchor"] = "⚓", - ["fuelpump"] = "⛽", - ["construction"] = "🚧", - ["vertical_traffic_light"] = "🚦", - ["traffic_light"] = "🚥", - ["busstop"] = "🚏", - ["map"] = "🗺️", - ["world_map"] = "🗺️", - ["moyai"] = "🗿", - ["statue_of_liberty"] = "🗽", - ["tokyo_tower"] = "🗼", - ["european_castle"] = "🏰", - ["japanese_castle"] = "🏯", - ["stadium"] = "🏟️", - ["ferris_wheel"] = "🎡", - ["roller_coaster"] = "🎢", - ["carousel_horse"] = "🎠", - ["fountain"] = "⛲", - ["beach_umbrella"] = "⛱️", - ["umbrella_on_ground"] = "⛱️", - ["beach"] = "🏖️", - ["beach_with_umbrella"] = "🏖️", - ["island"] = "🏝️", - ["desert_island"] = "🏝️", - ["desert"] = "🏜️", - ["volcano"] = "🌋", - ["mountain"] = "⛰️", - ["mountain_snow"] = "🏔️", - ["snow_capped_mountain"] = "🏔️", - ["mount_fuji"] = "🗻", - ["camping"] = "🏕️", - ["tent"] = "⛺", - ["house"] = "🏠", - ["house_with_garden"] = "🏡", - ["homes"] = "🏘️", - ["house_buildings"] = "🏘️", - ["house_abandoned"] = "🏚️", - ["derelict_house_building"] = "🏚️", - ["hut"] = "🛖", - ["construction_site"] = "🏗️", - ["building_construction"] = "🏗️", - ["factory"] = "🏭", - ["office"] = "🏢", - ["department_store"] = "🏬", - ["post_office"] = "🏣", - ["european_post_office"] = "🏤", - ["hospital"] = "🏥", - ["bank"] = "🏦", - ["hotel"] = "🏨", - ["convenience_store"] = "🏪", - ["school"] = "🏫", - ["love_hotel"] = "🏩", - ["wedding"] = "💒", - ["classical_building"] = "🏛️", - ["church"] = "⛪", - ["mosque"] = "🕌", - ["synagogue"] = "🕍", - ["hindu_temple"] = "🛕", - ["kaaba"] = "🕋", - ["shinto_shrine"] = "⛩️", - ["railway_track"] = "🛤️", - ["railroad_track"] = "🛤️", - ["motorway"] = "🛣️", - ["japan"] = "🗾", - ["rice_scene"] = "🎑", - ["park"] = "🏞️", - ["national_park"] = "🏞️", - ["sunrise"] = "🌅", - ["sunrise_over_mountains"] = "🌄", - ["stars"] = "🌠", - ["sparkler"] = "🎇", - ["fireworks"] = "🎆", - ["city_sunset"] = "🌇", - ["city_sunrise"] = "🌇", - ["city_dusk"] = "🌆", - ["cityscape"] = "🏙️", - ["night_with_stars"] = "🌃", - ["milky_way"] = "🌌", - ["bridge_at_night"] = "🌉", - ["foggy"] = "🌁", - ["watch"] = "⌚", - ["mobile_phone"] = "📱", - ["iphone"] = "📱", - ["calling"] = "📲", - ["computer"] = "💻", - ["keyboard"] = "⌨️", - ["desktop"] = "🖥️", - ["desktop_computer"] = "🖥️", - ["printer"] = "🖨️", - ["mouse_three_button"] = "🖱️", - ["three_button_mouse"] = "🖱️", - ["trackball"] = "🖲️", - ["joystick"] = "🕹️", - ["compression"] = "🗜️", - ["minidisc"] = "💽", - ["floppy_disk"] = "💾", - ["cd"] = "💿", - ["dvd"] = "📀", - ["vhs"] = "📼", - ["camera"] = "📷", - ["camera_with_flash"] = "📸", - ["video_camera"] = "📹", - ["movie_camera"] = "🎥", - ["projector"] = "📽️", - ["film_projector"] = "📽️", - ["film_frames"] = "🎞️", - ["telephone_receiver"] = "📞", - ["telephone"] = "☎️", - ["pager"] = "📟", - ["fax"] = "📠", - ["tv"] = "📺", - ["radio"] = "📻", - ["microphone2"] = "🎙️", - ["studio_microphone"] = "🎙️", - ["level_slider"] = "🎚️", - ["control_knobs"] = "🎛️", - ["compass"] = "🧭", - ["stopwatch"] = "⏱️", - ["timer"] = "⏲️", - ["timer_clock"] = "⏲️", - ["alarm_clock"] = "⏰", - ["clock"] = "🕰️", - ["mantlepiece_clock"] = "🕰️", - ["hourglass"] = "⌛", - ["hourglass_flowing_sand"] = "⏳", - ["satellite"] = "📡", - ["battery"] = "🔋", - ["electric_plug"] = "🔌", - ["bulb"] = "💡", - ["flashlight"] = "🔦", - ["candle"] = "🕯️", - ["diya_lamp"] = "🪔", - ["fire_extinguisher"] = "🧯", - ["oil"] = "🛢️", - ["oil_drum"] = "🛢️", - ["money_with_wings"] = "💸", - ["dollar"] = "💵", - ["yen"] = "💴", - ["euro"] = "💶", - ["pound"] = "💷", - ["coin"] = "🪙", - ["moneybag"] = "💰", - ["credit_card"] = "💳", - ["gem"] = "💎", - ["scales"] = "⚖️", - ["ladder"] = "🪜", - ["toolbox"] = "🧰", - ["screwdriver"] = "🪛", - ["wrench"] = "🔧", - ["hammer"] = "🔨", - ["hammer_pick"] = "⚒️", - ["hammer_and_pick"] = "⚒️", - ["tools"] = "🛠️", - ["hammer_and_wrench"] = "🛠️", - ["pick"] = "⛏️", - ["nut_and_bolt"] = "🔩", - ["gear"] = "⚙️", - ["bricks"] = "🧱", - ["chains"] = "⛓️", - ["hook"] = "🪝", - ["knot"] = "🪢", - ["magnet"] = "🧲", - ["gun"] = "🔫", - ["bomb"] = "💣", - ["firecracker"] = "🧨", - ["axe"] = "🪓", - ["carpentry_saw"] = "🪚", - ["knife"] = "🔪", - ["dagger"] = "🗡️", - ["dagger_knife"] = "🗡️", - ["crossed_swords"] = "⚔️", - ["shield"] = "🛡️", - ["smoking"] = "🚬", - ["coffin"] = "⚰️", - ["headstone"] = "🪦", - ["urn"] = "⚱️", - ["funeral_urn"] = "⚱️", - ["amphora"] = "🏺", - ["magic_wand"] = "🪄", - ["crystal_ball"] = "🔮", - ["prayer_beads"] = "📿", - ["nazar_amulet"] = "🧿", - ["barber"] = "💈", - ["alembic"] = "⚗️", - ["telescope"] = "🔭", - ["microscope"] = "🔬", - ["hole"] = "🕳️", - ["window"] = "🪟", - ["adhesive_bandage"] = "🩹", - ["stethoscope"] = "🩺", - ["pill"] = "💊", - ["syringe"] = "💉", - ["drop_of_blood"] = "🩸", - ["dna"] = "🧬", - ["microbe"] = "🦠", - ["petri_dish"] = "🧫", - ["test_tube"] = "🧪", - ["thermometer"] = "🌡️", - ["mouse_trap"] = "🪤", - ["broom"] = "🧹", - ["basket"] = "🧺", - ["sewing_needle"] = "🪡", - ["roll_of_paper"] = "🧻", - ["toilet"] = "🚽", - ["plunger"] = "🪠", - ["bucket"] = "🪣", - ["potable_water"] = "🚰", - ["shower"] = "🚿", - ["bathtub"] = "🛁", - ["bath"] = "🛀", - ["bath_tone1"] = "🛀🏻", - ["bath_tone2"] = "🛀🏼", - ["bath_tone3"] = "🛀🏽", - ["bath_tone4"] = "🛀🏾", - ["bath_tone5"] = "🛀🏿", - ["toothbrush"] = "🪥", - ["soap"] = "🧼", - ["razor"] = "🪒", - ["sponge"] = "🧽", - ["squeeze_bottle"] = "🧴", - ["bellhop"] = "🛎️", - ["bellhop_bell"] = "🛎️", - ["key"] = "🔑", - ["key2"] = "🗝️", - ["old_key"] = "🗝️", - ["door"] = "🚪", - ["chair"] = "🪑", - ["mirror"] = "🪞", - ["couch"] = "🛋️", - ["couch_and_lamp"] = "🛋️", - ["bed"] = "🛏️", - ["sleeping_accommodation"] = "🛌", - ["person_in_bed_tone1"] = "🛌🏻", - ["person_in_bed_light_skin_tone"] = "🛌🏻", - ["person_in_bed_tone2"] = "🛌🏼", - ["person_in_bed_medium_light_skin_tone"] = "🛌🏼", - ["person_in_bed_tone3"] = "🛌🏽", - ["person_in_bed_medium_skin_tone"] = "🛌🏽", - ["person_in_bed_tone4"] = "🛌🏾", - ["person_in_bed_medium_dark_skin_tone"] = "🛌🏾", - ["person_in_bed_tone5"] = "🛌🏿", - ["person_in_bed_dark_skin_tone"] = "🛌🏿", - ["teddy_bear"] = "🧸", - ["frame_photo"] = "🖼️", - ["frame_with_picture"] = "🖼️", - ["shopping_bags"] = "🛍️", - ["shopping_cart"] = "🛒", - ["shopping_trolley"] = "🛒", - ["gift"] = "🎁", - ["balloon"] = "🎈", - ["flags"] = "🎏", - ["ribbon"] = "🎀", - ["confetti_ball"] = "🎊", - ["tada"] = "🎉", - ["piñata"] = "🪅", - ["nesting_dolls"] = "🪆", - ["dolls"] = "🎎", - ["izakaya_lantern"] = "🏮", - ["wind_chime"] = "🎐", - ["red_envelope"] = "🧧", - ["envelope"] = "✉️", - ["envelope_with_arrow"] = "📩", - ["incoming_envelope"] = "📨", - ["e_mail"] = "📧", - ["email"] = "📧", - ["love_letter"] = "💌", - ["inbox_tray"] = "📥", - ["outbox_tray"] = "📤", - ["package"] = "📦", - ["label"] = "🏷️", - ["mailbox_closed"] = "📪", - ["mailbox"] = "📫", - ["mailbox_with_mail"] = "📬", - ["mailbox_with_no_mail"] = "📭", - ["postbox"] = "📮", - ["postal_horn"] = "📯", - ["placard"] = "🪧", - ["scroll"] = "📜", - ["page_with_curl"] = "📃", - ["page_facing_up"] = "📄", - ["bookmark_tabs"] = "📑", - ["receipt"] = "🧾", - ["bar_chart"] = "📊", - ["chart_with_upwards_trend"] = "📈", - ["chart_with_downwards_trend"] = "📉", - ["notepad_spiral"] = "🗒️", - ["spiral_note_pad"] = "🗒️", - ["calendar_spiral"] = "🗓️", - ["spiral_calendar_pad"] = "🗓️", - ["calendar"] = "📆", - ["date"] = "📅", - ["wastebasket"] = "🗑️", - ["card_index"] = "📇", - ["card_box"] = "🗃️", - ["card_file_box"] = "🗃️", - ["ballot_box"] = "🗳️", - ["ballot_box_with_ballot"] = "🗳️", - ["file_cabinet"] = "🗄️", - ["clipboard"] = "📋", - ["file_folder"] = "📁", - ["open_file_folder"] = "📂", - ["dividers"] = "🗂️", - ["card_index_dividers"] = "🗂️", - ["newspaper2"] = "🗞️", - ["rolled_up_newspaper"] = "🗞️", - ["newspaper"] = "📰", - ["notebook"] = "📓", - ["notebook_with_decorative_cover"] = "📔", - ["ledger"] = "📒", - ["closed_book"] = "📕", - ["green_book"] = "📗", - ["blue_book"] = "📘", - ["orange_book"] = "📙", - ["books"] = "📚", - ["book"] = "📖", - ["bookmark"] = "🔖", - ["safety_pin"] = "🧷", - ["link"] = "🔗", - ["paperclip"] = "📎", - ["paperclips"] = "🖇️", - ["linked_paperclips"] = "🖇️", - ["triangular_ruler"] = "📐", - ["straight_ruler"] = "📏", - ["abacus"] = "🧮", - ["pushpin"] = "📌", - ["round_pushpin"] = "📍", - ["scissors"] = "✂️", - ["pen_ballpoint"] = "🖊️", - ["lower_left_ballpoint_pen"] = "🖊️", - ["pen_fountain"] = "🖋️", - ["lower_left_fountain_pen"] = "🖋️", - ["black_nib"] = "✒️", - ["paintbrush"] = "🖌️", - ["lower_left_paintbrush"] = "🖌️", - ["crayon"] = "🖍️", - ["lower_left_crayon"] = "🖍️", - ["pencil"] = "📝", - ["memo"] = "📝", - ["pencil2"] = "✏️", - ["mag"] = "🔍", - ["mag_right"] = "🔎", - ["lock_with_ink_pen"] = "🔏", - ["closed_lock_with_key"] = "🔐", - ["lock"] = "🔒", - ["unlock"] = "🔓", - ["heart"] = "❤️", - ["orange_heart"] = "🧡", - ["yellow_heart"] = "💛", - ["green_heart"] = "💚", - ["blue_heart"] = "💙", - ["purple_heart"] = "💜", - ["black_heart"] = "🖤", - ["brown_heart"] = "🤎", - ["white_heart"] = "🤍", - ["broken_heart"] = "💔", - ["heart_exclamation"] = "❣️", - ["heavy_heart_exclamation_mark_ornament"] = "❣️", - ["two_hearts"] = "💕", - ["revolving_hearts"] = "💞", - ["heartbeat"] = "💓", - ["heartpulse"] = "💗", - ["sparkling_heart"] = "💖", - ["cupid"] = "💘", - ["gift_heart"] = "💝", - ["mending_heart"] = "❤️‍🩹", - ["heart_on_fire"] = "❤️‍🔥", - ["heart_decoration"] = "💟", - ["peace"] = "☮️", - ["peace_symbol"] = "☮️", - ["cross"] = "✝️", - ["latin_cross"] = "✝️", - ["star_and_crescent"] = "☪️", - ["om_symbol"] = "🕉️", - ["wheel_of_dharma"] = "☸️", - ["star_of_david"] = "✡️", - ["six_pointed_star"] = "🔯", - ["menorah"] = "🕎", - ["yin_yang"] = "☯️", - ["orthodox_cross"] = "☦️", - ["place_of_worship"] = "🛐", - ["worship_symbol"] = "🛐", - ["ophiuchus"] = "⛎", - ["aries"] = "♈", - ["taurus"] = "♉", - ["gemini"] = "♊", - ["cancer"] = "♋", - ["leo"] = "♌", - ["virgo"] = "♍", - ["libra"] = "♎", - ["scorpius"] = "♏", - ["sagittarius"] = "♐", - ["capricorn"] = "♑", - ["aquarius"] = "♒", - ["pisces"] = "♓", - ["id"] = "🆔", - ["atom"] = "⚛️", - ["atom_symbol"] = "⚛️", - ["accept"] = "🉑", - ["radioactive"] = "☢️", - ["radioactive_sign"] = "☢️", - ["biohazard"] = "☣️", - ["biohazard_sign"] = "☣️", - ["mobile_phone_off"] = "📴", - ["vibration_mode"] = "📳", - ["u6709"] = "🈶", - ["u7121"] = "🈚", - ["u7533"] = "🈸", - ["u55b6"] = "🈺", - ["u6708"] = "🈷️", - ["eight_pointed_black_star"] = "✴️", - ["vs"] = "🆚", - ["white_flower"] = "💮", - ["ideograph_advantage"] = "🉐", - ["secret"] = "㊙️", - ["congratulations"] = "㊗️", - ["u5408"] = "🈴", - ["u6e80"] = "🈵", - ["u5272"] = "🈹", - ["u7981"] = "🈲", - ["a"] = "🅰️", - ["b"] = "🅱️", - ["ab"] = "🆎", - ["cl"] = "🆑", - ["o2"] = "🅾️", - ["sos"] = "🆘", - ["x"] = "❌", - ["o"] = "⭕", - ["octagonal_sign"] = "🛑", - ["stop_sign"] = "🛑", - ["no_entry"] = "⛔", - ["name_badge"] = "📛", - ["no_entry_sign"] = "🚫", - ["100"] = "💯", - ["anger"] = "💢", - ["hotsprings"] = "♨️", - ["no_pedestrians"] = "🚷", - ["do_not_litter"] = "🚯", - ["no_bicycles"] = "🚳", - ["non_potable_water"] = "🚱", - ["underage"] = "🔞", - ["no_mobile_phones"] = "📵", - ["no_smoking"] = "🚭", - ["exclamation"] = "❗", - ["grey_exclamation"] = "❕", - ["question"] = "❓", - ["grey_question"] = "❔", - ["bangbang"] = "‼️", - ["interrobang"] = "⁉️", - ["low_brightness"] = "🔅", - ["high_brightness"] = "🔆", - ["part_alternation_mark"] = "〽️", - ["warning"] = "⚠️", - ["children_crossing"] = "🚸", - ["trident"] = "🔱", - ["fleur_de_lis"] = "⚜️", - ["beginner"] = "🔰", - ["recycle"] = "♻️", - ["white_check_mark"] = "✅", - ["u6307"] = "🈯", - ["chart"] = "💹", - ["sparkle"] = "❇️", - ["eight_spoked_asterisk"] = "✳️", - ["negative_squared_cross_mark"] = "❎", - ["globe_with_meridians"] = "🌐", - ["diamond_shape_with_a_dot_inside"] = "💠", - ["m"] = "Ⓜ️", - ["cyclone"] = "🌀", - ["zzz"] = "💤", - ["atm"] = "🏧", - ["wc"] = "🚾", - ["wheelchair"] = "♿", - ["parking"] = "🅿️", - ["u7a7a"] = "🈳", - ["sa"] = "🈂️", - ["passport_control"] = "🛂", - ["customs"] = "🛃", - ["baggage_claim"] = "🛄", - ["left_luggage"] = "🛅", - ["elevator"] = "🛗", - ["mens"] = "🚹", - ["womens"] = "🚺", - ["baby_symbol"] = "🚼", - ["restroom"] = "🚻", - ["put_litter_in_its_place"] = "🚮", - ["cinema"] = "🎦", - ["signal_strength"] = "📶", - ["koko"] = "🈁", - ["symbols"] = "🔣", - ["information_source"] = "ℹ️", - ["abc"] = "🔤", - ["abcd"] = "🔡", - ["capital_abcd"] = "🔠", - ["ng"] = "🆖", - ["ok"] = "🆗", - ["up"] = "🆙", - ["cool"] = "🆒", - ["new"] = "🆕", - ["free"] = "🆓", - ["zero"] = "0️⃣", - ["one"] = "1️⃣", - ["two"] = "2️⃣", - ["three"] = "3️⃣", - ["four"] = "4️⃣", - ["five"] = "5️⃣", - ["six"] = "6️⃣", - ["seven"] = "7️⃣", - ["eight"] = "8️⃣", - ["nine"] = "9️⃣", - ["keycap_ten"] = "🔟", - ["1234"] = "🔢", - ["hash"] = "#️⃣", - ["asterisk"] = "*️⃣", - ["keycap_asterisk"] = "*️⃣", - ["eject"] = "⏏️", - ["eject_symbol"] = "⏏️", - ["arrow_forward"] = "▶️", - ["pause_button"] = "⏸️", - ["double_vertical_bar"] = "⏸️", - ["play_pause"] = "⏯️", - ["stop_button"] = "⏹️", - ["record_button"] = "⏺️", - ["track_next"] = "⏭️", - ["next_track"] = "⏭️", - ["track_previous"] = "⏮️", - ["previous_track"] = "⏮️", - ["fast_forward"] = "⏩", - ["rewind"] = "⏪", - ["arrow_double_up"] = "⏫", - ["arrow_double_down"] = "⏬", - ["arrow_backward"] = "◀️", - ["arrow_up_small"] = "🔼", - ["arrow_down_small"] = "🔽", - ["arrow_right"] = "➡️", - ["arrow_left"] = "⬅️", - ["arrow_up"] = "⬆️", - ["arrow_down"] = "⬇️", - ["arrow_upper_right"] = "↗️", - ["arrow_lower_right"] = "↘️", - ["arrow_lower_left"] = "↙️", - ["arrow_upper_left"] = "↖️", - ["arrow_up_down"] = "↕️", - ["left_right_arrow"] = "↔️", - ["arrow_right_hook"] = "↪️", - ["leftwards_arrow_with_hook"] = "↩️", - ["arrow_heading_up"] = "⤴️", - ["arrow_heading_down"] = "⤵️", - ["twisted_rightwards_arrows"] = "🔀", - ["repeat"] = "🔁", - ["repeat_one"] = "🔂", - ["arrows_counterclockwise"] = "🔄", - ["arrows_clockwise"] = "🔃", - ["musical_note"] = "🎵", - ["notes"] = "🎶", - ["heavy_plus_sign"] = "➕", - ["heavy_minus_sign"] = "➖", - ["heavy_division_sign"] = "➗", - ["heavy_multiplication_x"] = "✖️", - ["infinity"] = "♾️", - ["heavy_dollar_sign"] = "💲", - ["currency_exchange"] = "💱", - ["tm"] = "™️", - ["copyright"] = "©️", - ["registered"] = "®️", - ["wavy_dash"] = "〰️", - ["curly_loop"] = "➰", - ["loop"] = "➿", - ["end"] = "🔚", - ["back"] = "🔙", - ["on"] = "🔛", - ["top"] = "🔝", - ["soon"] = "🔜", - ["heavy_check_mark"] = "✔️", - ["ballot_box_with_check"] = "☑️", - ["radio_button"] = "🔘", - ["white_circle"] = "⚪", - ["black_circle"] = "⚫", - ["red_circle"] = "🔴", - ["blue_circle"] = "🔵", - ["brown_circle"] = "🟤", - ["purple_circle"] = "🟣", - ["green_circle"] = "🟢", - ["yellow_circle"] = "🟡", - ["orange_circle"] = "🟠", - ["small_red_triangle"] = "🔺", - ["small_red_triangle_down"] = "🔻", - ["small_orange_diamond"] = "🔸", - ["small_blue_diamond"] = "🔹", - ["large_orange_diamond"] = "🔶", - ["large_blue_diamond"] = "🔷", - ["white_square_button"] = "🔳", - ["black_square_button"] = "🔲", - ["black_small_square"] = "▪️", - ["white_small_square"] = "▫️", - ["black_medium_small_square"] = "◾", - ["white_medium_small_square"] = "◽", - ["black_medium_square"] = "◼️", - ["white_medium_square"] = "◻️", - ["black_large_square"] = "⬛", - ["white_large_square"] = "⬜", - ["orange_square"] = "🟧", - ["blue_square"] = "🟦", - ["red_square"] = "🟥", - ["brown_square"] = "🟫", - ["purple_square"] = "🟪", - ["green_square"] = "🟩", - ["yellow_square"] = "🟨", - ["speaker"] = "🔈", - ["mute"] = "🔇", - ["sound"] = "🔉", - ["loud_sound"] = "🔊", - ["bell"] = "🔔", - ["no_bell"] = "🔕", - ["mega"] = "📣", - ["loudspeaker"] = "📢", - ["speech_left"] = "🗨️", - ["left_speech_bubble"] = "🗨️", - ["eye_in_speech_bubble"] = "👁‍🗨", - ["speech_balloon"] = "💬", - ["thought_balloon"] = "💭", - ["anger_right"] = "🗯️", - ["right_anger_bubble"] = "🗯️", - ["spades"] = "♠️", - ["clubs"] = "♣️", - ["hearts"] = "♥️", - ["diamonds"] = "♦️", - ["black_joker"] = "🃏", - ["flower_playing_cards"] = "🎴", - ["mahjong"] = "🀄", - ["clock1"] = "🕐", - ["clock2"] = "🕑", - ["clock3"] = "🕒", - ["clock4"] = "🕓", - ["clock5"] = "🕔", - ["clock6"] = "🕕", - ["clock7"] = "🕖", - ["clock8"] = "🕗", - ["clock9"] = "🕘", - ["clock10"] = "🕙", - ["clock11"] = "🕚", - ["clock12"] = "🕛", - ["clock130"] = "🕜", - ["clock230"] = "🕝", - ["clock330"] = "🕞", - ["clock430"] = "🕟", - ["clock530"] = "🕠", - ["clock630"] = "🕡", - ["clock730"] = "🕢", - ["clock830"] = "🕣", - ["clock930"] = "🕤", - ["clock1030"] = "🕥", - ["clock1130"] = "🕦", - ["clock1230"] = "🕧", - ["female_sign"] = "♀️", - ["male_sign"] = "♂️", - ["transgender_symbol"] = "⚧", - ["medical_symbol"] = "⚕️", - ["regional_indicator_z"] = "🇿", - ["regional_indicator_y"] = "🇾", - ["regional_indicator_x"] = "🇽", - ["regional_indicator_w"] = "🇼", - ["regional_indicator_v"] = "🇻", - ["regional_indicator_u"] = "🇺", - ["regional_indicator_t"] = "🇹", - ["regional_indicator_s"] = "🇸", - ["regional_indicator_r"] = "🇷", - ["regional_indicator_q"] = "🇶", - ["regional_indicator_p"] = "🇵", - ["regional_indicator_o"] = "🇴", - ["regional_indicator_n"] = "🇳", - ["regional_indicator_m"] = "🇲", - ["regional_indicator_l"] = "🇱", - ["regional_indicator_k"] = "🇰", - ["regional_indicator_j"] = "🇯", - ["regional_indicator_i"] = "🇮", - ["regional_indicator_h"] = "🇭", - ["regional_indicator_g"] = "🇬", - ["regional_indicator_f"] = "🇫", - ["regional_indicator_e"] = "🇪", - ["regional_indicator_d"] = "🇩", - ["regional_indicator_c"] = "🇨", - ["regional_indicator_b"] = "🇧", - ["regional_indicator_a"] = "🇦", - ["flag_white"] = "🏳️", - ["flag_black"] = "🏴", - ["checkered_flag"] = "🏁", - ["triangular_flag_on_post"] = "🚩", - ["rainbow_flag"] = "🏳️‍🌈", - ["gay_pride_flag"] = "🏳️‍🌈", - ["transgender_flag"] = "🏳️‍⚧️", - ["pirate_flag"] = "🏴‍☠️", - ["flag_af"] = "🇦🇫", - ["flag_ax"] = "🇦🇽", - ["flag_al"] = "🇦🇱", - ["flag_dz"] = "🇩🇿", - ["flag_as"] = "🇦🇸", - ["flag_ad"] = "🇦🇩", - ["flag_ao"] = "🇦🇴", - ["flag_ai"] = "🇦🇮", - ["flag_aq"] = "🇦🇶", - ["flag_ag"] = "🇦🇬", - ["flag_ar"] = "🇦🇷", - ["flag_am"] = "🇦🇲", - ["flag_aw"] = "🇦🇼", - ["flag_au"] = "🇦🇺", - ["flag_at"] = "🇦🇹", - ["flag_az"] = "🇦🇿", - ["flag_bs"] = "🇧🇸", - ["flag_bh"] = "🇧🇭", - ["flag_bd"] = "🇧🇩", - ["flag_bb"] = "🇧🇧", - ["flag_by"] = "🇧🇾", - ["flag_be"] = "🇧🇪", - ["flag_bz"] = "🇧🇿", - ["flag_bj"] = "🇧🇯", - ["flag_bm"] = "🇧🇲", - ["flag_bt"] = "🇧🇹", - ["flag_bo"] = "🇧🇴", - ["flag_ba"] = "🇧🇦", - ["flag_bw"] = "🇧🇼", - ["flag_br"] = "🇧🇷", - ["flag_io"] = "🇮🇴", - ["flag_vg"] = "🇻🇬", - ["flag_bn"] = "🇧🇳", - ["flag_bg"] = "🇧🇬", - ["flag_bf"] = "🇧🇫", - ["flag_bi"] = "🇧🇮", - ["flag_kh"] = "🇰🇭", - ["flag_cm"] = "🇨🇲", - ["flag_ca"] = "🇨🇦", - ["flag_ic"] = "🇮🇨", - ["flag_cv"] = "🇨🇻", - ["flag_bq"] = "🇧🇶", - ["flag_ky"] = "🇰🇾", - ["flag_cf"] = "🇨🇫", - ["flag_td"] = "🇹🇩", - ["flag_cl"] = "🇨🇱", - ["flag_cn"] = "🇨🇳", - ["flag_cx"] = "🇨🇽", - ["flag_cc"] = "🇨🇨", - ["flag_co"] = "🇨🇴", - ["flag_km"] = "🇰🇲", - ["flag_cg"] = "🇨🇬", - ["flag_cd"] = "🇨🇩", - ["flag_ck"] = "🇨🇰", - ["flag_cr"] = "🇨🇷", - ["flag_ci"] = "🇨🇮", - ["flag_hr"] = "🇭🇷", - ["flag_cu"] = "🇨🇺", - ["flag_cw"] = "🇨🇼", - ["flag_cy"] = "🇨🇾", - ["flag_cz"] = "🇨🇿", - ["flag_dk"] = "🇩🇰", - ["flag_dj"] = "🇩🇯", - ["flag_dm"] = "🇩🇲", - ["flag_do"] = "🇩🇴", - ["flag_ec"] = "🇪🇨", - ["flag_eg"] = "🇪🇬", - ["flag_sv"] = "🇸🇻", - ["flag_gq"] = "🇬🇶", - ["flag_er"] = "🇪🇷", - ["flag_ee"] = "🇪🇪", - ["flag_et"] = "🇪🇹", - ["flag_eu"] = "🇪🇺", - ["flag_fk"] = "🇫🇰", - ["flag_fo"] = "🇫🇴", - ["flag_fj"] = "🇫🇯", - ["flag_fi"] = "🇫🇮", - ["flag_fr"] = "🇫🇷", - ["flag_gf"] = "🇬🇫", - ["flag_pf"] = "🇵🇫", - ["flag_tf"] = "🇹🇫", - ["flag_ga"] = "🇬🇦", - ["flag_gm"] = "🇬🇲", - ["flag_ge"] = "🇬🇪", - ["flag_de"] = "🇩🇪", - ["flag_gh"] = "🇬🇭", - ["flag_gi"] = "🇬🇮", - ["flag_gr"] = "🇬🇷", - ["flag_gl"] = "🇬🇱", - ["flag_gd"] = "🇬🇩", - ["flag_gp"] = "🇬🇵", - ["flag_gu"] = "🇬🇺", - ["flag_gt"] = "🇬🇹", - ["flag_gg"] = "🇬🇬", - ["flag_gn"] = "🇬🇳", - ["flag_gw"] = "🇬🇼", - ["flag_gy"] = "🇬🇾", - ["flag_ht"] = "🇭🇹", - ["flag_hn"] = "🇭🇳", - ["flag_hk"] = "🇭🇰", - ["flag_hu"] = "🇭🇺", - ["flag_is"] = "🇮🇸", - ["flag_in"] = "🇮🇳", - ["flag_id"] = "🇮🇩", - ["flag_ir"] = "🇮🇷", - ["flag_iq"] = "🇮🇶", - ["flag_ie"] = "🇮🇪", - ["flag_im"] = "🇮🇲", - ["flag_il"] = "🇮🇱", - ["flag_it"] = "🇮🇹", - ["flag_jm"] = "🇯🇲", - ["flag_jp"] = "🇯🇵", - ["crossed_flags"] = "🎌", - ["flag_je"] = "🇯🇪", - ["flag_jo"] = "🇯🇴", - ["flag_kz"] = "🇰🇿", - ["flag_ke"] = "🇰🇪", - ["flag_ki"] = "🇰🇮", - ["flag_xk"] = "🇽🇰", - ["flag_kw"] = "🇰🇼", - ["flag_kg"] = "🇰🇬", - ["flag_la"] = "🇱🇦", - ["flag_lv"] = "🇱🇻", - ["flag_lb"] = "🇱🇧", - ["flag_ls"] = "🇱🇸", - ["flag_lr"] = "🇱🇷", - ["flag_ly"] = "🇱🇾", - ["flag_li"] = "🇱🇮", - ["flag_lt"] = "🇱🇹", - ["flag_lu"] = "🇱🇺", - ["flag_mo"] = "🇲🇴", - ["flag_mk"] = "🇲🇰", - ["flag_mg"] = "🇲🇬", - ["flag_mw"] = "🇲🇼", - ["flag_my"] = "🇲🇾", - ["flag_mv"] = "🇲🇻", - ["flag_ml"] = "🇲🇱", - ["flag_mt"] = "🇲🇹", - ["flag_mh"] = "🇲🇭", - ["flag_mq"] = "🇲🇶", - ["flag_mr"] = "🇲🇷", - ["flag_mu"] = "🇲🇺", - ["flag_yt"] = "🇾🇹", - ["flag_mx"] = "🇲🇽", - ["flag_fm"] = "🇫🇲", - ["flag_md"] = "🇲🇩", - ["flag_mc"] = "🇲🇨", - ["flag_mn"] = "🇲🇳", - ["flag_me"] = "🇲🇪", - ["flag_ms"] = "🇲🇸", - ["flag_ma"] = "🇲🇦", - ["flag_mz"] = "🇲🇿", - ["flag_mm"] = "🇲🇲", - ["flag_na"] = "🇳🇦", - ["flag_nr"] = "🇳🇷", - ["flag_np"] = "🇳🇵", - ["flag_nl"] = "🇳🇱", - ["flag_nc"] = "🇳🇨", - ["flag_nz"] = "🇳🇿", - ["flag_ni"] = "🇳🇮", - ["flag_ne"] = "🇳🇪", - ["flag_ng"] = "🇳🇬", - ["flag_nu"] = "🇳🇺", - ["flag_nf"] = "🇳🇫", - ["flag_kp"] = "🇰🇵", - ["flag_mp"] = "🇲🇵", - ["flag_no"] = "🇳🇴", - ["flag_om"] = "🇴🇲", - ["flag_pk"] = "🇵🇰", - ["flag_pw"] = "🇵🇼", - ["flag_ps"] = "🇵🇸", - ["flag_pa"] = "🇵🇦", - ["flag_pg"] = "🇵🇬", - ["flag_py"] = "🇵🇾", - ["flag_pe"] = "🇵🇪", - ["flag_ph"] = "🇵🇭", - ["flag_pn"] = "🇵🇳", - ["flag_pl"] = "🇵🇱", - ["flag_pt"] = "🇵🇹", - ["flag_pr"] = "🇵🇷", - ["flag_qa"] = "🇶🇦", - ["flag_re"] = "🇷🇪", - ["flag_ro"] = "🇷🇴", - ["flag_ru"] = "🇷🇺", - ["flag_rw"] = "🇷🇼", - ["flag_ws"] = "🇼🇸", - ["flag_sm"] = "🇸🇲", - ["flag_st"] = "🇸🇹", - ["flag_sa"] = "🇸🇦", - ["flag_sn"] = "🇸🇳", - ["flag_rs"] = "🇷🇸", - ["flag_sc"] = "🇸🇨", - ["flag_sl"] = "🇸🇱", - ["flag_sg"] = "🇸🇬", - ["flag_sx"] = "🇸🇽", - ["flag_sk"] = "🇸🇰", - ["flag_si"] = "🇸🇮", - ["flag_gs"] = "🇬🇸", - ["flag_sb"] = "🇸🇧", - ["flag_so"] = "🇸🇴", - ["flag_za"] = "🇿🇦", - ["flag_kr"] = "🇰🇷", - ["flag_ss"] = "🇸🇸", - ["flag_es"] = "🇪🇸", - ["flag_lk"] = "🇱🇰", - ["flag_bl"] = "🇧🇱", - ["flag_sh"] = "🇸🇭", - ["flag_kn"] = "🇰🇳", - ["flag_lc"] = "🇱🇨", - ["flag_pm"] = "🇵🇲", - ["flag_vc"] = "🇻🇨", - ["flag_sd"] = "🇸🇩", - ["flag_sr"] = "🇸🇷", - ["flag_sz"] = "🇸🇿", - ["flag_se"] = "🇸🇪", - ["flag_ch"] = "🇨🇭", - ["flag_sy"] = "🇸🇾", - ["flag_tw"] = "🇹🇼", - ["flag_tj"] = "🇹🇯", - ["flag_tz"] = "🇹🇿", - ["flag_th"] = "🇹🇭", - ["flag_tl"] = "🇹🇱", - ["flag_tg"] = "🇹🇬", - ["flag_tk"] = "🇹🇰", - ["flag_to"] = "🇹🇴", - ["flag_tt"] = "🇹🇹", - ["flag_tn"] = "🇹🇳", - ["flag_tr"] = "🇹🇷", - ["flag_tm"] = "🇹🇲", - ["flag_tc"] = "🇹🇨", - ["flag_vi"] = "🇻🇮", - ["flag_tv"] = "🇹🇻", - ["flag_ug"] = "🇺🇬", - ["flag_ua"] = "🇺🇦", - ["flag_ae"] = "🇦🇪", - ["flag_gb"] = "🇬🇧", - ["england"] = "🏴󠁧󠁢󠁥󠁮󠁧󠁿", - ["scotland"] = "🏴󠁧󠁢󠁳󠁣󠁴󠁿", - ["wales"] = "🏴󠁧󠁢󠁷󠁬󠁳󠁿", - ["flag_us"] = "🇺🇸", - ["flag_uy"] = "🇺🇾", - ["flag_uz"] = "🇺🇿", - ["flag_vu"] = "🇻🇺", - ["flag_va"] = "🇻🇦", - ["flag_ve"] = "🇻🇪", - ["flag_vn"] = "🇻🇳", - ["flag_wf"] = "🇼🇫", - ["flag_eh"] = "🇪🇭", - ["flag_ye"] = "🇾🇪", - ["flag_zm"] = "🇿🇲", - ["flag_zw"] = "🇿🇼", - ["flag_ac"] = "🇦🇨", - ["flag_bv"] = "🇧🇻", - ["flag_cp"] = "🇨🇵", - ["flag_ea"] = "🇪🇦", - ["flag_dg"] = "🇩🇬", - ["flag_hm"] = "🇭🇲", - ["flag_mf"] = "🇲🇫", - ["flag_sj"] = "🇸🇯", - ["flag_ta"] = "🇹🇦", - ["flag_um"] = "🇺🇲", - ["united_nations"] = "🇺🇳" - }; + private static Dictionary _fromCodes = new(5000, StringComparer.Ordinal) + { + ["grinning"] = "😀", + ["smiley"] = "😃", + ["smile"] = "😄", + ["grin"] = "😁", + ["laughing"] = "😆", + ["satisfied"] = "😆", + ["sweat_smile"] = "😅", + ["joy"] = "😂", + ["rofl"] = "🤣", + ["rolling_on_the_floor_laughing"] = "🤣", + ["relaxed"] = "☺️", + ["blush"] = "😊", + ["innocent"] = "😇", + ["slight_smile"] = "🙂", + ["slightly_smiling_face"] = "🙂", + ["upside_down"] = "🙃", + ["upside_down_face"] = "🙃", + ["wink"] = "😉", + ["relieved"] = "😌", + ["smiling_face_with_tear"] = "🥲", + ["heart_eyes"] = "😍", + ["smiling_face_with_3_hearts"] = "🥰", + ["kissing_heart"] = "😘", + ["kissing"] = "😗", + ["kissing_smiling_eyes"] = "😙", + ["kissing_closed_eyes"] = "😚", + ["yum"] = "😋", + ["stuck_out_tongue"] = "😛", + ["stuck_out_tongue_closed_eyes"] = "😝", + ["stuck_out_tongue_winking_eye"] = "😜", + ["zany_face"] = "🤪", + ["face_with_raised_eyebrow"] = "🤨", + ["face_with_monocle"] = "🧐", + ["nerd"] = "🤓", + ["nerd_face"] = "🤓", + ["sunglasses"] = "😎", + ["star_struck"] = "🤩", + ["partying_face"] = "🥳", + ["smirk"] = "😏", + ["unamused"] = "😒", + ["disappointed"] = "😞", + ["pensive"] = "😔", + ["worried"] = "😟", + ["confused"] = "😕", + ["slight_frown"] = "🙁", + ["slightly_frowning_face"] = "🙁", + ["frowning2"] = "☹️", + ["white_frowning_face"] = "☹️", + ["persevere"] = "😣", + ["confounded"] = "😖", + ["tired_face"] = "😫", + ["weary"] = "😩", + ["pleading_face"] = "🥺", + ["cry"] = "😢", + ["sob"] = "😭", + ["triumph"] = "😤", + ["face_exhaling"] = "😮‍💨", + ["angry"] = "😠", + ["rage"] = "😡", + ["face_with_symbols_over_mouth"] = "🤬", + ["exploding_head"] = "🤯", + ["flushed"] = "😳", + ["face_in_clouds"] = "😶‍🌫️", + ["hot_face"] = "🥵", + ["cold_face"] = "🥶", + ["scream"] = "😱", + ["fearful"] = "😨", + ["cold_sweat"] = "😰", + ["disappointed_relieved"] = "😥", + ["sweat"] = "😓", + ["hugging"] = "🤗", + ["hugging_face"] = "🤗", + ["thinking"] = "🤔", + ["thinking_face"] = "🤔", + ["face_with_hand_over_mouth"] = "🤭", + ["yawning_face"] = "🥱", + ["shushing_face"] = "🤫", + ["lying_face"] = "🤥", + ["liar"] = "🤥", + ["no_mouth"] = "😶", + ["neutral_face"] = "😐", + ["expressionless"] = "😑", + ["grimacing"] = "😬", + ["rolling_eyes"] = "🙄", + ["face_with_rolling_eyes"] = "🙄", + ["hushed"] = "😯", + ["frowning"] = "😦", + ["anguished"] = "😧", + ["open_mouth"] = "😮", + ["astonished"] = "😲", + ["sleeping"] = "😴", + ["drooling_face"] = "🤤", + ["drool"] = "🤤", + ["sleepy"] = "😪", + ["dizzy_face"] = "😵", + ["face_with_spiral_eyes"] = "😵‍💫", + ["zipper_mouth"] = "🤐", + ["zipper_mouth_face"] = "🤐", + ["woozy_face"] = "🥴", + ["nauseated_face"] = "🤢", + ["sick"] = "🤢", + ["face_vomiting"] = "🤮", + ["sneezing_face"] = "🤧", + ["sneeze"] = "🤧", + ["mask"] = "😷", + ["thermometer_face"] = "🤒", + ["face_with_thermometer"] = "🤒", + ["head_bandage"] = "🤕", + ["face_with_head_bandage"] = "🤕", + ["money_mouth"] = "🤑", + ["money_mouth_face"] = "🤑", + ["cowboy"] = "🤠", + ["face_with_cowboy_hat"] = "🤠", + ["disguised_face"] = "🥸", + ["smiling_imp"] = "😈", + ["imp"] = "👿", + ["japanese_ogre"] = "👹", + ["japanese_goblin"] = "👺", + ["clown"] = "🤡", + ["clown_face"] = "🤡", + ["poop"] = "💩", + ["shit"] = "💩", + ["hankey"] = "💩", + ["poo"] = "💩", + ["ghost"] = "👻", + ["skull"] = "💀", + ["skeleton"] = "💀", + ["skull_crossbones"] = "☠️", + ["skull_and_crossbones"] = "☠️", + ["alien"] = "👽", + ["space_invader"] = "👾", + ["robot"] = "🤖", + ["robot_face"] = "🤖", + ["jack_o_lantern"] = "🎃", + ["smiley_cat"] = "😺", + ["smile_cat"] = "😸", + ["joy_cat"] = "😹", + ["heart_eyes_cat"] = "😻", + ["smirk_cat"] = "😼", + ["kissing_cat"] = "😽", + ["scream_cat"] = "🙀", + ["crying_cat_face"] = "😿", + ["pouting_cat"] = "😾", + ["palms_up_together"] = "🤲", + ["palms_up_together_tone1"] = "🤲🏻", + ["palms_up_together_light_skin_tone"] = "🤲🏻", + ["palms_up_together_tone2"] = "🤲🏼", + ["palms_up_together_medium_light_skin_tone"] = "🤲🏼", + ["palms_up_together_tone3"] = "🤲🏽", + ["palms_up_together_medium_skin_tone"] = "🤲🏽", + ["palms_up_together_tone4"] = "🤲🏾", + ["palms_up_together_medium_dark_skin_tone"] = "🤲🏾", + ["palms_up_together_tone5"] = "🤲🏿", + ["palms_up_together_dark_skin_tone"] = "🤲🏿", + ["open_hands"] = "👐", + ["open_hands_tone1"] = "👐🏻", + ["open_hands_tone2"] = "👐🏼", + ["open_hands_tone3"] = "👐🏽", + ["open_hands_tone4"] = "👐🏾", + ["open_hands_tone5"] = "👐🏿", + ["raised_hands"] = "🙌", + ["raised_hands_tone1"] = "🙌🏻", + ["raised_hands_tone2"] = "🙌🏼", + ["raised_hands_tone3"] = "🙌🏽", + ["raised_hands_tone4"] = "🙌🏾", + ["raised_hands_tone5"] = "🙌🏿", + ["clap"] = "👏", + ["clap_tone1"] = "👏🏻", + ["clap_tone2"] = "👏🏼", + ["clap_tone3"] = "👏🏽", + ["clap_tone4"] = "👏🏾", + ["clap_tone5"] = "👏🏿", + ["handshake"] = "🤝", + ["shaking_hands"] = "🤝", + ["thumbsup"] = "👍", + ["+1"] = "👍", + ["thumbup"] = "👍", + ["thumbsup_tone1"] = "👍🏻", + ["+1_tone1"] = "👍🏻", + ["thumbup_tone1"] = "👍🏻", + ["thumbsup_tone2"] = "👍🏼", + ["+1_tone2"] = "👍🏼", + ["thumbup_tone2"] = "👍🏼", + ["thumbsup_tone3"] = "👍🏽", + ["+1_tone3"] = "👍🏽", + ["thumbup_tone3"] = "👍🏽", + ["thumbsup_tone4"] = "👍🏾", + ["+1_tone4"] = "👍🏾", + ["thumbup_tone4"] = "👍🏾", + ["thumbsup_tone5"] = "👍🏿", + ["+1_tone5"] = "👍🏿", + ["thumbup_tone5"] = "👍🏿", + ["thumbsdown"] = "👎", + ["-1"] = "👎", + ["thumbdown"] = "👎", + ["thumbsdown_tone1"] = "👎🏻", + ["_1_tone1"] = "👎🏻", + ["thumbdown_tone1"] = "👎🏻", + ["thumbsdown_tone2"] = "👎🏼", + ["_1_tone2"] = "👎🏼", + ["thumbdown_tone2"] = "👎🏼", + ["thumbsdown_tone3"] = "👎🏽", + ["_1_tone3"] = "👎🏽", + ["thumbdown_tone3"] = "👎🏽", + ["thumbsdown_tone4"] = "👎🏾", + ["_1_tone4"] = "👎🏾", + ["thumbdown_tone4"] = "👎🏾", + ["thumbsdown_tone5"] = "👎🏿", + ["_1_tone5"] = "👎🏿", + ["thumbdown_tone5"] = "👎🏿", + ["punch"] = "👊", + ["punch_tone1"] = "👊🏻", + ["punch_tone2"] = "👊🏼", + ["punch_tone3"] = "👊🏽", + ["punch_tone4"] = "👊🏾", + ["punch_tone5"] = "👊🏿", + ["fist"] = "✊", + ["fist_tone1"] = "✊🏻", + ["fist_tone2"] = "✊🏼", + ["fist_tone3"] = "✊🏽", + ["fist_tone4"] = "✊🏾", + ["fist_tone5"] = "✊🏿", + ["left_facing_fist"] = "🤛", + ["left_fist"] = "🤛", + ["left_facing_fist_tone1"] = "🤛🏻", + ["left_fist_tone1"] = "🤛🏻", + ["left_facing_fist_tone2"] = "🤛🏼", + ["left_fist_tone2"] = "🤛🏼", + ["left_facing_fist_tone3"] = "🤛🏽", + ["left_fist_tone3"] = "🤛🏽", + ["left_facing_fist_tone4"] = "🤛🏾", + ["left_fist_tone4"] = "🤛🏾", + ["left_facing_fist_tone5"] = "🤛🏿", + ["left_fist_tone5"] = "🤛🏿", + ["right_facing_fist"] = "🤜", + ["right_fist"] = "🤜", + ["right_facing_fist_tone1"] = "🤜🏻", + ["right_fist_tone1"] = "🤜🏻", + ["right_facing_fist_tone2"] = "🤜🏼", + ["right_fist_tone2"] = "🤜🏼", + ["right_facing_fist_tone3"] = "🤜🏽", + ["right_fist_tone3"] = "🤜🏽", + ["right_facing_fist_tone4"] = "🤜🏾", + ["right_fist_tone4"] = "🤜🏾", + ["right_facing_fist_tone5"] = "🤜🏿", + ["right_fist_tone5"] = "🤜🏿", + ["fingers_crossed"] = "🤞", + ["hand_with_index_and_middle_finger_crossed"] = "🤞", + ["fingers_crossed_tone1"] = "🤞🏻", + ["hand_with_index_and_middle_fingers_crossed_tone1"] = "🤞🏻", + ["fingers_crossed_tone2"] = "🤞🏼", + ["hand_with_index_and_middle_fingers_crossed_tone2"] = "🤞🏼", + ["fingers_crossed_tone3"] = "🤞🏽", + ["hand_with_index_and_middle_fingers_crossed_tone3"] = "🤞🏽", + ["fingers_crossed_tone4"] = "🤞🏾", + ["hand_with_index_and_middle_fingers_crossed_tone4"] = "🤞🏾", + ["fingers_crossed_tone5"] = "🤞🏿", + ["hand_with_index_and_middle_fingers_crossed_tone5"] = "🤞🏿", + ["v"] = "✌️", + ["v_tone1"] = "✌🏻", + ["v_tone2"] = "✌🏼", + ["v_tone3"] = "✌🏽", + ["v_tone4"] = "✌🏾", + ["v_tone5"] = "✌🏿", + ["love_you_gesture"] = "🤟", + ["love_you_gesture_tone1"] = "🤟🏻", + ["love_you_gesture_light_skin_tone"] = "🤟🏻", + ["love_you_gesture_tone2"] = "🤟🏼", + ["love_you_gesture_medium_light_skin_tone"] = "🤟🏼", + ["love_you_gesture_tone3"] = "🤟🏽", + ["love_you_gesture_medium_skin_tone"] = "🤟🏽", + ["love_you_gesture_tone4"] = "🤟🏾", + ["love_you_gesture_medium_dark_skin_tone"] = "🤟🏾", + ["love_you_gesture_tone5"] = "🤟🏿", + ["love_you_gesture_dark_skin_tone"] = "🤟🏿", + ["metal"] = "🤘", + ["sign_of_the_horns"] = "🤘", + ["metal_tone1"] = "🤘🏻", + ["sign_of_the_horns_tone1"] = "🤘🏻", + ["metal_tone2"] = "🤘🏼", + ["sign_of_the_horns_tone2"] = "🤘🏼", + ["metal_tone3"] = "🤘🏽", + ["sign_of_the_horns_tone3"] = "🤘🏽", + ["metal_tone4"] = "🤘🏾", + ["sign_of_the_horns_tone4"] = "🤘🏾", + ["metal_tone5"] = "🤘🏿", + ["sign_of_the_horns_tone5"] = "🤘🏿", + ["ok_hand"] = "👌", + ["ok_hand_tone1"] = "👌🏻", + ["ok_hand_tone2"] = "👌🏼", + ["ok_hand_tone3"] = "👌🏽", + ["ok_hand_tone4"] = "👌🏾", + ["ok_hand_tone5"] = "👌🏿", + ["pinching_hand"] = "🤏", + ["pinching_hand_tone1"] = "🤏🏻", + ["pinching_hand_light_skin_tone"] = "🤏🏻", + ["pinching_hand_tone2"] = "🤏🏼", + ["pinching_hand_medium_light_skin_tone"] = "🤏🏼", + ["pinching_hand_tone3"] = "🤏🏽", + ["pinching_hand_medium_skin_tone"] = "🤏🏽", + ["pinching_hand_tone4"] = "🤏🏾", + ["pinching_hand_medium_dark_skin_tone"] = "🤏🏾", + ["pinching_hand_tone5"] = "🤏🏿", + ["pinching_hand_dark_skin_tone"] = "🤏🏿", + ["pinched_fingers"] = "🤌", + ["pinched_fingers_tone2"] = "🤌🏼", + ["pinched_fingers_medium_light_skin_tone"] = "🤌🏼", + ["pinched_fingers_tone1"] = "🤌🏻", + ["pinched_fingers_light_skin_tone"] = "🤌🏻", + ["pinched_fingers_tone3"] = "🤌🏽", + ["pinched_fingers_medium_skin_tone"] = "🤌🏽", + ["pinched_fingers_tone4"] = "🤌🏾", + ["pinched_fingers_medium_dark_skin_tone"] = "🤌🏾", + ["pinched_fingers_tone5"] = "🤌🏿", + ["pinched_fingers_dark_skin_tone"] = "🤌🏿", + ["point_left"] = "👈", + ["point_left_tone1"] = "👈🏻", + ["point_left_tone2"] = "👈🏼", + ["point_left_tone3"] = "👈🏽", + ["point_left_tone4"] = "👈🏾", + ["point_left_tone5"] = "👈🏿", + ["point_right"] = "👉", + ["point_right_tone1"] = "👉🏻", + ["point_right_tone2"] = "👉🏼", + ["point_right_tone3"] = "👉🏽", + ["point_right_tone4"] = "👉🏾", + ["point_right_tone5"] = "👉🏿", + ["point_up_2"] = "👆", + ["point_up_2_tone1"] = "👆🏻", + ["point_up_2_tone2"] = "👆🏼", + ["point_up_2_tone3"] = "👆🏽", + ["point_up_2_tone4"] = "👆🏾", + ["point_up_2_tone5"] = "👆🏿", + ["point_down"] = "👇", + ["point_down_tone1"] = "👇🏻", + ["point_down_tone2"] = "👇🏼", + ["point_down_tone3"] = "👇🏽", + ["point_down_tone4"] = "👇🏾", + ["point_down_tone5"] = "👇🏿", + ["point_up"] = "☝️", + ["point_up_tone1"] = "☝🏻", + ["point_up_tone2"] = "☝🏼", + ["point_up_tone3"] = "☝🏽", + ["point_up_tone4"] = "☝🏾", + ["point_up_tone5"] = "☝🏿", + ["raised_hand"] = "✋", + ["raised_hand_tone1"] = "✋🏻", + ["raised_hand_tone2"] = "✋🏼", + ["raised_hand_tone3"] = "✋🏽", + ["raised_hand_tone4"] = "✋🏾", + ["raised_hand_tone5"] = "✋🏿", + ["raised_back_of_hand"] = "🤚", + ["back_of_hand"] = "🤚", + ["raised_back_of_hand_tone1"] = "🤚🏻", + ["back_of_hand_tone1"] = "🤚🏻", + ["raised_back_of_hand_tone2"] = "🤚🏼", + ["back_of_hand_tone2"] = "🤚🏼", + ["raised_back_of_hand_tone3"] = "🤚🏽", + ["back_of_hand_tone3"] = "🤚🏽", + ["raised_back_of_hand_tone4"] = "🤚🏾", + ["back_of_hand_tone4"] = "🤚🏾", + ["raised_back_of_hand_tone5"] = "🤚🏿", + ["back_of_hand_tone5"] = "🤚🏿", + ["hand_splayed"] = "🖐️", + ["raised_hand_with_fingers_splayed"] = "🖐️", + ["hand_splayed_tone1"] = "🖐🏻", + ["raised_hand_with_fingers_splayed_tone1"] = "🖐🏻", + ["hand_splayed_tone2"] = "🖐🏼", + ["raised_hand_with_fingers_splayed_tone2"] = "🖐🏼", + ["hand_splayed_tone3"] = "🖐🏽", + ["raised_hand_with_fingers_splayed_tone3"] = "🖐🏽", + ["hand_splayed_tone4"] = "🖐🏾", + ["raised_hand_with_fingers_splayed_tone4"] = "🖐🏾", + ["hand_splayed_tone5"] = "🖐🏿", + ["raised_hand_with_fingers_splayed_tone5"] = "🖐🏿", + ["vulcan"] = "🖖", + ["raised_hand_with_part_between_middle_and_ring_fingers"] = "🖖", + ["vulcan_tone1"] = "🖖🏻", + ["raised_hand_with_part_between_middle_and_ring_fingers_tone1"] = "🖖🏻", + ["vulcan_tone2"] = "🖖🏼", + ["raised_hand_with_part_between_middle_and_ring_fingers_tone2"] = "🖖🏼", + ["vulcan_tone3"] = "🖖🏽", + ["raised_hand_with_part_between_middle_and_ring_fingers_tone3"] = "🖖🏽", + ["vulcan_tone4"] = "🖖🏾", + ["raised_hand_with_part_between_middle_and_ring_fingers_tone4"] = "🖖🏾", + ["vulcan_tone5"] = "🖖🏿", + ["raised_hand_with_part_between_middle_and_ring_fingers_tone5"] = "🖖🏿", + ["wave"] = "👋", + ["wave_tone1"] = "👋🏻", + ["wave_tone2"] = "👋🏼", + ["wave_tone3"] = "👋🏽", + ["wave_tone4"] = "👋🏾", + ["wave_tone5"] = "👋🏿", + ["call_me"] = "🤙", + ["call_me_hand"] = "🤙", + ["call_me_tone1"] = "🤙🏻", + ["call_me_hand_tone1"] = "🤙🏻", + ["call_me_tone2"] = "🤙🏼", + ["call_me_hand_tone2"] = "🤙🏼", + ["call_me_tone3"] = "🤙🏽", + ["call_me_hand_tone3"] = "🤙🏽", + ["call_me_tone4"] = "🤙🏾", + ["call_me_hand_tone4"] = "🤙🏾", + ["call_me_tone5"] = "🤙🏿", + ["call_me_hand_tone5"] = "🤙🏿", + ["muscle"] = "💪", + ["muscle_tone1"] = "💪🏻", + ["muscle_tone2"] = "💪🏼", + ["muscle_tone3"] = "💪🏽", + ["muscle_tone4"] = "💪🏾", + ["muscle_tone5"] = "💪🏿", + ["mechanical_arm"] = "🦾", + ["middle_finger"] = "🖕", + ["reversed_hand_with_middle_finger_extended"] = "🖕", + ["middle_finger_tone1"] = "🖕🏻", + ["reversed_hand_with_middle_finger_extended_tone1"] = "🖕🏻", + ["middle_finger_tone2"] = "🖕🏼", + ["reversed_hand_with_middle_finger_extended_tone2"] = "🖕🏼", + ["middle_finger_tone3"] = "🖕🏽", + ["reversed_hand_with_middle_finger_extended_tone3"] = "🖕🏽", + ["middle_finger_tone4"] = "🖕🏾", + ["reversed_hand_with_middle_finger_extended_tone4"] = "🖕🏾", + ["middle_finger_tone5"] = "🖕🏿", + ["reversed_hand_with_middle_finger_extended_tone5"] = "🖕🏿", + ["writing_hand"] = "✍️", + ["writing_hand_tone1"] = "✍🏻", + ["writing_hand_tone2"] = "✍🏼", + ["writing_hand_tone3"] = "✍🏽", + ["writing_hand_tone4"] = "✍🏾", + ["writing_hand_tone5"] = "✍🏿", + ["pray"] = "🙏", + ["pray_tone1"] = "🙏🏻", + ["pray_tone2"] = "🙏🏼", + ["pray_tone3"] = "🙏🏽", + ["pray_tone4"] = "🙏🏾", + ["pray_tone5"] = "🙏🏿", + ["foot"] = "🦶", + ["foot_tone1"] = "🦶🏻", + ["foot_light_skin_tone"] = "🦶🏻", + ["foot_tone2"] = "🦶🏼", + ["foot_medium_light_skin_tone"] = "🦶🏼", + ["foot_tone3"] = "🦶🏽", + ["foot_medium_skin_tone"] = "🦶🏽", + ["foot_tone4"] = "🦶🏾", + ["foot_medium_dark_skin_tone"] = "🦶🏾", + ["foot_tone5"] = "🦶🏿", + ["foot_dark_skin_tone"] = "🦶🏿", + ["leg"] = "🦵", + ["leg_tone1"] = "🦵🏻", + ["leg_light_skin_tone"] = "🦵🏻", + ["leg_tone2"] = "🦵🏼", + ["leg_medium_light_skin_tone"] = "🦵🏼", + ["leg_tone3"] = "🦵🏽", + ["leg_medium_skin_tone"] = "🦵🏽", + ["leg_tone4"] = "🦵🏾", + ["leg_medium_dark_skin_tone"] = "🦵🏾", + ["leg_tone5"] = "🦵🏿", + ["leg_dark_skin_tone"] = "🦵🏿", + ["mechanical_leg"] = "🦿", + ["lipstick"] = "💄", + ["kiss"] = "💋", + ["lips"] = "👄", + ["tooth"] = "🦷", + ["tongue"] = "👅", + ["ear"] = "👂", + ["ear_tone1"] = "👂🏻", + ["ear_tone2"] = "👂🏼", + ["ear_tone3"] = "👂🏽", + ["ear_tone4"] = "👂🏾", + ["ear_tone5"] = "👂🏿", + ["ear_with_hearing_aid"] = "🦻", + ["ear_with_hearing_aid_tone1"] = "🦻🏻", + ["ear_with_hearing_aid_light_skin_tone"] = "🦻🏻", + ["ear_with_hearing_aid_tone2"] = "🦻🏼", + ["ear_with_hearing_aid_medium_light_skin_tone"] = "🦻🏼", + ["ear_with_hearing_aid_tone3"] = "🦻🏽", + ["ear_with_hearing_aid_medium_skin_tone"] = "🦻🏽", + ["ear_with_hearing_aid_tone4"] = "🦻🏾", + ["ear_with_hearing_aid_medium_dark_skin_tone"] = "🦻🏾", + ["ear_with_hearing_aid_tone5"] = "🦻🏿", + ["ear_with_hearing_aid_dark_skin_tone"] = "🦻🏿", + ["nose"] = "👃", + ["nose_tone1"] = "👃🏻", + ["nose_tone2"] = "👃🏼", + ["nose_tone3"] = "👃🏽", + ["nose_tone4"] = "👃🏾", + ["nose_tone5"] = "👃🏿", + ["footprints"] = "👣", + ["eye"] = "👁️", + ["eyes"] = "👀", + ["brain"] = "🧠", + ["anatomical_heart"] = "🫀", + ["lungs"] = "🫁", + ["bone"] = "🦴", + ["speaking_head"] = "🗣️", + ["speaking_head_in_silhouette"] = "🗣️", + ["bust_in_silhouette"] = "👤", + ["busts_in_silhouette"] = "👥", + ["people_hugging"] = "🫂", + ["baby"] = "👶", + ["baby_tone1"] = "👶🏻", + ["baby_tone2"] = "👶🏼", + ["baby_tone3"] = "👶🏽", + ["baby_tone4"] = "👶🏾", + ["baby_tone5"] = "👶🏿", + ["girl"] = "👧", + ["girl_tone1"] = "👧🏻", + ["girl_tone2"] = "👧🏼", + ["girl_tone3"] = "👧🏽", + ["girl_tone4"] = "👧🏾", + ["girl_tone5"] = "👧🏿", + ["child"] = "🧒", + ["child_tone1"] = "🧒🏻", + ["child_light_skin_tone"] = "🧒🏻", + ["child_tone2"] = "🧒🏼", + ["child_medium_light_skin_tone"] = "🧒🏼", + ["child_tone3"] = "🧒🏽", + ["child_medium_skin_tone"] = "🧒🏽", + ["child_tone4"] = "🧒🏾", + ["child_medium_dark_skin_tone"] = "🧒🏾", + ["child_tone5"] = "🧒🏿", + ["child_dark_skin_tone"] = "🧒🏿", + ["boy"] = "👦", + ["boy_tone1"] = "👦🏻", + ["boy_tone2"] = "👦🏼", + ["boy_tone3"] = "👦🏽", + ["boy_tone4"] = "👦🏾", + ["boy_tone5"] = "👦🏿", + ["woman"] = "👩", + ["woman_tone1"] = "👩🏻", + ["woman_tone2"] = "👩🏼", + ["woman_tone3"] = "👩🏽", + ["woman_tone4"] = "👩🏾", + ["woman_tone5"] = "👩🏿", + ["adult"] = "🧑", + ["adult_tone1"] = "🧑🏻", + ["adult_light_skin_tone"] = "🧑🏻", + ["adult_tone2"] = "🧑🏼", + ["adult_medium_light_skin_tone"] = "🧑🏼", + ["adult_tone3"] = "🧑🏽", + ["adult_medium_skin_tone"] = "🧑🏽", + ["adult_tone4"] = "🧑🏾", + ["adult_medium_dark_skin_tone"] = "🧑🏾", + ["adult_tone5"] = "🧑🏿", + ["adult_dark_skin_tone"] = "🧑🏿", + ["man"] = "👨", + ["man_tone1"] = "👨🏻", + ["man_tone2"] = "👨🏼", + ["man_tone3"] = "👨🏽", + ["man_tone4"] = "👨🏾", + ["man_tone5"] = "👨🏿", + ["person_curly_hair"] = "🧑‍🦱", + ["person_tone1_curly_hair"] = "🧑🏻‍🦱", + ["person_light_skin_tone_curly_hair"] = "🧑🏻‍🦱", + ["person_tone2_curly_hair"] = "🧑🏼‍🦱", + ["person_medium_light_skin_tone_curly_hair"] = "🧑🏼‍🦱", + ["person_tone3_curly_hair"] = "🧑🏽‍🦱", + ["person_medium_skin_tone_curly_hair"] = "🧑🏽‍🦱", + ["person_tone4_curly_hair"] = "🧑🏾‍🦱", + ["person_medium_dark_skin_tone_curly_hair"] = "🧑🏾‍🦱", + ["person_tone5_curly_hair"] = "🧑🏿‍🦱", + ["person_dark_skin_tone_curly_hair"] = "🧑🏿‍🦱", + ["woman_curly_haired"] = "👩‍🦱", + ["woman_curly_haired_tone1"] = "👩🏻‍🦱", + ["woman_curly_haired_light_skin_tone"] = "👩🏻‍🦱", + ["woman_curly_haired_tone2"] = "👩🏼‍🦱", + ["woman_curly_haired_medium_light_skin_tone"] = "👩🏼‍🦱", + ["woman_curly_haired_tone3"] = "👩🏽‍🦱", + ["woman_curly_haired_medium_skin_tone"] = "👩🏽‍🦱", + ["woman_curly_haired_tone4"] = "👩🏾‍🦱", + ["woman_curly_haired_medium_dark_skin_tone"] = "👩🏾‍🦱", + ["woman_curly_haired_tone5"] = "👩🏿‍🦱", + ["woman_curly_haired_dark_skin_tone"] = "👩🏿‍🦱", + ["man_curly_haired"] = "👨‍🦱", + ["man_curly_haired_tone1"] = "👨🏻‍🦱", + ["man_curly_haired_light_skin_tone"] = "👨🏻‍🦱", + ["man_curly_haired_tone2"] = "👨🏼‍🦱", + ["man_curly_haired_medium_light_skin_tone"] = "👨🏼‍🦱", + ["man_curly_haired_tone3"] = "👨🏽‍🦱", + ["man_curly_haired_medium_skin_tone"] = "👨🏽‍🦱", + ["man_curly_haired_tone4"] = "👨🏾‍🦱", + ["man_curly_haired_medium_dark_skin_tone"] = "👨🏾‍🦱", + ["man_curly_haired_tone5"] = "👨🏿‍🦱", + ["man_curly_haired_dark_skin_tone"] = "👨🏿‍🦱", + ["person_red_hair"] = "🧑‍🦰", + ["person_tone1_red_hair"] = "🧑🏻‍🦰", + ["person_light_skin_tone_red_hair"] = "🧑🏻‍🦰", + ["person_tone2_red_hair"] = "🧑🏼‍🦰", + ["person_medium_light_skin_tone_red_hair"] = "🧑🏼‍🦰", + ["person_tone3_red_hair"] = "🧑🏽‍🦰", + ["person_medium_skin_tone_red_hair"] = "🧑🏽‍🦰", + ["person_tone4_red_hair"] = "🧑🏾‍🦰", + ["person_medium_dark_skin_tone_red_hair"] = "🧑🏾‍🦰", + ["person_tone5_red_hair"] = "🧑🏿‍🦰", + ["person_dark_skin_tone_red_hair"] = "🧑🏿‍🦰", + ["woman_red_haired"] = "👩‍🦰", + ["woman_red_haired_tone1"] = "👩🏻‍🦰", + ["woman_red_haired_light_skin_tone"] = "👩🏻‍🦰", + ["woman_red_haired_tone2"] = "👩🏼‍🦰", + ["woman_red_haired_medium_light_skin_tone"] = "👩🏼‍🦰", + ["woman_red_haired_tone3"] = "👩🏽‍🦰", + ["woman_red_haired_medium_skin_tone"] = "👩🏽‍🦰", + ["woman_red_haired_tone4"] = "👩🏾‍🦰", + ["woman_red_haired_medium_dark_skin_tone"] = "👩🏾‍🦰", + ["woman_red_haired_tone5"] = "👩🏿‍🦰", + ["woman_red_haired_dark_skin_tone"] = "👩🏿‍🦰", + ["man_red_haired"] = "👨‍🦰", + ["man_red_haired_tone1"] = "👨🏻‍🦰", + ["man_red_haired_light_skin_tone"] = "👨🏻‍🦰", + ["man_red_haired_tone2"] = "👨🏼‍🦰", + ["man_red_haired_medium_light_skin_tone"] = "👨🏼‍🦰", + ["man_red_haired_tone3"] = "👨🏽‍🦰", + ["man_red_haired_medium_skin_tone"] = "👨🏽‍🦰", + ["man_red_haired_tone4"] = "👨🏾‍🦰", + ["man_red_haired_medium_dark_skin_tone"] = "👨🏾‍🦰", + ["man_red_haired_tone5"] = "👨🏿‍🦰", + ["man_red_haired_dark_skin_tone"] = "👨🏿‍🦰", + ["blond_haired_woman"] = "👱‍♀️", + ["blond_haired_woman_tone1"] = "👱🏻‍♀️", + ["blond_haired_woman_light_skin_tone"] = "👱🏻‍♀️", + ["blond_haired_woman_tone2"] = "👱🏼‍♀️", + ["blond_haired_woman_medium_light_skin_tone"] = "👱🏼‍♀️", + ["blond_haired_woman_tone3"] = "👱🏽‍♀️", + ["blond_haired_woman_medium_skin_tone"] = "👱🏽‍♀️", + ["blond_haired_woman_tone4"] = "👱🏾‍♀️", + ["blond_haired_woman_medium_dark_skin_tone"] = "👱🏾‍♀️", + ["blond_haired_woman_tone5"] = "👱🏿‍♀️", + ["blond_haired_woman_dark_skin_tone"] = "👱🏿‍♀️", + ["blond_haired_person"] = "👱", + ["person_with_blond_hair"] = "👱", + ["blond_haired_person_tone1"] = "👱🏻", + ["person_with_blond_hair_tone1"] = "👱🏻", + ["blond_haired_person_tone2"] = "👱🏼", + ["person_with_blond_hair_tone2"] = "👱🏼", + ["blond_haired_person_tone3"] = "👱🏽", + ["person_with_blond_hair_tone3"] = "👱🏽", + ["blond_haired_person_tone4"] = "👱🏾", + ["person_with_blond_hair_tone4"] = "👱🏾", + ["blond_haired_person_tone5"] = "👱🏿", + ["person_with_blond_hair_tone5"] = "👱🏿", + ["blond_haired_man"] = "👱‍♂️", + ["blond_haired_man_tone1"] = "👱🏻‍♂️", + ["blond_haired_man_light_skin_tone"] = "👱🏻‍♂️", + ["blond_haired_man_tone2"] = "👱🏼‍♂️", + ["blond_haired_man_medium_light_skin_tone"] = "👱🏼‍♂️", + ["blond_haired_man_tone3"] = "👱🏽‍♂️", + ["blond_haired_man_medium_skin_tone"] = "👱🏽‍♂️", + ["blond_haired_man_tone4"] = "👱🏾‍♂️", + ["blond_haired_man_medium_dark_skin_tone"] = "👱🏾‍♂️", + ["blond_haired_man_tone5"] = "👱🏿‍♂️", + ["blond_haired_man_dark_skin_tone"] = "👱🏿‍♂️", + ["person_white_hair"] = "🧑‍🦳", + ["person_tone1_white_hair"] = "🧑🏻‍🦳", + ["person_light_skin_tone_white_hair"] = "🧑🏻‍🦳", + ["person_tone2_white_hair"] = "🧑🏼‍🦳", + ["person_medium_light_skin_tone_white_hair"] = "🧑🏼‍🦳", + ["person_tone3_white_hair"] = "🧑🏽‍🦳", + ["person_medium_skin_tone_white_hair"] = "🧑🏽‍🦳", + ["person_tone4_white_hair"] = "🧑🏾‍🦳", + ["person_medium_dark_skin_tone_white_hair"] = "🧑🏾‍🦳", + ["person_tone5_white_hair"] = "🧑🏿‍🦳", + ["person_dark_skin_tone_white_hair"] = "🧑🏿‍🦳", + ["woman_white_haired"] = "👩‍🦳", + ["woman_white_haired_tone1"] = "👩🏻‍🦳", + ["woman_white_haired_light_skin_tone"] = "👩🏻‍🦳", + ["woman_white_haired_tone2"] = "👩🏼‍🦳", + ["woman_white_haired_medium_light_skin_tone"] = "👩🏼‍🦳", + ["woman_white_haired_tone3"] = "👩🏽‍🦳", + ["woman_white_haired_medium_skin_tone"] = "👩🏽‍🦳", + ["woman_white_haired_tone4"] = "👩🏾‍🦳", + ["woman_white_haired_medium_dark_skin_tone"] = "👩🏾‍🦳", + ["woman_white_haired_tone5"] = "👩🏿‍🦳", + ["woman_white_haired_dark_skin_tone"] = "👩🏿‍🦳", + ["man_white_haired"] = "👨‍🦳", + ["man_white_haired_tone1"] = "👨🏻‍🦳", + ["man_white_haired_light_skin_tone"] = "👨🏻‍🦳", + ["man_white_haired_tone2"] = "👨🏼‍🦳", + ["man_white_haired_medium_light_skin_tone"] = "👨🏼‍🦳", + ["man_white_haired_tone3"] = "👨🏽‍🦳", + ["man_white_haired_medium_skin_tone"] = "👨🏽‍🦳", + ["man_white_haired_tone4"] = "👨🏾‍🦳", + ["man_white_haired_medium_dark_skin_tone"] = "👨🏾‍🦳", + ["man_white_haired_tone5"] = "👨🏿‍🦳", + ["man_white_haired_dark_skin_tone"] = "👨🏿‍🦳", + ["person_bald"] = "🧑‍🦲", + ["person_tone1_bald"] = "🧑🏻‍🦲", + ["person_light_skin_tone_bald"] = "🧑🏻‍🦲", + ["person_tone2_bald"] = "🧑🏼‍🦲", + ["person_medium_light_skin_tone_bald"] = "🧑🏼‍🦲", + ["person_tone3_bald"] = "🧑🏽‍🦲", + ["person_medium_skin_tone_bald"] = "🧑🏽‍🦲", + ["person_tone4_bald"] = "🧑🏾‍🦲", + ["person_medium_dark_skin_tone_bald"] = "🧑🏾‍🦲", + ["person_tone5_bald"] = "🧑🏿‍🦲", + ["person_dark_skin_tone_bald"] = "🧑🏿‍🦲", + ["woman_bald"] = "👩‍🦲", + ["woman_bald_tone1"] = "👩🏻‍🦲", + ["woman_bald_light_skin_tone"] = "👩🏻‍🦲", + ["woman_bald_tone2"] = "👩🏼‍🦲", + ["woman_bald_medium_light_skin_tone"] = "👩🏼‍🦲", + ["woman_bald_tone3"] = "👩🏽‍🦲", + ["woman_bald_medium_skin_tone"] = "👩🏽‍🦲", + ["woman_bald_tone4"] = "👩🏾‍🦲", + ["woman_bald_medium_dark_skin_tone"] = "👩🏾‍🦲", + ["woman_bald_tone5"] = "👩🏿‍🦲", + ["woman_bald_dark_skin_tone"] = "👩🏿‍🦲", + ["man_bald"] = "👨‍🦲", + ["man_bald_tone1"] = "👨🏻‍🦲", + ["man_bald_light_skin_tone"] = "👨🏻‍🦲", + ["man_bald_tone2"] = "👨🏼‍🦲", + ["man_bald_medium_light_skin_tone"] = "👨🏼‍🦲", + ["man_bald_tone3"] = "👨🏽‍🦲", + ["man_bald_medium_skin_tone"] = "👨🏽‍🦲", + ["man_bald_tone4"] = "👨🏾‍🦲", + ["man_bald_medium_dark_skin_tone"] = "👨🏾‍🦲", + ["man_bald_tone5"] = "👨🏿‍🦲", + ["man_bald_dark_skin_tone"] = "👨🏿‍🦲", + ["bearded_person"] = "🧔", + ["bearded_person_tone1"] = "🧔🏻", + ["bearded_person_light_skin_tone"] = "🧔🏻", + ["bearded_person_tone2"] = "🧔🏼", + ["bearded_person_medium_light_skin_tone"] = "🧔🏼", + ["bearded_person_tone3"] = "🧔🏽", + ["bearded_person_medium_skin_tone"] = "🧔🏽", + ["bearded_person_tone4"] = "🧔🏾", + ["bearded_person_medium_dark_skin_tone"] = "🧔🏾", + ["bearded_person_tone5"] = "🧔🏿", + ["bearded_person_dark_skin_tone"] = "🧔🏿", + ["man_beard"] = "🧔‍♂️", + ["man_tone1_beard"] = "🧔🏻‍♂️", + ["man_light_skin_tone_beard"] = "🧔🏻‍♂️", + ["man_tone2_beard"] = "🧔🏼‍♂️", + ["man_medium_light_skin_tone_beard"] = "🧔🏼‍♂️", + ["man_tone3_beard"] = "🧔🏽‍♂️", + ["man_medium_skin_tone_beard"] = "🧔🏽‍♂️", + ["man_tone4_beard"] = "🧔🏾‍♂️", + ["man_medium_dark_skin_tone_beard"] = "🧔🏾‍♂️", + ["man_tone5_beard"] = "🧔🏿‍♂️", + ["man_dark_skin_tone_beard"] = "🧔🏿‍♂️", + ["woman_beard"] = "🧔‍♀️", + ["woman_tone1_beard"] = "🧔🏻‍♀️", + ["woman_light_skin_tone_beard"] = "🧔🏻‍♀️", + ["woman_tone2_beard"] = "🧔🏼‍♀️", + ["woman_medium_light_skin_tone_beard"] = "🧔🏼‍♀️", + ["woman_tone3_beard"] = "🧔🏽‍♀️", + ["woman_medium_skin_tone_beard"] = "🧔🏽‍♀️", + ["woman_tone4_beard"] = "🧔🏾‍♀️", + ["woman_medium_dark_skin_tone_beard"] = "🧔🏾‍♀️", + ["woman_tone5_beard"] = "🧔🏿‍♀️", + ["woman_dark_skin_tone_beard"] = "🧔🏿‍♀️", + ["older_woman"] = "👵", + ["grandma"] = "👵", + ["older_woman_tone1"] = "👵🏻", + ["grandma_tone1"] = "👵🏻", + ["older_woman_tone2"] = "👵🏼", + ["grandma_tone2"] = "👵🏼", + ["older_woman_tone3"] = "👵🏽", + ["grandma_tone3"] = "👵🏽", + ["older_woman_tone4"] = "👵🏾", + ["grandma_tone4"] = "👵🏾", + ["older_woman_tone5"] = "👵🏿", + ["grandma_tone5"] = "👵🏿", + ["older_adult"] = "🧓", + ["older_adult_tone1"] = "🧓🏻", + ["older_adult_light_skin_tone"] = "🧓🏻", + ["older_adult_tone2"] = "🧓🏼", + ["older_adult_medium_light_skin_tone"] = "🧓🏼", + ["older_adult_tone3"] = "🧓🏽", + ["older_adult_medium_skin_tone"] = "🧓🏽", + ["older_adult_tone4"] = "🧓🏾", + ["older_adult_medium_dark_skin_tone"] = "🧓🏾", + ["older_adult_tone5"] = "🧓🏿", + ["older_adult_dark_skin_tone"] = "🧓🏿", + ["older_man"] = "👴", + ["older_man_tone1"] = "👴🏻", + ["older_man_tone2"] = "👴🏼", + ["older_man_tone3"] = "👴🏽", + ["older_man_tone4"] = "👴🏾", + ["older_man_tone5"] = "👴🏿", + ["man_with_chinese_cap"] = "👲", + ["man_with_gua_pi_mao"] = "👲", + ["man_with_chinese_cap_tone1"] = "👲🏻", + ["man_with_gua_pi_mao_tone1"] = "👲🏻", + ["man_with_chinese_cap_tone2"] = "👲🏼", + ["man_with_gua_pi_mao_tone2"] = "👲🏼", + ["man_with_chinese_cap_tone3"] = "👲🏽", + ["man_with_gua_pi_mao_tone3"] = "👲🏽", + ["man_with_chinese_cap_tone4"] = "👲🏾", + ["man_with_gua_pi_mao_tone4"] = "👲🏾", + ["man_with_chinese_cap_tone5"] = "👲🏿", + ["man_with_gua_pi_mao_tone5"] = "👲🏿", + ["person_wearing_turban"] = "👳", + ["man_with_turban"] = "👳", + ["person_wearing_turban_tone1"] = "👳🏻", + ["man_with_turban_tone1"] = "👳🏻", + ["person_wearing_turban_tone2"] = "👳🏼", + ["man_with_turban_tone2"] = "👳🏼", + ["person_wearing_turban_tone3"] = "👳🏽", + ["man_with_turban_tone3"] = "👳🏽", + ["person_wearing_turban_tone4"] = "👳🏾", + ["man_with_turban_tone4"] = "👳🏾", + ["person_wearing_turban_tone5"] = "👳🏿", + ["man_with_turban_tone5"] = "👳🏿", + ["woman_wearing_turban"] = "👳‍♀️", + ["woman_wearing_turban_tone1"] = "👳🏻‍♀️", + ["woman_wearing_turban_light_skin_tone"] = "👳🏻‍♀️", + ["woman_wearing_turban_tone2"] = "👳🏼‍♀️", + ["woman_wearing_turban_medium_light_skin_tone"] = "👳🏼‍♀️", + ["woman_wearing_turban_tone3"] = "👳🏽‍♀️", + ["woman_wearing_turban_medium_skin_tone"] = "👳🏽‍♀️", + ["woman_wearing_turban_tone4"] = "👳🏾‍♀️", + ["woman_wearing_turban_medium_dark_skin_tone"] = "👳🏾‍♀️", + ["woman_wearing_turban_tone5"] = "👳🏿‍♀️", + ["woman_wearing_turban_dark_skin_tone"] = "👳🏿‍♀️", + ["man_wearing_turban"] = "👳‍♂️", + ["man_wearing_turban_tone1"] = "👳🏻‍♂️", + ["man_wearing_turban_light_skin_tone"] = "👳🏻‍♂️", + ["man_wearing_turban_tone2"] = "👳🏼‍♂️", + ["man_wearing_turban_medium_light_skin_tone"] = "👳🏼‍♂️", + ["man_wearing_turban_tone3"] = "👳🏽‍♂️", + ["man_wearing_turban_medium_skin_tone"] = "👳🏽‍♂️", + ["man_wearing_turban_tone4"] = "👳🏾‍♂️", + ["man_wearing_turban_medium_dark_skin_tone"] = "👳🏾‍♂️", + ["man_wearing_turban_tone5"] = "👳🏿‍♂️", + ["man_wearing_turban_dark_skin_tone"] = "👳🏿‍♂️", + ["woman_with_headscarf"] = "🧕", + ["woman_with_headscarf_tone1"] = "🧕🏻", + ["woman_with_headscarf_light_skin_tone"] = "🧕🏻", + ["woman_with_headscarf_tone2"] = "🧕🏼", + ["woman_with_headscarf_medium_light_skin_tone"] = "🧕🏼", + ["woman_with_headscarf_tone3"] = "🧕🏽", + ["woman_with_headscarf_medium_skin_tone"] = "🧕🏽", + ["woman_with_headscarf_tone4"] = "🧕🏾", + ["woman_with_headscarf_medium_dark_skin_tone"] = "🧕🏾", + ["woman_with_headscarf_tone5"] = "🧕🏿", + ["woman_with_headscarf_dark_skin_tone"] = "🧕🏿", + ["police_officer"] = "👮", + ["cop"] = "👮", + ["police_officer_tone1"] = "👮🏻", + ["cop_tone1"] = "👮🏻", + ["police_officer_tone2"] = "👮🏼", + ["cop_tone2"] = "👮🏼", + ["police_officer_tone3"] = "👮🏽", + ["cop_tone3"] = "👮🏽", + ["police_officer_tone4"] = "👮🏾", + ["cop_tone4"] = "👮🏾", + ["police_officer_tone5"] = "👮🏿", + ["cop_tone5"] = "👮🏿", + ["woman_police_officer"] = "👮‍♀️", + ["woman_police_officer_tone1"] = "👮🏻‍♀️", + ["woman_police_officer_light_skin_tone"] = "👮🏻‍♀️", + ["woman_police_officer_tone2"] = "👮🏼‍♀️", + ["woman_police_officer_medium_light_skin_tone"] = "👮🏼‍♀️", + ["woman_police_officer_tone3"] = "👮🏽‍♀️", + ["woman_police_officer_medium_skin_tone"] = "👮🏽‍♀️", + ["woman_police_officer_tone4"] = "👮🏾‍♀️", + ["woman_police_officer_medium_dark_skin_tone"] = "👮🏾‍♀️", + ["woman_police_officer_tone5"] = "👮🏿‍♀️", + ["woman_police_officer_dark_skin_tone"] = "👮🏿‍♀️", + ["man_police_officer"] = "👮‍♂️", + ["man_police_officer_tone1"] = "👮🏻‍♂️", + ["man_police_officer_light_skin_tone"] = "👮🏻‍♂️", + ["man_police_officer_tone2"] = "👮🏼‍♂️", + ["man_police_officer_medium_light_skin_tone"] = "👮🏼‍♂️", + ["man_police_officer_tone3"] = "👮🏽‍♂️", + ["man_police_officer_medium_skin_tone"] = "👮🏽‍♂️", + ["man_police_officer_tone4"] = "👮🏾‍♂️", + ["man_police_officer_medium_dark_skin_tone"] = "👮🏾‍♂️", + ["man_police_officer_tone5"] = "👮🏿‍♂️", + ["man_police_officer_dark_skin_tone"] = "👮🏿‍♂️", + ["construction_worker"] = "👷", + ["construction_worker_tone1"] = "👷🏻", + ["construction_worker_tone2"] = "👷🏼", + ["construction_worker_tone3"] = "👷🏽", + ["construction_worker_tone4"] = "👷🏾", + ["construction_worker_tone5"] = "👷🏿", + ["woman_construction_worker"] = "👷‍♀️", + ["woman_construction_worker_tone1"] = "👷🏻‍♀️", + ["woman_construction_worker_light_skin_tone"] = "👷🏻‍♀️", + ["woman_construction_worker_tone2"] = "👷🏼‍♀️", + ["woman_construction_worker_medium_light_skin_tone"] = "👷🏼‍♀️", + ["woman_construction_worker_tone3"] = "👷🏽‍♀️", + ["woman_construction_worker_medium_skin_tone"] = "👷🏽‍♀️", + ["woman_construction_worker_tone4"] = "👷🏾‍♀️", + ["woman_construction_worker_medium_dark_skin_tone"] = "👷🏾‍♀️", + ["woman_construction_worker_tone5"] = "👷🏿‍♀️", + ["woman_construction_worker_dark_skin_tone"] = "👷🏿‍♀️", + ["man_construction_worker"] = "👷‍♂️", + ["man_construction_worker_tone1"] = "👷🏻‍♂️", + ["man_construction_worker_light_skin_tone"] = "👷🏻‍♂️", + ["man_construction_worker_tone2"] = "👷🏼‍♂️", + ["man_construction_worker_medium_light_skin_tone"] = "👷🏼‍♂️", + ["man_construction_worker_tone3"] = "👷🏽‍♂️", + ["man_construction_worker_medium_skin_tone"] = "👷🏽‍♂️", + ["man_construction_worker_tone4"] = "👷🏾‍♂️", + ["man_construction_worker_medium_dark_skin_tone"] = "👷🏾‍♂️", + ["man_construction_worker_tone5"] = "👷🏿‍♂️", + ["man_construction_worker_dark_skin_tone"] = "👷🏿‍♂️", + ["guard"] = "💂", + ["guardsman"] = "💂", + ["guard_tone1"] = "💂🏻", + ["guardsman_tone1"] = "💂🏻", + ["guard_tone2"] = "💂🏼", + ["guardsman_tone2"] = "💂🏼", + ["guard_tone3"] = "💂🏽", + ["guardsman_tone3"] = "💂🏽", + ["guard_tone4"] = "💂🏾", + ["guardsman_tone4"] = "💂🏾", + ["guard_tone5"] = "💂🏿", + ["guardsman_tone5"] = "💂🏿", + ["woman_guard"] = "💂‍♀️", + ["woman_guard_tone1"] = "💂🏻‍♀️", + ["woman_guard_light_skin_tone"] = "💂🏻‍♀️", + ["woman_guard_tone2"] = "💂🏼‍♀️", + ["woman_guard_medium_light_skin_tone"] = "💂🏼‍♀️", + ["woman_guard_tone3"] = "💂🏽‍♀️", + ["woman_guard_medium_skin_tone"] = "💂🏽‍♀️", + ["woman_guard_tone4"] = "💂🏾‍♀️", + ["woman_guard_medium_dark_skin_tone"] = "💂🏾‍♀️", + ["woman_guard_tone5"] = "💂🏿‍♀️", + ["woman_guard_dark_skin_tone"] = "💂🏿‍♀️", + ["man_guard"] = "💂‍♂️", + ["man_guard_tone1"] = "💂🏻‍♂️", + ["man_guard_light_skin_tone"] = "💂🏻‍♂️", + ["man_guard_tone2"] = "💂🏼‍♂️", + ["man_guard_medium_light_skin_tone"] = "💂🏼‍♂️", + ["man_guard_tone3"] = "💂🏽‍♂️", + ["man_guard_medium_skin_tone"] = "💂🏽‍♂️", + ["man_guard_tone4"] = "💂🏾‍♂️", + ["man_guard_medium_dark_skin_tone"] = "💂🏾‍♂️", + ["man_guard_tone5"] = "💂🏿‍♂️", + ["man_guard_dark_skin_tone"] = "💂🏿‍♂️", + ["detective"] = "🕵️", + ["spy"] = "🕵️", + ["sleuth_or_spy"] = "🕵️", + ["detective_tone1"] = "🕵🏻", + ["spy_tone1"] = "🕵🏻", + ["sleuth_or_spy_tone1"] = "🕵🏻", + ["detective_tone2"] = "🕵🏼", + ["spy_tone2"] = "🕵🏼", + ["sleuth_or_spy_tone2"] = "🕵🏼", + ["detective_tone3"] = "🕵🏽", + ["spy_tone3"] = "🕵🏽", + ["sleuth_or_spy_tone3"] = "🕵🏽", + ["detective_tone4"] = "🕵🏾", + ["spy_tone4"] = "🕵🏾", + ["sleuth_or_spy_tone4"] = "🕵🏾", + ["detective_tone5"] = "🕵🏿", + ["spy_tone5"] = "🕵🏿", + ["sleuth_or_spy_tone5"] = "🕵🏿", + ["woman_detective"] = "🕵️‍♀️", + ["woman_detective_tone1"] = "🕵🏻‍♀️", + ["woman_detective_light_skin_tone"] = "🕵🏻‍♀️", + ["woman_detective_tone2"] = "🕵🏼‍♀️", + ["woman_detective_medium_light_skin_tone"] = "🕵🏼‍♀️", + ["woman_detective_tone3"] = "🕵🏽‍♀️", + ["woman_detective_medium_skin_tone"] = "🕵🏽‍♀️", + ["woman_detective_tone4"] = "🕵🏾‍♀️", + ["woman_detective_medium_dark_skin_tone"] = "🕵🏾‍♀️", + ["woman_detective_tone5"] = "🕵🏿‍♀️", + ["woman_detective_dark_skin_tone"] = "🕵🏿‍♀️", + ["man_detective"] = "🕵️‍♂️", + ["man_detective_tone1"] = "🕵🏻‍♂️", + ["man_detective_light_skin_tone"] = "🕵🏻‍♂️", + ["man_detective_tone2"] = "🕵🏼‍♂️", + ["man_detective_medium_light_skin_tone"] = "🕵🏼‍♂️", + ["man_detective_tone3"] = "🕵🏽‍♂️", + ["man_detective_medium_skin_tone"] = "🕵🏽‍♂️", + ["man_detective_tone4"] = "🕵🏾‍♂️", + ["man_detective_medium_dark_skin_tone"] = "🕵🏾‍♂️", + ["man_detective_tone5"] = "🕵🏿‍♂️", + ["man_detective_dark_skin_tone"] = "🕵🏿‍♂️", + ["health_worker"] = "🧑‍⚕️", + ["health_worker_tone1"] = "🧑🏻‍⚕️", + ["health_worker_light_skin_tone"] = "🧑🏻‍⚕️", + ["health_worker_tone2"] = "🧑🏼‍⚕️", + ["health_worker_medium_light_skin_tone"] = "🧑🏼‍⚕️", + ["health_worker_tone3"] = "🧑🏽‍⚕️", + ["health_worker_medium_skin_tone"] = "🧑🏽‍⚕️", + ["health_worker_tone4"] = "🧑🏾‍⚕️", + ["health_worker_medium_dark_skin_tone"] = "🧑🏾‍⚕️", + ["health_worker_tone5"] = "🧑🏿‍⚕️", + ["health_worker_dark_skin_tone"] = "🧑🏿‍⚕️", + ["woman_health_worker"] = "👩‍⚕️", + ["woman_health_worker_tone1"] = "👩🏻‍⚕️", + ["woman_health_worker_light_skin_tone"] = "👩🏻‍⚕️", + ["woman_health_worker_tone2"] = "👩🏼‍⚕️", + ["woman_health_worker_medium_light_skin_tone"] = "👩🏼‍⚕️", + ["woman_health_worker_tone3"] = "👩🏽‍⚕️", + ["woman_health_worker_medium_skin_tone"] = "👩🏽‍⚕️", + ["woman_health_worker_tone4"] = "👩🏾‍⚕️", + ["woman_health_worker_medium_dark_skin_tone"] = "👩🏾‍⚕️", + ["woman_health_worker_tone5"] = "👩🏿‍⚕️", + ["woman_health_worker_dark_skin_tone"] = "👩🏿‍⚕️", + ["man_health_worker"] = "👨‍⚕️", + ["man_health_worker_tone1"] = "👨🏻‍⚕️", + ["man_health_worker_light_skin_tone"] = "👨🏻‍⚕️", + ["man_health_worker_tone2"] = "👨🏼‍⚕️", + ["man_health_worker_medium_light_skin_tone"] = "👨🏼‍⚕️", + ["man_health_worker_tone3"] = "👨🏽‍⚕️", + ["man_health_worker_medium_skin_tone"] = "👨🏽‍⚕️", + ["man_health_worker_tone4"] = "👨🏾‍⚕️", + ["man_health_worker_medium_dark_skin_tone"] = "👨🏾‍⚕️", + ["man_health_worker_tone5"] = "👨🏿‍⚕️", + ["man_health_worker_dark_skin_tone"] = "👨🏿‍⚕️", + ["farmer"] = "🧑‍🌾", + ["farmer_tone1"] = "🧑🏻‍🌾", + ["farmer_light_skin_tone"] = "🧑🏻‍🌾", + ["farmer_tone2"] = "🧑🏼‍🌾", + ["farmer_medium_light_skin_tone"] = "🧑🏼‍🌾", + ["farmer_tone3"] = "🧑🏽‍🌾", + ["farmer_medium_skin_tone"] = "🧑🏽‍🌾", + ["farmer_tone4"] = "🧑🏾‍🌾", + ["farmer_medium_dark_skin_tone"] = "🧑🏾‍🌾", + ["farmer_tone5"] = "🧑🏿‍🌾", + ["farmer_dark_skin_tone"] = "🧑🏿‍🌾", + ["woman_farmer"] = "👩‍🌾", + ["woman_farmer_tone1"] = "👩🏻‍🌾", + ["woman_farmer_light_skin_tone"] = "👩🏻‍🌾", + ["woman_farmer_tone2"] = "👩🏼‍🌾", + ["woman_farmer_medium_light_skin_tone"] = "👩🏼‍🌾", + ["woman_farmer_tone3"] = "👩🏽‍🌾", + ["woman_farmer_medium_skin_tone"] = "👩🏽‍🌾", + ["woman_farmer_tone4"] = "👩🏾‍🌾", + ["woman_farmer_medium_dark_skin_tone"] = "👩🏾‍🌾", + ["woman_farmer_tone5"] = "👩🏿‍🌾", + ["woman_farmer_dark_skin_tone"] = "👩🏿‍🌾", + ["man_farmer"] = "👨‍🌾", + ["man_farmer_tone1"] = "👨🏻‍🌾", + ["man_farmer_light_skin_tone"] = "👨🏻‍🌾", + ["man_farmer_tone2"] = "👨🏼‍🌾", + ["man_farmer_medium_light_skin_tone"] = "👨🏼‍🌾", + ["man_farmer_tone3"] = "👨🏽‍🌾", + ["man_farmer_medium_skin_tone"] = "👨🏽‍🌾", + ["man_farmer_tone4"] = "👨🏾‍🌾", + ["man_farmer_medium_dark_skin_tone"] = "👨🏾‍🌾", + ["man_farmer_tone5"] = "👨🏿‍🌾", + ["man_farmer_dark_skin_tone"] = "👨🏿‍🌾", + ["cook"] = "🧑‍🍳", + ["cook_tone1"] = "🧑🏻‍🍳", + ["cook_light_skin_tone"] = "🧑🏻‍🍳", + ["cook_tone2"] = "🧑🏼‍🍳", + ["cook_medium_light_skin_tone"] = "🧑🏼‍🍳", + ["cook_tone3"] = "🧑🏽‍🍳", + ["cook_medium_skin_tone"] = "🧑🏽‍🍳", + ["cook_tone4"] = "🧑🏾‍🍳", + ["cook_medium_dark_skin_tone"] = "🧑🏾‍🍳", + ["cook_tone5"] = "🧑🏿‍🍳", + ["cook_dark_skin_tone"] = "🧑🏿‍🍳", + ["woman_cook"] = "👩‍🍳", + ["woman_cook_tone1"] = "👩🏻‍🍳", + ["woman_cook_light_skin_tone"] = "👩🏻‍🍳", + ["woman_cook_tone2"] = "👩🏼‍🍳", + ["woman_cook_medium_light_skin_tone"] = "👩🏼‍🍳", + ["woman_cook_tone3"] = "👩🏽‍🍳", + ["woman_cook_medium_skin_tone"] = "👩🏽‍🍳", + ["woman_cook_tone4"] = "👩🏾‍🍳", + ["woman_cook_medium_dark_skin_tone"] = "👩🏾‍🍳", + ["woman_cook_tone5"] = "👩🏿‍🍳", + ["woman_cook_dark_skin_tone"] = "👩🏿‍🍳", + ["man_cook"] = "👨‍🍳", + ["man_cook_tone1"] = "👨🏻‍🍳", + ["man_cook_light_skin_tone"] = "👨🏻‍🍳", + ["man_cook_tone2"] = "👨🏼‍🍳", + ["man_cook_medium_light_skin_tone"] = "👨🏼‍🍳", + ["man_cook_tone3"] = "👨🏽‍🍳", + ["man_cook_medium_skin_tone"] = "👨🏽‍🍳", + ["man_cook_tone4"] = "👨🏾‍🍳", + ["man_cook_medium_dark_skin_tone"] = "👨🏾‍🍳", + ["man_cook_tone5"] = "👨🏿‍🍳", + ["man_cook_dark_skin_tone"] = "👨🏿‍🍳", + ["student"] = "🧑‍🎓", + ["student_tone1"] = "🧑🏻‍🎓", + ["student_light_skin_tone"] = "🧑🏻‍🎓", + ["student_tone2"] = "🧑🏼‍🎓", + ["student_medium_light_skin_tone"] = "🧑🏼‍🎓", + ["student_tone3"] = "🧑🏽‍🎓", + ["student_medium_skin_tone"] = "🧑🏽‍🎓", + ["student_tone4"] = "🧑🏾‍🎓", + ["student_medium_dark_skin_tone"] = "🧑🏾‍🎓", + ["student_tone5"] = "🧑🏿‍🎓", + ["student_dark_skin_tone"] = "🧑🏿‍🎓", + ["woman_student"] = "👩‍🎓", + ["woman_student_tone1"] = "👩🏻‍🎓", + ["woman_student_light_skin_tone"] = "👩🏻‍🎓", + ["woman_student_tone2"] = "👩🏼‍🎓", + ["woman_student_medium_light_skin_tone"] = "👩🏼‍🎓", + ["woman_student_tone3"] = "👩🏽‍🎓", + ["woman_student_medium_skin_tone"] = "👩🏽‍🎓", + ["woman_student_tone4"] = "👩🏾‍🎓", + ["woman_student_medium_dark_skin_tone"] = "👩🏾‍🎓", + ["woman_student_tone5"] = "👩🏿‍🎓", + ["woman_student_dark_skin_tone"] = "👩🏿‍🎓", + ["man_student"] = "👨‍🎓", + ["man_student_tone1"] = "👨🏻‍🎓", + ["man_student_light_skin_tone"] = "👨🏻‍🎓", + ["man_student_tone2"] = "👨🏼‍🎓", + ["man_student_medium_light_skin_tone"] = "👨🏼‍🎓", + ["man_student_tone3"] = "👨🏽‍🎓", + ["man_student_medium_skin_tone"] = "👨🏽‍🎓", + ["man_student_tone4"] = "👨🏾‍🎓", + ["man_student_medium_dark_skin_tone"] = "👨🏾‍🎓", + ["man_student_tone5"] = "👨🏿‍🎓", + ["man_student_dark_skin_tone"] = "👨🏿‍🎓", + ["singer"] = "🧑‍🎤", + ["singer_tone1"] = "🧑🏻‍🎤", + ["singer_light_skin_tone"] = "🧑🏻‍🎤", + ["singer_tone2"] = "🧑🏼‍🎤", + ["singer_medium_light_skin_tone"] = "🧑🏼‍🎤", + ["singer_tone3"] = "🧑🏽‍🎤", + ["singer_medium_skin_tone"] = "🧑🏽‍🎤", + ["singer_tone4"] = "🧑🏾‍🎤", + ["singer_medium_dark_skin_tone"] = "🧑🏾‍🎤", + ["singer_tone5"] = "🧑🏿‍🎤", + ["singer_dark_skin_tone"] = "🧑🏿‍🎤", + ["woman_singer"] = "👩‍🎤", + ["woman_singer_tone1"] = "👩🏻‍🎤", + ["woman_singer_light_skin_tone"] = "👩🏻‍🎤", + ["woman_singer_tone2"] = "👩🏼‍🎤", + ["woman_singer_medium_light_skin_tone"] = "👩🏼‍🎤", + ["woman_singer_tone3"] = "👩🏽‍🎤", + ["woman_singer_medium_skin_tone"] = "👩🏽‍🎤", + ["woman_singer_tone4"] = "👩🏾‍🎤", + ["woman_singer_medium_dark_skin_tone"] = "👩🏾‍🎤", + ["woman_singer_tone5"] = "👩🏿‍🎤", + ["woman_singer_dark_skin_tone"] = "👩🏿‍🎤", + ["man_singer"] = "👨‍🎤", + ["man_singer_tone1"] = "👨🏻‍🎤", + ["man_singer_light_skin_tone"] = "👨🏻‍🎤", + ["man_singer_tone2"] = "👨🏼‍🎤", + ["man_singer_medium_light_skin_tone"] = "👨🏼‍🎤", + ["man_singer_tone3"] = "👨🏽‍🎤", + ["man_singer_medium_skin_tone"] = "👨🏽‍🎤", + ["man_singer_tone4"] = "👨🏾‍🎤", + ["man_singer_medium_dark_skin_tone"] = "👨🏾‍🎤", + ["man_singer_tone5"] = "👨🏿‍🎤", + ["man_singer_dark_skin_tone"] = "👨🏿‍🎤", + ["teacher"] = "🧑‍🏫", + ["teacher_tone1"] = "🧑🏻‍🏫", + ["teacher_light_skin_tone"] = "🧑🏻‍🏫", + ["teacher_tone2"] = "🧑🏼‍🏫", + ["teacher_medium_light_skin_tone"] = "🧑🏼‍🏫", + ["teacher_tone3"] = "🧑🏽‍🏫", + ["teacher_medium_skin_tone"] = "🧑🏽‍🏫", + ["teacher_tone4"] = "🧑🏾‍🏫", + ["teacher_medium_dark_skin_tone"] = "🧑🏾‍🏫", + ["teacher_tone5"] = "🧑🏿‍🏫", + ["teacher_dark_skin_tone"] = "🧑🏿‍🏫", + ["woman_teacher"] = "👩‍🏫", + ["woman_teacher_tone1"] = "👩🏻‍🏫", + ["woman_teacher_light_skin_tone"] = "👩🏻‍🏫", + ["woman_teacher_tone2"] = "👩🏼‍🏫", + ["woman_teacher_medium_light_skin_tone"] = "👩🏼‍🏫", + ["woman_teacher_tone3"] = "👩🏽‍🏫", + ["woman_teacher_medium_skin_tone"] = "👩🏽‍🏫", + ["woman_teacher_tone4"] = "👩🏾‍🏫", + ["woman_teacher_medium_dark_skin_tone"] = "👩🏾‍🏫", + ["woman_teacher_tone5"] = "👩🏿‍🏫", + ["woman_teacher_dark_skin_tone"] = "👩🏿‍🏫", + ["man_teacher"] = "👨‍🏫", + ["man_teacher_tone1"] = "👨🏻‍🏫", + ["man_teacher_light_skin_tone"] = "👨🏻‍🏫", + ["man_teacher_tone2"] = "👨🏼‍🏫", + ["man_teacher_medium_light_skin_tone"] = "👨🏼‍🏫", + ["man_teacher_tone3"] = "👨🏽‍🏫", + ["man_teacher_medium_skin_tone"] = "👨🏽‍🏫", + ["man_teacher_tone4"] = "👨🏾‍🏫", + ["man_teacher_medium_dark_skin_tone"] = "👨🏾‍🏫", + ["man_teacher_tone5"] = "👨🏿‍🏫", + ["man_teacher_dark_skin_tone"] = "👨🏿‍🏫", + ["factory_worker"] = "🧑‍🏭", + ["factory_worker_tone1"] = "🧑🏻‍🏭", + ["factory_worker_light_skin_tone"] = "🧑🏻‍🏭", + ["factory_worker_tone2"] = "🧑🏼‍🏭", + ["factory_worker_medium_light_skin_tone"] = "🧑🏼‍🏭", + ["factory_worker_tone3"] = "🧑🏽‍🏭", + ["factory_worker_medium_skin_tone"] = "🧑🏽‍🏭", + ["factory_worker_tone4"] = "🧑🏾‍🏭", + ["factory_worker_medium_dark_skin_tone"] = "🧑🏾‍🏭", + ["factory_worker_tone5"] = "🧑🏿‍🏭", + ["factory_worker_dark_skin_tone"] = "🧑🏿‍🏭", + ["woman_factory_worker"] = "👩‍🏭", + ["woman_factory_worker_tone1"] = "👩🏻‍🏭", + ["woman_factory_worker_light_skin_tone"] = "👩🏻‍🏭", + ["woman_factory_worker_tone2"] = "👩🏼‍🏭", + ["woman_factory_worker_medium_light_skin_tone"] = "👩🏼‍🏭", + ["woman_factory_worker_tone3"] = "👩🏽‍🏭", + ["woman_factory_worker_medium_skin_tone"] = "👩🏽‍🏭", + ["woman_factory_worker_tone4"] = "👩🏾‍🏭", + ["woman_factory_worker_medium_dark_skin_tone"] = "👩🏾‍🏭", + ["woman_factory_worker_tone5"] = "👩🏿‍🏭", + ["woman_factory_worker_dark_skin_tone"] = "👩🏿‍🏭", + ["man_factory_worker"] = "👨‍🏭", + ["man_factory_worker_tone1"] = "👨🏻‍🏭", + ["man_factory_worker_light_skin_tone"] = "👨🏻‍🏭", + ["man_factory_worker_tone2"] = "👨🏼‍🏭", + ["man_factory_worker_medium_light_skin_tone"] = "👨🏼‍🏭", + ["man_factory_worker_tone3"] = "👨🏽‍🏭", + ["man_factory_worker_medium_skin_tone"] = "👨🏽‍🏭", + ["man_factory_worker_tone4"] = "👨🏾‍🏭", + ["man_factory_worker_medium_dark_skin_tone"] = "👨🏾‍🏭", + ["man_factory_worker_tone5"] = "👨🏿‍🏭", + ["man_factory_worker_dark_skin_tone"] = "👨🏿‍🏭", + ["technologist"] = "🧑‍💻", + ["technologist_tone1"] = "🧑🏻‍💻", + ["technologist_light_skin_tone"] = "🧑🏻‍💻", + ["technologist_tone2"] = "🧑🏼‍💻", + ["technologist_medium_light_skin_tone"] = "🧑🏼‍💻", + ["technologist_tone3"] = "🧑🏽‍💻", + ["technologist_medium_skin_tone"] = "🧑🏽‍💻", + ["technologist_tone4"] = "🧑🏾‍💻", + ["technologist_medium_dark_skin_tone"] = "🧑🏾‍💻", + ["technologist_tone5"] = "🧑🏿‍💻", + ["technologist_dark_skin_tone"] = "🧑🏿‍💻", + ["woman_technologist"] = "👩‍💻", + ["woman_technologist_tone1"] = "👩🏻‍💻", + ["woman_technologist_light_skin_tone"] = "👩🏻‍💻", + ["woman_technologist_tone2"] = "👩🏼‍💻", + ["woman_technologist_medium_light_skin_tone"] = "👩🏼‍💻", + ["woman_technologist_tone3"] = "👩🏽‍💻", + ["woman_technologist_medium_skin_tone"] = "👩🏽‍💻", + ["woman_technologist_tone4"] = "👩🏾‍💻", + ["woman_technologist_medium_dark_skin_tone"] = "👩🏾‍💻", + ["woman_technologist_tone5"] = "👩🏿‍💻", + ["woman_technologist_dark_skin_tone"] = "👩🏿‍💻", + ["man_technologist"] = "👨‍💻", + ["man_technologist_tone1"] = "👨🏻‍💻", + ["man_technologist_light_skin_tone"] = "👨🏻‍💻", + ["man_technologist_tone2"] = "👨🏼‍💻", + ["man_technologist_medium_light_skin_tone"] = "👨🏼‍💻", + ["man_technologist_tone3"] = "👨🏽‍💻", + ["man_technologist_medium_skin_tone"] = "👨🏽‍💻", + ["man_technologist_tone4"] = "👨🏾‍💻", + ["man_technologist_medium_dark_skin_tone"] = "👨🏾‍💻", + ["man_technologist_tone5"] = "👨🏿‍💻", + ["man_technologist_dark_skin_tone"] = "👨🏿‍💻", + ["office_worker"] = "🧑‍💼", + ["office_worker_tone1"] = "🧑🏻‍💼", + ["office_worker_light_skin_tone"] = "🧑🏻‍💼", + ["office_worker_tone2"] = "🧑🏼‍💼", + ["office_worker_medium_light_skin_tone"] = "🧑🏼‍💼", + ["office_worker_tone3"] = "🧑🏽‍💼", + ["office_worker_medium_skin_tone"] = "🧑🏽‍💼", + ["office_worker_tone4"] = "🧑🏾‍💼", + ["office_worker_medium_dark_skin_tone"] = "🧑🏾‍💼", + ["office_worker_tone5"] = "🧑🏿‍💼", + ["office_worker_dark_skin_tone"] = "🧑🏿‍💼", + ["woman_office_worker"] = "👩‍💼", + ["woman_office_worker_tone1"] = "👩🏻‍💼", + ["woman_office_worker_light_skin_tone"] = "👩🏻‍💼", + ["woman_office_worker_tone2"] = "👩🏼‍💼", + ["woman_office_worker_medium_light_skin_tone"] = "👩🏼‍💼", + ["woman_office_worker_tone3"] = "👩🏽‍💼", + ["woman_office_worker_medium_skin_tone"] = "👩🏽‍💼", + ["woman_office_worker_tone4"] = "👩🏾‍💼", + ["woman_office_worker_medium_dark_skin_tone"] = "👩🏾‍💼", + ["woman_office_worker_tone5"] = "👩🏿‍💼", + ["woman_office_worker_dark_skin_tone"] = "👩🏿‍💼", + ["man_office_worker"] = "👨‍💼", + ["man_office_worker_tone1"] = "👨🏻‍💼", + ["man_office_worker_light_skin_tone"] = "👨🏻‍💼", + ["man_office_worker_tone2"] = "👨🏼‍💼", + ["man_office_worker_medium_light_skin_tone"] = "👨🏼‍💼", + ["man_office_worker_tone3"] = "👨🏽‍💼", + ["man_office_worker_medium_skin_tone"] = "👨🏽‍💼", + ["man_office_worker_tone4"] = "👨🏾‍💼", + ["man_office_worker_medium_dark_skin_tone"] = "👨🏾‍💼", + ["man_office_worker_tone5"] = "👨🏿‍💼", + ["man_office_worker_dark_skin_tone"] = "👨🏿‍💼", + ["mechanic"] = "🧑‍🔧", + ["mechanic_tone1"] = "🧑🏻‍🔧", + ["mechanic_light_skin_tone"] = "🧑🏻‍🔧", + ["mechanic_tone2"] = "🧑🏼‍🔧", + ["mechanic_medium_light_skin_tone"] = "🧑🏼‍🔧", + ["mechanic_tone3"] = "🧑🏽‍🔧", + ["mechanic_medium_skin_tone"] = "🧑🏽‍🔧", + ["mechanic_tone4"] = "🧑🏾‍🔧", + ["mechanic_medium_dark_skin_tone"] = "🧑🏾‍🔧", + ["mechanic_tone5"] = "🧑🏿‍🔧", + ["mechanic_dark_skin_tone"] = "🧑🏿‍🔧", + ["woman_mechanic"] = "👩‍🔧", + ["woman_mechanic_tone1"] = "👩🏻‍🔧", + ["woman_mechanic_light_skin_tone"] = "👩🏻‍🔧", + ["woman_mechanic_tone2"] = "👩🏼‍🔧", + ["woman_mechanic_medium_light_skin_tone"] = "👩🏼‍🔧", + ["woman_mechanic_tone3"] = "👩🏽‍🔧", + ["woman_mechanic_medium_skin_tone"] = "👩🏽‍🔧", + ["woman_mechanic_tone4"] = "👩🏾‍🔧", + ["woman_mechanic_medium_dark_skin_tone"] = "👩🏾‍🔧", + ["woman_mechanic_tone5"] = "👩🏿‍🔧", + ["woman_mechanic_dark_skin_tone"] = "👩🏿‍🔧", + ["man_mechanic"] = "👨‍🔧", + ["man_mechanic_tone1"] = "👨🏻‍🔧", + ["man_mechanic_light_skin_tone"] = "👨🏻‍🔧", + ["man_mechanic_tone2"] = "👨🏼‍🔧", + ["man_mechanic_medium_light_skin_tone"] = "👨🏼‍🔧", + ["man_mechanic_tone3"] = "👨🏽‍🔧", + ["man_mechanic_medium_skin_tone"] = "👨🏽‍🔧", + ["man_mechanic_tone4"] = "👨🏾‍🔧", + ["man_mechanic_medium_dark_skin_tone"] = "👨🏾‍🔧", + ["man_mechanic_tone5"] = "👨🏿‍🔧", + ["man_mechanic_dark_skin_tone"] = "👨🏿‍🔧", + ["scientist"] = "🧑‍🔬", + ["scientist_tone1"] = "🧑🏻‍🔬", + ["scientist_light_skin_tone"] = "🧑🏻‍🔬", + ["scientist_tone2"] = "🧑🏼‍🔬", + ["scientist_medium_light_skin_tone"] = "🧑🏼‍🔬", + ["scientist_tone3"] = "🧑🏽‍🔬", + ["scientist_medium_skin_tone"] = "🧑🏽‍🔬", + ["scientist_tone4"] = "🧑🏾‍🔬", + ["scientist_medium_dark_skin_tone"] = "🧑🏾‍🔬", + ["scientist_tone5"] = "🧑🏿‍🔬", + ["scientist_dark_skin_tone"] = "🧑🏿‍🔬", + ["woman_scientist"] = "👩‍🔬", + ["woman_scientist_tone1"] = "👩🏻‍🔬", + ["woman_scientist_light_skin_tone"] = "👩🏻‍🔬", + ["woman_scientist_tone2"] = "👩🏼‍🔬", + ["woman_scientist_medium_light_skin_tone"] = "👩🏼‍🔬", + ["woman_scientist_tone3"] = "👩🏽‍🔬", + ["woman_scientist_medium_skin_tone"] = "👩🏽‍🔬", + ["woman_scientist_tone4"] = "👩🏾‍🔬", + ["woman_scientist_medium_dark_skin_tone"] = "👩🏾‍🔬", + ["woman_scientist_tone5"] = "👩🏿‍🔬", + ["woman_scientist_dark_skin_tone"] = "👩🏿‍🔬", + ["man_scientist"] = "👨‍🔬", + ["man_scientist_tone1"] = "👨🏻‍🔬", + ["man_scientist_light_skin_tone"] = "👨🏻‍🔬", + ["man_scientist_tone2"] = "👨🏼‍🔬", + ["man_scientist_medium_light_skin_tone"] = "👨🏼‍🔬", + ["man_scientist_tone3"] = "👨🏽‍🔬", + ["man_scientist_medium_skin_tone"] = "👨🏽‍🔬", + ["man_scientist_tone4"] = "👨🏾‍🔬", + ["man_scientist_medium_dark_skin_tone"] = "👨🏾‍🔬", + ["man_scientist_tone5"] = "👨🏿‍🔬", + ["man_scientist_dark_skin_tone"] = "👨🏿‍🔬", + ["artist"] = "🧑‍🎨", + ["artist_tone1"] = "🧑🏻‍🎨", + ["artist_light_skin_tone"] = "🧑🏻‍🎨", + ["artist_tone2"] = "🧑🏼‍🎨", + ["artist_medium_light_skin_tone"] = "🧑🏼‍🎨", + ["artist_tone3"] = "🧑🏽‍🎨", + ["artist_medium_skin_tone"] = "🧑🏽‍🎨", + ["artist_tone4"] = "🧑🏾‍🎨", + ["artist_medium_dark_skin_tone"] = "🧑🏾‍🎨", + ["artist_tone5"] = "🧑🏿‍🎨", + ["artist_dark_skin_tone"] = "🧑🏿‍🎨", + ["woman_artist"] = "👩‍🎨", + ["woman_artist_tone1"] = "👩🏻‍🎨", + ["woman_artist_light_skin_tone"] = "👩🏻‍🎨", + ["woman_artist_tone2"] = "👩🏼‍🎨", + ["woman_artist_medium_light_skin_tone"] = "👩🏼‍🎨", + ["woman_artist_tone3"] = "👩🏽‍🎨", + ["woman_artist_medium_skin_tone"] = "👩🏽‍🎨", + ["woman_artist_tone4"] = "👩🏾‍🎨", + ["woman_artist_medium_dark_skin_tone"] = "👩🏾‍🎨", + ["woman_artist_tone5"] = "👩🏿‍🎨", + ["woman_artist_dark_skin_tone"] = "👩🏿‍🎨", + ["man_artist"] = "👨‍🎨", + ["man_artist_tone1"] = "👨🏻‍🎨", + ["man_artist_light_skin_tone"] = "👨🏻‍🎨", + ["man_artist_tone2"] = "👨🏼‍🎨", + ["man_artist_medium_light_skin_tone"] = "👨🏼‍🎨", + ["man_artist_tone3"] = "👨🏽‍🎨", + ["man_artist_medium_skin_tone"] = "👨🏽‍🎨", + ["man_artist_tone4"] = "👨🏾‍🎨", + ["man_artist_medium_dark_skin_tone"] = "👨🏾‍🎨", + ["man_artist_tone5"] = "👨🏿‍🎨", + ["man_artist_dark_skin_tone"] = "👨🏿‍🎨", + ["firefighter"] = "🧑‍🚒", + ["firefighter_tone1"] = "🧑🏻‍🚒", + ["firefighter_light_skin_tone"] = "🧑🏻‍🚒", + ["firefighter_tone2"] = "🧑🏼‍🚒", + ["firefighter_medium_light_skin_tone"] = "🧑🏼‍🚒", + ["firefighter_tone3"] = "🧑🏽‍🚒", + ["firefighter_medium_skin_tone"] = "🧑🏽‍🚒", + ["firefighter_tone4"] = "🧑🏾‍🚒", + ["firefighter_medium_dark_skin_tone"] = "🧑🏾‍🚒", + ["firefighter_tone5"] = "🧑🏿‍🚒", + ["firefighter_dark_skin_tone"] = "🧑🏿‍🚒", + ["woman_firefighter"] = "👩‍🚒", + ["woman_firefighter_tone1"] = "👩🏻‍🚒", + ["woman_firefighter_light_skin_tone"] = "👩🏻‍🚒", + ["woman_firefighter_tone2"] = "👩🏼‍🚒", + ["woman_firefighter_medium_light_skin_tone"] = "👩🏼‍🚒", + ["woman_firefighter_tone3"] = "👩🏽‍🚒", + ["woman_firefighter_medium_skin_tone"] = "👩🏽‍🚒", + ["woman_firefighter_tone4"] = "👩🏾‍🚒", + ["woman_firefighter_medium_dark_skin_tone"] = "👩🏾‍🚒", + ["woman_firefighter_tone5"] = "👩🏿‍🚒", + ["woman_firefighter_dark_skin_tone"] = "👩🏿‍🚒", + ["man_firefighter"] = "👨‍🚒", + ["man_firefighter_tone1"] = "👨🏻‍🚒", + ["man_firefighter_light_skin_tone"] = "👨🏻‍🚒", + ["man_firefighter_tone2"] = "👨🏼‍🚒", + ["man_firefighter_medium_light_skin_tone"] = "👨🏼‍🚒", + ["man_firefighter_tone3"] = "👨🏽‍🚒", + ["man_firefighter_medium_skin_tone"] = "👨🏽‍🚒", + ["man_firefighter_tone4"] = "👨🏾‍🚒", + ["man_firefighter_medium_dark_skin_tone"] = "👨🏾‍🚒", + ["man_firefighter_tone5"] = "👨🏿‍🚒", + ["man_firefighter_dark_skin_tone"] = "👨🏿‍🚒", + ["pilot"] = "🧑‍✈️", + ["pilot_tone1"] = "🧑🏻‍✈️", + ["pilot_light_skin_tone"] = "🧑🏻‍✈️", + ["pilot_tone2"] = "🧑🏼‍✈️", + ["pilot_medium_light_skin_tone"] = "🧑🏼‍✈️", + ["pilot_tone3"] = "🧑🏽‍✈️", + ["pilot_medium_skin_tone"] = "🧑🏽‍✈️", + ["pilot_tone4"] = "🧑🏾‍✈️", + ["pilot_medium_dark_skin_tone"] = "🧑🏾‍✈️", + ["pilot_tone5"] = "🧑🏿‍✈️", + ["pilot_dark_skin_tone"] = "🧑🏿‍✈️", + ["woman_pilot"] = "👩‍✈️", + ["woman_pilot_tone1"] = "👩🏻‍✈️", + ["woman_pilot_light_skin_tone"] = "👩🏻‍✈️", + ["woman_pilot_tone2"] = "👩🏼‍✈️", + ["woman_pilot_medium_light_skin_tone"] = "👩🏼‍✈️", + ["woman_pilot_tone3"] = "👩🏽‍✈️", + ["woman_pilot_medium_skin_tone"] = "👩🏽‍✈️", + ["woman_pilot_tone4"] = "👩🏾‍✈️", + ["woman_pilot_medium_dark_skin_tone"] = "👩🏾‍✈️", + ["woman_pilot_tone5"] = "👩🏿‍✈️", + ["woman_pilot_dark_skin_tone"] = "👩🏿‍✈️", + ["man_pilot"] = "👨‍✈️", + ["man_pilot_tone1"] = "👨🏻‍✈️", + ["man_pilot_light_skin_tone"] = "👨🏻‍✈️", + ["man_pilot_tone2"] = "👨🏼‍✈️", + ["man_pilot_medium_light_skin_tone"] = "👨🏼‍✈️", + ["man_pilot_tone3"] = "👨🏽‍✈️", + ["man_pilot_medium_skin_tone"] = "👨🏽‍✈️", + ["man_pilot_tone4"] = "👨🏾‍✈️", + ["man_pilot_medium_dark_skin_tone"] = "👨🏾‍✈️", + ["man_pilot_tone5"] = "👨🏿‍✈️", + ["man_pilot_dark_skin_tone"] = "👨🏿‍✈️", + ["astronaut"] = "🧑‍🚀", + ["astronaut_tone1"] = "🧑🏻‍🚀", + ["astronaut_light_skin_tone"] = "🧑🏻‍🚀", + ["astronaut_tone2"] = "🧑🏼‍🚀", + ["astronaut_medium_light_skin_tone"] = "🧑🏼‍🚀", + ["astronaut_tone3"] = "🧑🏽‍🚀", + ["astronaut_medium_skin_tone"] = "🧑🏽‍🚀", + ["astronaut_tone4"] = "🧑🏾‍🚀", + ["astronaut_medium_dark_skin_tone"] = "🧑🏾‍🚀", + ["astronaut_tone5"] = "🧑🏿‍🚀", + ["astronaut_dark_skin_tone"] = "🧑🏿‍🚀", + ["woman_astronaut"] = "👩‍🚀", + ["woman_astronaut_tone1"] = "👩🏻‍🚀", + ["woman_astronaut_light_skin_tone"] = "👩🏻‍🚀", + ["woman_astronaut_tone2"] = "👩🏼‍🚀", + ["woman_astronaut_medium_light_skin_tone"] = "👩🏼‍🚀", + ["woman_astronaut_tone3"] = "👩🏽‍🚀", + ["woman_astronaut_medium_skin_tone"] = "👩🏽‍🚀", + ["woman_astronaut_tone4"] = "👩🏾‍🚀", + ["woman_astronaut_medium_dark_skin_tone"] = "👩🏾‍🚀", + ["woman_astronaut_tone5"] = "👩🏿‍🚀", + ["woman_astronaut_dark_skin_tone"] = "👩🏿‍🚀", + ["man_astronaut"] = "👨‍🚀", + ["man_astronaut_tone1"] = "👨🏻‍🚀", + ["man_astronaut_light_skin_tone"] = "👨🏻‍🚀", + ["man_astronaut_tone2"] = "👨🏼‍🚀", + ["man_astronaut_medium_light_skin_tone"] = "👨🏼‍🚀", + ["man_astronaut_tone3"] = "👨🏽‍🚀", + ["man_astronaut_medium_skin_tone"] = "👨🏽‍🚀", + ["man_astronaut_tone4"] = "👨🏾‍🚀", + ["man_astronaut_medium_dark_skin_tone"] = "👨🏾‍🚀", + ["man_astronaut_tone5"] = "👨🏿‍🚀", + ["man_astronaut_dark_skin_tone"] = "👨🏿‍🚀", + ["judge"] = "🧑‍⚖️", + ["judge_tone1"] = "🧑🏻‍⚖️", + ["judge_light_skin_tone"] = "🧑🏻‍⚖️", + ["judge_tone2"] = "🧑🏼‍⚖️", + ["judge_medium_light_skin_tone"] = "🧑🏼‍⚖️", + ["judge_tone3"] = "🧑🏽‍⚖️", + ["judge_medium_skin_tone"] = "🧑🏽‍⚖️", + ["judge_tone4"] = "🧑🏾‍⚖️", + ["judge_medium_dark_skin_tone"] = "🧑🏾‍⚖️", + ["judge_tone5"] = "🧑🏿‍⚖️", + ["judge_dark_skin_tone"] = "🧑🏿‍⚖️", + ["woman_judge"] = "👩‍⚖️", + ["woman_judge_tone1"] = "👩🏻‍⚖️", + ["woman_judge_light_skin_tone"] = "👩🏻‍⚖️", + ["woman_judge_tone2"] = "👩🏼‍⚖️", + ["woman_judge_medium_light_skin_tone"] = "👩🏼‍⚖️", + ["woman_judge_tone3"] = "👩🏽‍⚖️", + ["woman_judge_medium_skin_tone"] = "👩🏽‍⚖️", + ["woman_judge_tone4"] = "👩🏾‍⚖️", + ["woman_judge_medium_dark_skin_tone"] = "👩🏾‍⚖️", + ["woman_judge_tone5"] = "👩🏿‍⚖️", + ["woman_judge_dark_skin_tone"] = "👩🏿‍⚖️", + ["man_judge"] = "👨‍⚖️", + ["man_judge_tone1"] = "👨🏻‍⚖️", + ["man_judge_light_skin_tone"] = "👨🏻‍⚖️", + ["man_judge_tone2"] = "👨🏼‍⚖️", + ["man_judge_medium_light_skin_tone"] = "👨🏼‍⚖️", + ["man_judge_tone3"] = "👨🏽‍⚖️", + ["man_judge_medium_skin_tone"] = "👨🏽‍⚖️", + ["man_judge_tone4"] = "👨🏾‍⚖️", + ["man_judge_medium_dark_skin_tone"] = "👨🏾‍⚖️", + ["man_judge_tone5"] = "👨🏿‍⚖️", + ["man_judge_dark_skin_tone"] = "👨🏿‍⚖️", + ["person_with_veil"] = "👰", + ["person_with_veil_tone1"] = "👰🏻", + ["person_with_veil_tone2"] = "👰🏼", + ["person_with_veil_tone3"] = "👰🏽", + ["person_with_veil_tone4"] = "👰🏾", + ["person_with_veil_tone5"] = "👰🏿", + ["woman_with_veil"] = "👰‍♀️", + ["bride_with_veil"] = "👰‍♀️", + ["woman_with_veil_tone1"] = "👰🏻‍♀️", + ["woman_with_veil_light_skin_tone"] = "👰🏻‍♀️", + ["woman_with_veil_tone2"] = "👰🏼‍♀️", + ["woman_with_veil_medium_light_skin_tone"] = "👰🏼‍♀️", + ["woman_with_veil_tone3"] = "👰🏽‍♀️", + ["woman_with_veil_medium_skin_tone"] = "👰🏽‍♀️", + ["woman_with_veil_tone4"] = "👰🏾‍♀️", + ["woman_with_veil_medium_dark_skin_tone"] = "👰🏾‍♀️", + ["woman_with_veil_tone5"] = "👰🏿‍♀️", + ["woman_with_veil_dark_skin_tone"] = "👰🏿‍♀️", + ["man_with_veil"] = "👰‍♂️", + ["man_with_veil_tone1"] = "👰🏻‍♂️", + ["man_with_veil_light_skin_tone"] = "👰🏻‍♂️", + ["man_with_veil_tone2"] = "👰🏼‍♂️", + ["man_with_veil_medium_light_skin_tone"] = "👰🏼‍♂️", + ["man_with_veil_tone3"] = "👰🏽‍♂️", + ["man_with_veil_medium_skin_tone"] = "👰🏽‍♂️", + ["man_with_veil_tone4"] = "👰🏾‍♂️", + ["man_with_veil_medium_dark_skin_tone"] = "👰🏾‍♂️", + ["man_with_veil_tone5"] = "👰🏿‍♂️", + ["man_with_veil_dark_skin_tone"] = "👰🏿‍♂️", + ["person_in_tuxedo"] = "🤵", + ["person_in_tuxedo_tone1"] = "🤵🏻", + ["tuxedo_tone1"] = "🤵🏻", + ["person_in_tuxedo_tone2"] = "🤵🏼", + ["tuxedo_tone2"] = "🤵🏼", + ["person_in_tuxedo_tone3"] = "🤵🏽", + ["tuxedo_tone3"] = "🤵🏽", + ["person_in_tuxedo_tone4"] = "🤵🏾", + ["tuxedo_tone4"] = "🤵🏾", + ["person_in_tuxedo_tone5"] = "🤵🏿", + ["tuxedo_tone5"] = "🤵🏿", + ["woman_in_tuxedo"] = "🤵‍♀️", + ["woman_in_tuxedo_tone1"] = "🤵🏻‍♀️", + ["woman_in_tuxedo_light_skin_tone"] = "🤵🏻‍♀️", + ["woman_in_tuxedo_tone2"] = "🤵🏼‍♀️", + ["woman_in_tuxedo_medium_light_skin_tone"] = "🤵🏼‍♀️", + ["woman_in_tuxedo_tone3"] = "🤵🏽‍♀️", + ["woman_in_tuxedo_medium_skin_tone"] = "🤵🏽‍♀️", + ["woman_in_tuxedo_tone4"] = "🤵🏾‍♀️", + ["woman_in_tuxedo_medium_dark_skin_tone"] = "🤵🏾‍♀️", + ["woman_in_tuxedo_tone5"] = "🤵🏿‍♀️", + ["woman_in_tuxedo_dark_skin_tone"] = "🤵🏿‍♀️", + ["man_in_tuxedo"] = "🤵‍♂️", + ["man_in_tuxedo_tone1"] = "🤵🏻‍♂️", + ["man_in_tuxedo_light_skin_tone"] = "🤵🏻‍♂️", + ["man_in_tuxedo_tone2"] = "🤵🏼‍♂️", + ["man_in_tuxedo_medium_light_skin_tone"] = "🤵🏼‍♂️", + ["man_in_tuxedo_tone3"] = "🤵🏽‍♂️", + ["man_in_tuxedo_medium_skin_tone"] = "🤵🏽‍♂️", + ["man_in_tuxedo_tone4"] = "🤵🏾‍♂️", + ["man_in_tuxedo_medium_dark_skin_tone"] = "🤵🏾‍♂️", + ["man_in_tuxedo_tone5"] = "🤵🏿‍♂️", + ["man_in_tuxedo_dark_skin_tone"] = "🤵🏿‍♂️", + ["princess"] = "👸", + ["princess_tone1"] = "👸🏻", + ["princess_tone2"] = "👸🏼", + ["princess_tone3"] = "👸🏽", + ["princess_tone4"] = "👸🏾", + ["princess_tone5"] = "👸🏿", + ["prince"] = "🤴", + ["prince_tone1"] = "🤴🏻", + ["prince_tone2"] = "🤴🏼", + ["prince_tone3"] = "🤴🏽", + ["prince_tone4"] = "🤴🏾", + ["prince_tone5"] = "🤴🏿", + ["superhero"] = "🦸", + ["superhero_tone1"] = "🦸🏻", + ["superhero_light_skin_tone"] = "🦸🏻", + ["superhero_tone2"] = "🦸🏼", + ["superhero_medium_light_skin_tone"] = "🦸🏼", + ["superhero_tone3"] = "🦸🏽", + ["superhero_medium_skin_tone"] = "🦸🏽", + ["superhero_tone4"] = "🦸🏾", + ["superhero_medium_dark_skin_tone"] = "🦸🏾", + ["superhero_tone5"] = "🦸🏿", + ["superhero_dark_skin_tone"] = "🦸🏿", + ["woman_superhero"] = "🦸‍♀️", + ["woman_superhero_tone1"] = "🦸🏻‍♀️", + ["woman_superhero_light_skin_tone"] = "🦸🏻‍♀️", + ["woman_superhero_tone2"] = "🦸🏼‍♀️", + ["woman_superhero_medium_light_skin_tone"] = "🦸🏼‍♀️", + ["woman_superhero_tone3"] = "🦸🏽‍♀️", + ["woman_superhero_medium_skin_tone"] = "🦸🏽‍♀️", + ["woman_superhero_tone4"] = "🦸🏾‍♀️", + ["woman_superhero_medium_dark_skin_tone"] = "🦸🏾‍♀️", + ["woman_superhero_tone5"] = "🦸🏿‍♀️", + ["woman_superhero_dark_skin_tone"] = "🦸🏿‍♀️", + ["man_superhero"] = "🦸‍♂️", + ["man_superhero_tone1"] = "🦸🏻‍♂️", + ["man_superhero_light_skin_tone"] = "🦸🏻‍♂️", + ["man_superhero_tone2"] = "🦸🏼‍♂️", + ["man_superhero_medium_light_skin_tone"] = "🦸🏼‍♂️", + ["man_superhero_tone3"] = "🦸🏽‍♂️", + ["man_superhero_medium_skin_tone"] = "🦸🏽‍♂️", + ["man_superhero_tone4"] = "🦸🏾‍♂️", + ["man_superhero_medium_dark_skin_tone"] = "🦸🏾‍♂️", + ["man_superhero_tone5"] = "🦸🏿‍♂️", + ["man_superhero_dark_skin_tone"] = "🦸🏿‍♂️", + ["supervillain"] = "🦹", + ["supervillain_tone1"] = "🦹🏻", + ["supervillain_light_skin_tone"] = "🦹🏻", + ["supervillain_tone2"] = "🦹🏼", + ["supervillain_medium_light_skin_tone"] = "🦹🏼", + ["supervillain_tone3"] = "🦹🏽", + ["supervillain_medium_skin_tone"] = "🦹🏽", + ["supervillain_tone4"] = "🦹🏾", + ["supervillain_medium_dark_skin_tone"] = "🦹🏾", + ["supervillain_tone5"] = "🦹🏿", + ["supervillain_dark_skin_tone"] = "🦹🏿", + ["woman_supervillain"] = "🦹‍♀️", + ["woman_supervillain_tone1"] = "🦹🏻‍♀️", + ["woman_supervillain_light_skin_tone"] = "🦹🏻‍♀️", + ["woman_supervillain_tone2"] = "🦹🏼‍♀️", + ["woman_supervillain_medium_light_skin_tone"] = "🦹🏼‍♀️", + ["woman_supervillain_tone3"] = "🦹🏽‍♀️", + ["woman_supervillain_medium_skin_tone"] = "🦹🏽‍♀️", + ["woman_supervillain_tone4"] = "🦹🏾‍♀️", + ["woman_supervillain_medium_dark_skin_tone"] = "🦹🏾‍♀️", + ["woman_supervillain_tone5"] = "🦹🏿‍♀️", + ["woman_supervillain_dark_skin_tone"] = "🦹🏿‍♀️", + ["man_supervillain"] = "🦹‍♂️", + ["man_supervillain_tone1"] = "🦹🏻‍♂️", + ["man_supervillain_light_skin_tone"] = "🦹🏻‍♂️", + ["man_supervillain_tone2"] = "🦹🏼‍♂️", + ["man_supervillain_medium_light_skin_tone"] = "🦹🏼‍♂️", + ["man_supervillain_tone3"] = "🦹🏽‍♂️", + ["man_supervillain_medium_skin_tone"] = "🦹🏽‍♂️", + ["man_supervillain_tone4"] = "🦹🏾‍♂️", + ["man_supervillain_medium_dark_skin_tone"] = "🦹🏾‍♂️", + ["man_supervillain_tone5"] = "🦹🏿‍♂️", + ["man_supervillain_dark_skin_tone"] = "🦹🏿‍♂️", + ["ninja"] = "🥷", + ["ninja_tone1"] = "🥷🏻", + ["ninja_light_skin_tone"] = "🥷🏻", + ["ninja_tone2"] = "🥷🏼", + ["ninja_medium_light_skin_tone"] = "🥷🏼", + ["ninja_tone3"] = "🥷🏽", + ["ninja_medium_skin_tone"] = "🥷🏽", + ["ninja_tone4"] = "🥷🏾", + ["ninja_medium_dark_skin_tone"] = "🥷🏾", + ["ninja_tone5"] = "🥷🏿", + ["ninja_dark_skin_tone"] = "🥷🏿", + ["mx_claus"] = "🧑‍🎄", + ["mx_claus_tone1"] = "🧑🏻‍🎄", + ["mx_claus_light_skin_tone"] = "🧑🏻‍🎄", + ["mx_claus_tone2"] = "🧑🏼‍🎄", + ["mx_claus_medium_light_skin_tone"] = "🧑🏼‍🎄", + ["mx_claus_tone3"] = "🧑🏽‍🎄", + ["mx_claus_medium_skin_tone"] = "🧑🏽‍🎄", + ["mx_claus_tone4"] = "🧑🏾‍🎄", + ["mx_claus_medium_dark_skin_tone"] = "🧑🏾‍🎄", + ["mx_claus_tone5"] = "🧑🏿‍🎄", + ["mx_claus_dark_skin_tone"] = "🧑🏿‍🎄", + ["mrs_claus"] = "🤶", + ["mother_christmas"] = "🤶", + ["mrs_claus_tone1"] = "🤶🏻", + ["mother_christmas_tone1"] = "🤶🏻", + ["mrs_claus_tone2"] = "🤶🏼", + ["mother_christmas_tone2"] = "🤶🏼", + ["mrs_claus_tone3"] = "🤶🏽", + ["mother_christmas_tone3"] = "🤶🏽", + ["mrs_claus_tone4"] = "🤶🏾", + ["mother_christmas_tone4"] = "🤶🏾", + ["mrs_claus_tone5"] = "🤶🏿", + ["mother_christmas_tone5"] = "🤶🏿", + ["santa"] = "🎅", + ["santa_tone1"] = "🎅🏻", + ["santa_tone2"] = "🎅🏼", + ["santa_tone3"] = "🎅🏽", + ["santa_tone4"] = "🎅🏾", + ["santa_tone5"] = "🎅🏿", + ["mage"] = "🧙", + ["mage_tone1"] = "🧙🏻", + ["mage_light_skin_tone"] = "🧙🏻", + ["mage_tone2"] = "🧙🏼", + ["mage_medium_light_skin_tone"] = "🧙🏼", + ["mage_tone3"] = "🧙🏽", + ["mage_medium_skin_tone"] = "🧙🏽", + ["mage_tone4"] = "🧙🏾", + ["mage_medium_dark_skin_tone"] = "🧙🏾", + ["mage_tone5"] = "🧙🏿", + ["mage_dark_skin_tone"] = "🧙🏿", + ["woman_mage"] = "🧙‍♀️", + ["woman_mage_tone1"] = "🧙🏻‍♀️", + ["woman_mage_light_skin_tone"] = "🧙🏻‍♀️", + ["woman_mage_tone2"] = "🧙🏼‍♀️", + ["woman_mage_medium_light_skin_tone"] = "🧙🏼‍♀️", + ["woman_mage_tone3"] = "🧙🏽‍♀️", + ["woman_mage_medium_skin_tone"] = "🧙🏽‍♀️", + ["woman_mage_tone4"] = "🧙🏾‍♀️", + ["woman_mage_medium_dark_skin_tone"] = "🧙🏾‍♀️", + ["woman_mage_tone5"] = "🧙🏿‍♀️", + ["woman_mage_dark_skin_tone"] = "🧙🏿‍♀️", + ["man_mage"] = "🧙‍♂️", + ["man_mage_tone1"] = "🧙🏻‍♂️", + ["man_mage_light_skin_tone"] = "🧙🏻‍♂️", + ["man_mage_tone2"] = "🧙🏼‍♂️", + ["man_mage_medium_light_skin_tone"] = "🧙🏼‍♂️", + ["man_mage_tone3"] = "🧙🏽‍♂️", + ["man_mage_medium_skin_tone"] = "🧙🏽‍♂️", + ["man_mage_tone4"] = "🧙🏾‍♂️", + ["man_mage_medium_dark_skin_tone"] = "🧙🏾‍♂️", + ["man_mage_tone5"] = "🧙🏿‍♂️", + ["man_mage_dark_skin_tone"] = "🧙🏿‍♂️", + ["elf"] = "🧝", + ["elf_tone1"] = "🧝🏻", + ["elf_light_skin_tone"] = "🧝🏻", + ["elf_tone2"] = "🧝🏼", + ["elf_medium_light_skin_tone"] = "🧝🏼", + ["elf_tone3"] = "🧝🏽", + ["elf_medium_skin_tone"] = "🧝🏽", + ["elf_tone4"] = "🧝🏾", + ["elf_medium_dark_skin_tone"] = "🧝🏾", + ["elf_tone5"] = "🧝🏿", + ["elf_dark_skin_tone"] = "🧝🏿", + ["woman_elf"] = "🧝‍♀️", + ["woman_elf_tone1"] = "🧝🏻‍♀️", + ["woman_elf_light_skin_tone"] = "🧝🏻‍♀️", + ["woman_elf_tone2"] = "🧝🏼‍♀️", + ["woman_elf_medium_light_skin_tone"] = "🧝🏼‍♀️", + ["woman_elf_tone3"] = "🧝🏽‍♀️", + ["woman_elf_medium_skin_tone"] = "🧝🏽‍♀️", + ["woman_elf_tone4"] = "🧝🏾‍♀️", + ["woman_elf_medium_dark_skin_tone"] = "🧝🏾‍♀️", + ["woman_elf_tone5"] = "🧝🏿‍♀️", + ["woman_elf_dark_skin_tone"] = "🧝🏿‍♀️", + ["man_elf"] = "🧝‍♂️", + ["man_elf_tone1"] = "🧝🏻‍♂️", + ["man_elf_light_skin_tone"] = "🧝🏻‍♂️", + ["man_elf_tone2"] = "🧝🏼‍♂️", + ["man_elf_medium_light_skin_tone"] = "🧝🏼‍♂️", + ["man_elf_tone3"] = "🧝🏽‍♂️", + ["man_elf_medium_skin_tone"] = "🧝🏽‍♂️", + ["man_elf_tone4"] = "🧝🏾‍♂️", + ["man_elf_medium_dark_skin_tone"] = "🧝🏾‍♂️", + ["man_elf_tone5"] = "🧝🏿‍♂️", + ["man_elf_dark_skin_tone"] = "🧝🏿‍♂️", + ["vampire"] = "🧛", + ["vampire_tone1"] = "🧛🏻", + ["vampire_light_skin_tone"] = "🧛🏻", + ["vampire_tone2"] = "🧛🏼", + ["vampire_medium_light_skin_tone"] = "🧛🏼", + ["vampire_tone3"] = "🧛🏽", + ["vampire_medium_skin_tone"] = "🧛🏽", + ["vampire_tone4"] = "🧛🏾", + ["vampire_medium_dark_skin_tone"] = "🧛🏾", + ["vampire_tone5"] = "🧛🏿", + ["vampire_dark_skin_tone"] = "🧛🏿", + ["woman_vampire"] = "🧛‍♀️", + ["woman_vampire_tone1"] = "🧛🏻‍♀️", + ["woman_vampire_light_skin_tone"] = "🧛🏻‍♀️", + ["woman_vampire_tone2"] = "🧛🏼‍♀️", + ["woman_vampire_medium_light_skin_tone"] = "🧛🏼‍♀️", + ["woman_vampire_tone3"] = "🧛🏽‍♀️", + ["woman_vampire_medium_skin_tone"] = "🧛🏽‍♀️", + ["woman_vampire_tone4"] = "🧛🏾‍♀️", + ["woman_vampire_medium_dark_skin_tone"] = "🧛🏾‍♀️", + ["woman_vampire_tone5"] = "🧛🏿‍♀️", + ["woman_vampire_dark_skin_tone"] = "🧛🏿‍♀️", + ["man_vampire"] = "🧛‍♂️", + ["man_vampire_tone1"] = "🧛🏻‍♂️", + ["man_vampire_light_skin_tone"] = "🧛🏻‍♂️", + ["man_vampire_tone2"] = "🧛🏼‍♂️", + ["man_vampire_medium_light_skin_tone"] = "🧛🏼‍♂️", + ["man_vampire_tone3"] = "🧛🏽‍♂️", + ["man_vampire_medium_skin_tone"] = "🧛🏽‍♂️", + ["man_vampire_tone4"] = "🧛🏾‍♂️", + ["man_vampire_medium_dark_skin_tone"] = "🧛🏾‍♂️", + ["man_vampire_tone5"] = "🧛🏿‍♂️", + ["man_vampire_dark_skin_tone"] = "🧛🏿‍♂️", + ["zombie"] = "🧟", + ["woman_zombie"] = "🧟‍♀️", + ["man_zombie"] = "🧟‍♂️", + ["genie"] = "🧞", + ["woman_genie"] = "🧞‍♀️", + ["man_genie"] = "🧞‍♂️", + ["merperson"] = "🧜", + ["merperson_tone1"] = "🧜🏻", + ["merperson_light_skin_tone"] = "🧜🏻", + ["merperson_tone2"] = "🧜🏼", + ["merperson_medium_light_skin_tone"] = "🧜🏼", + ["merperson_tone3"] = "🧜🏽", + ["merperson_medium_skin_tone"] = "🧜🏽", + ["merperson_tone4"] = "🧜🏾", + ["merperson_medium_dark_skin_tone"] = "🧜🏾", + ["merperson_tone5"] = "🧜🏿", + ["merperson_dark_skin_tone"] = "🧜🏿", + ["mermaid"] = "🧜‍♀️", + ["mermaid_tone1"] = "🧜🏻‍♀️", + ["mermaid_light_skin_tone"] = "🧜🏻‍♀️", + ["mermaid_tone2"] = "🧜🏼‍♀️", + ["mermaid_medium_light_skin_tone"] = "🧜🏼‍♀️", + ["mermaid_tone3"] = "🧜🏽‍♀️", + ["mermaid_medium_skin_tone"] = "🧜🏽‍♀️", + ["mermaid_tone4"] = "🧜🏾‍♀️", + ["mermaid_medium_dark_skin_tone"] = "🧜🏾‍♀️", + ["mermaid_tone5"] = "🧜🏿‍♀️", + ["mermaid_dark_skin_tone"] = "🧜🏿‍♀️", + ["merman"] = "🧜‍♂️", + ["merman_tone1"] = "🧜🏻‍♂️", + ["merman_light_skin_tone"] = "🧜🏻‍♂️", + ["merman_tone2"] = "🧜🏼‍♂️", + ["merman_medium_light_skin_tone"] = "🧜🏼‍♂️", + ["merman_tone3"] = "🧜🏽‍♂️", + ["merman_medium_skin_tone"] = "🧜🏽‍♂️", + ["merman_tone4"] = "🧜🏾‍♂️", + ["merman_medium_dark_skin_tone"] = "🧜🏾‍♂️", + ["merman_tone5"] = "🧜🏿‍♂️", + ["merman_dark_skin_tone"] = "🧜🏿‍♂️", + ["fairy"] = "🧚", + ["fairy_tone1"] = "🧚🏻", + ["fairy_light_skin_tone"] = "🧚🏻", + ["fairy_tone2"] = "🧚🏼", + ["fairy_medium_light_skin_tone"] = "🧚🏼", + ["fairy_tone3"] = "🧚🏽", + ["fairy_medium_skin_tone"] = "🧚🏽", + ["fairy_tone4"] = "🧚🏾", + ["fairy_medium_dark_skin_tone"] = "🧚🏾", + ["fairy_tone5"] = "🧚🏿", + ["fairy_dark_skin_tone"] = "🧚🏿", + ["woman_fairy"] = "🧚‍♀️", + ["woman_fairy_tone1"] = "🧚🏻‍♀️", + ["woman_fairy_light_skin_tone"] = "🧚🏻‍♀️", + ["woman_fairy_tone2"] = "🧚🏼‍♀️", + ["woman_fairy_medium_light_skin_tone"] = "🧚🏼‍♀️", + ["woman_fairy_tone3"] = "🧚🏽‍♀️", + ["woman_fairy_medium_skin_tone"] = "🧚🏽‍♀️", + ["woman_fairy_tone4"] = "🧚🏾‍♀️", + ["woman_fairy_medium_dark_skin_tone"] = "🧚🏾‍♀️", + ["woman_fairy_tone5"] = "🧚🏿‍♀️", + ["woman_fairy_dark_skin_tone"] = "🧚🏿‍♀️", + ["man_fairy"] = "🧚‍♂️", + ["man_fairy_tone1"] = "🧚🏻‍♂️", + ["man_fairy_light_skin_tone"] = "🧚🏻‍♂️", + ["man_fairy_tone2"] = "🧚🏼‍♂️", + ["man_fairy_medium_light_skin_tone"] = "🧚🏼‍♂️", + ["man_fairy_tone3"] = "🧚🏽‍♂️", + ["man_fairy_medium_skin_tone"] = "🧚🏽‍♂️", + ["man_fairy_tone4"] = "🧚🏾‍♂️", + ["man_fairy_medium_dark_skin_tone"] = "🧚🏾‍♂️", + ["man_fairy_tone5"] = "🧚🏿‍♂️", + ["man_fairy_dark_skin_tone"] = "🧚🏿‍♂️", + ["angel"] = "👼", + ["angel_tone1"] = "👼🏻", + ["angel_tone2"] = "👼🏼", + ["angel_tone3"] = "👼🏽", + ["angel_tone4"] = "👼🏾", + ["angel_tone5"] = "👼🏿", + ["pregnant_woman"] = "🤰", + ["expecting_woman"] = "🤰", + ["pregnant_woman_tone1"] = "🤰🏻", + ["expecting_woman_tone1"] = "🤰🏻", + ["pregnant_woman_tone2"] = "🤰🏼", + ["expecting_woman_tone2"] = "🤰🏼", + ["pregnant_woman_tone3"] = "🤰🏽", + ["expecting_woman_tone3"] = "🤰🏽", + ["pregnant_woman_tone4"] = "🤰🏾", + ["expecting_woman_tone4"] = "🤰🏾", + ["pregnant_woman_tone5"] = "🤰🏿", + ["expecting_woman_tone5"] = "🤰🏿", + ["breast_feeding"] = "🤱", + ["breast_feeding_tone1"] = "🤱🏻", + ["breast_feeding_light_skin_tone"] = "🤱🏻", + ["breast_feeding_tone2"] = "🤱🏼", + ["breast_feeding_medium_light_skin_tone"] = "🤱🏼", + ["breast_feeding_tone3"] = "🤱🏽", + ["breast_feeding_medium_skin_tone"] = "🤱🏽", + ["breast_feeding_tone4"] = "🤱🏾", + ["breast_feeding_medium_dark_skin_tone"] = "🤱🏾", + ["breast_feeding_tone5"] = "🤱🏿", + ["breast_feeding_dark_skin_tone"] = "🤱🏿", + ["person_feeding_baby"] = "🧑‍🍼", + ["person_feeding_baby_tone1"] = "🧑🏻‍🍼", + ["person_feeding_baby_light_skin_tone"] = "🧑🏻‍🍼", + ["person_feeding_baby_tone2"] = "🧑🏼‍🍼", + ["person_feeding_baby_medium_light_skin_tone"] = "🧑🏼‍🍼", + ["person_feeding_baby_tone3"] = "🧑🏽‍🍼", + ["person_feeding_baby_medium_skin_tone"] = "🧑🏽‍🍼", + ["person_feeding_baby_tone4"] = "🧑🏾‍🍼", + ["person_feeding_baby_medium_dark_skin_tone"] = "🧑🏾‍🍼", + ["person_feeding_baby_tone5"] = "🧑🏿‍🍼", + ["person_feeding_baby_dark_skin_tone"] = "🧑🏿‍🍼", + ["woman_feeding_baby"] = "👩‍🍼", + ["woman_feeding_baby_tone1"] = "👩🏻‍🍼", + ["woman_feeding_baby_light_skin_tone"] = "👩🏻‍🍼", + ["woman_feeding_baby_tone2"] = "👩🏼‍🍼", + ["woman_feeding_baby_medium_light_skin_tone"] = "👩🏼‍🍼", + ["woman_feeding_baby_tone3"] = "👩🏽‍🍼", + ["woman_feeding_baby_medium_skin_tone"] = "👩🏽‍🍼", + ["woman_feeding_baby_tone4"] = "👩🏾‍🍼", + ["woman_feeding_baby_medium_dark_skin_tone"] = "👩🏾‍🍼", + ["woman_feeding_baby_tone5"] = "👩🏿‍🍼", + ["woman_feeding_baby_dark_skin_tone"] = "👩🏿‍🍼", + ["man_feeding_baby"] = "👨‍🍼", + ["man_feeding_baby_tone1"] = "👨🏻‍🍼", + ["man_feeding_baby_light_skin_tone"] = "👨🏻‍🍼", + ["man_feeding_baby_tone2"] = "👨🏼‍🍼", + ["man_feeding_baby_medium_light_skin_tone"] = "👨🏼‍🍼", + ["man_feeding_baby_tone3"] = "👨🏽‍🍼", + ["man_feeding_baby_medium_skin_tone"] = "👨🏽‍🍼", + ["man_feeding_baby_tone4"] = "👨🏾‍🍼", + ["man_feeding_baby_medium_dark_skin_tone"] = "👨🏾‍🍼", + ["man_feeding_baby_tone5"] = "👨🏿‍🍼", + ["man_feeding_baby_dark_skin_tone"] = "👨🏿‍🍼", + ["person_bowing"] = "🙇", + ["bow"] = "🙇", + ["person_bowing_tone1"] = "🙇🏻", + ["bow_tone1"] = "🙇🏻", + ["person_bowing_tone2"] = "🙇🏼", + ["bow_tone2"] = "🙇🏼", + ["person_bowing_tone3"] = "🙇🏽", + ["bow_tone3"] = "🙇🏽", + ["person_bowing_tone4"] = "🙇🏾", + ["bow_tone4"] = "🙇🏾", + ["person_bowing_tone5"] = "🙇🏿", + ["bow_tone5"] = "🙇🏿", + ["woman_bowing"] = "🙇‍♀️", + ["woman_bowing_tone1"] = "🙇🏻‍♀️", + ["woman_bowing_light_skin_tone"] = "🙇🏻‍♀️", + ["woman_bowing_tone2"] = "🙇🏼‍♀️", + ["woman_bowing_medium_light_skin_tone"] = "🙇🏼‍♀️", + ["woman_bowing_tone3"] = "🙇🏽‍♀️", + ["woman_bowing_medium_skin_tone"] = "🙇🏽‍♀️", + ["woman_bowing_tone4"] = "🙇🏾‍♀️", + ["woman_bowing_medium_dark_skin_tone"] = "🙇🏾‍♀️", + ["woman_bowing_tone5"] = "🙇🏿‍♀️", + ["woman_bowing_dark_skin_tone"] = "🙇🏿‍♀️", + ["man_bowing"] = "🙇‍♂️", + ["man_bowing_tone1"] = "🙇🏻‍♂️", + ["man_bowing_light_skin_tone"] = "🙇🏻‍♂️", + ["man_bowing_tone2"] = "🙇🏼‍♂️", + ["man_bowing_medium_light_skin_tone"] = "🙇🏼‍♂️", + ["man_bowing_tone3"] = "🙇🏽‍♂️", + ["man_bowing_medium_skin_tone"] = "🙇🏽‍♂️", + ["man_bowing_tone4"] = "🙇🏾‍♂️", + ["man_bowing_medium_dark_skin_tone"] = "🙇🏾‍♂️", + ["man_bowing_tone5"] = "🙇🏿‍♂️", + ["man_bowing_dark_skin_tone"] = "🙇🏿‍♂️", + ["person_tipping_hand"] = "💁", + ["information_desk_person"] = "💁", + ["person_tipping_hand_tone1"] = "💁🏻", + ["information_desk_person_tone1"] = "💁🏻", + ["person_tipping_hand_tone2"] = "💁🏼", + ["information_desk_person_tone2"] = "💁🏼", + ["person_tipping_hand_tone3"] = "💁🏽", + ["information_desk_person_tone3"] = "💁🏽", + ["person_tipping_hand_tone4"] = "💁🏾", + ["information_desk_person_tone4"] = "💁🏾", + ["person_tipping_hand_tone5"] = "💁🏿", + ["information_desk_person_tone5"] = "💁🏿", + ["woman_tipping_hand"] = "💁‍♀️", + ["woman_tipping_hand_tone1"] = "💁🏻‍♀️", + ["woman_tipping_hand_light_skin_tone"] = "💁🏻‍♀️", + ["woman_tipping_hand_tone2"] = "💁🏼‍♀️", + ["woman_tipping_hand_medium_light_skin_tone"] = "💁🏼‍♀️", + ["woman_tipping_hand_tone3"] = "💁🏽‍♀️", + ["woman_tipping_hand_medium_skin_tone"] = "💁🏽‍♀️", + ["woman_tipping_hand_tone4"] = "💁🏾‍♀️", + ["woman_tipping_hand_medium_dark_skin_tone"] = "💁🏾‍♀️", + ["woman_tipping_hand_tone5"] = "💁🏿‍♀️", + ["woman_tipping_hand_dark_skin_tone"] = "💁🏿‍♀️", + ["man_tipping_hand"] = "💁‍♂️", + ["man_tipping_hand_tone1"] = "💁🏻‍♂️", + ["man_tipping_hand_light_skin_tone"] = "💁🏻‍♂️", + ["man_tipping_hand_tone2"] = "💁🏼‍♂️", + ["man_tipping_hand_medium_light_skin_tone"] = "💁🏼‍♂️", + ["man_tipping_hand_tone3"] = "💁🏽‍♂️", + ["man_tipping_hand_medium_skin_tone"] = "💁🏽‍♂️", + ["man_tipping_hand_tone4"] = "💁🏾‍♂️", + ["man_tipping_hand_medium_dark_skin_tone"] = "💁🏾‍♂️", + ["man_tipping_hand_tone5"] = "💁🏿‍♂️", + ["man_tipping_hand_dark_skin_tone"] = "💁🏿‍♂️", + ["person_gesturing_no"] = "🙅", + ["no_good"] = "🙅", + ["person_gesturing_no_tone1"] = "🙅🏻", + ["no_good_tone1"] = "🙅🏻", + ["person_gesturing_no_tone2"] = "🙅🏼", + ["no_good_tone2"] = "🙅🏼", + ["person_gesturing_no_tone3"] = "🙅🏽", + ["no_good_tone3"] = "🙅🏽", + ["person_gesturing_no_tone4"] = "🙅🏾", + ["no_good_tone4"] = "🙅🏾", + ["person_gesturing_no_tone5"] = "🙅🏿", + ["no_good_tone5"] = "🙅🏿", + ["woman_gesturing_no"] = "🙅‍♀️", + ["woman_gesturing_no_tone1"] = "🙅🏻‍♀️", + ["woman_gesturing_no_light_skin_tone"] = "🙅🏻‍♀️", + ["woman_gesturing_no_tone2"] = "🙅🏼‍♀️", + ["woman_gesturing_no_medium_light_skin_tone"] = "🙅🏼‍♀️", + ["woman_gesturing_no_tone3"] = "🙅🏽‍♀️", + ["woman_gesturing_no_medium_skin_tone"] = "🙅🏽‍♀️", + ["woman_gesturing_no_tone4"] = "🙅🏾‍♀️", + ["woman_gesturing_no_medium_dark_skin_tone"] = "🙅🏾‍♀️", + ["woman_gesturing_no_tone5"] = "🙅🏿‍♀️", + ["woman_gesturing_no_dark_skin_tone"] = "🙅🏿‍♀️", + ["man_gesturing_no"] = "🙅‍♂️", + ["man_gesturing_no_tone1"] = "🙅🏻‍♂️", + ["man_gesturing_no_light_skin_tone"] = "🙅🏻‍♂️", + ["man_gesturing_no_tone2"] = "🙅🏼‍♂️", + ["man_gesturing_no_medium_light_skin_tone"] = "🙅🏼‍♂️", + ["man_gesturing_no_tone3"] = "🙅🏽‍♂️", + ["man_gesturing_no_medium_skin_tone"] = "🙅🏽‍♂️", + ["man_gesturing_no_tone4"] = "🙅🏾‍♂️", + ["man_gesturing_no_medium_dark_skin_tone"] = "🙅🏾‍♂️", + ["man_gesturing_no_tone5"] = "🙅🏿‍♂️", + ["man_gesturing_no_dark_skin_tone"] = "🙅🏿‍♂️", + ["person_gesturing_ok"] = "🙆", + ["ok_woman"] = "🙆", + ["person_gesturing_ok_tone1"] = "🙆🏻", + ["ok_woman_tone1"] = "🙆🏻", + ["person_gesturing_ok_tone2"] = "🙆🏼", + ["ok_woman_tone2"] = "🙆🏼", + ["person_gesturing_ok_tone3"] = "🙆🏽", + ["ok_woman_tone3"] = "🙆🏽", + ["person_gesturing_ok_tone4"] = "🙆🏾", + ["ok_woman_tone4"] = "🙆🏾", + ["person_gesturing_ok_tone5"] = "🙆🏿", + ["ok_woman_tone5"] = "🙆🏿", + ["woman_gesturing_ok"] = "🙆‍♀️", + ["woman_gesturing_ok_tone1"] = "🙆🏻‍♀️", + ["woman_gesturing_ok_light_skin_tone"] = "🙆🏻‍♀️", + ["woman_gesturing_ok_tone2"] = "🙆🏼‍♀️", + ["woman_gesturing_ok_medium_light_skin_tone"] = "🙆🏼‍♀️", + ["woman_gesturing_ok_tone3"] = "🙆🏽‍♀️", + ["woman_gesturing_ok_medium_skin_tone"] = "🙆🏽‍♀️", + ["woman_gesturing_ok_tone4"] = "🙆🏾‍♀️", + ["woman_gesturing_ok_medium_dark_skin_tone"] = "🙆🏾‍♀️", + ["woman_gesturing_ok_tone5"] = "🙆🏿‍♀️", + ["woman_gesturing_ok_dark_skin_tone"] = "🙆🏿‍♀️", + ["man_gesturing_ok"] = "🙆‍♂️", + ["man_gesturing_ok_tone1"] = "🙆🏻‍♂️", + ["man_gesturing_ok_light_skin_tone"] = "🙆🏻‍♂️", + ["man_gesturing_ok_tone2"] = "🙆🏼‍♂️", + ["man_gesturing_ok_medium_light_skin_tone"] = "🙆🏼‍♂️", + ["man_gesturing_ok_tone3"] = "🙆🏽‍♂️", + ["man_gesturing_ok_medium_skin_tone"] = "🙆🏽‍♂️", + ["man_gesturing_ok_tone4"] = "🙆🏾‍♂️", + ["man_gesturing_ok_medium_dark_skin_tone"] = "🙆🏾‍♂️", + ["man_gesturing_ok_tone5"] = "🙆🏿‍♂️", + ["man_gesturing_ok_dark_skin_tone"] = "🙆🏿‍♂️", + ["person_raising_hand"] = "🙋", + ["raising_hand"] = "🙋", + ["person_raising_hand_tone1"] = "🙋🏻", + ["raising_hand_tone1"] = "🙋🏻", + ["person_raising_hand_tone2"] = "🙋🏼", + ["raising_hand_tone2"] = "🙋🏼", + ["person_raising_hand_tone3"] = "🙋🏽", + ["raising_hand_tone3"] = "🙋🏽", + ["person_raising_hand_tone4"] = "🙋🏾", + ["raising_hand_tone4"] = "🙋🏾", + ["person_raising_hand_tone5"] = "🙋🏿", + ["raising_hand_tone5"] = "🙋🏿", + ["woman_raising_hand"] = "🙋‍♀️", + ["woman_raising_hand_tone1"] = "🙋🏻‍♀️", + ["woman_raising_hand_light_skin_tone"] = "🙋🏻‍♀️", + ["woman_raising_hand_tone2"] = "🙋🏼‍♀️", + ["woman_raising_hand_medium_light_skin_tone"] = "🙋🏼‍♀️", + ["woman_raising_hand_tone3"] = "🙋🏽‍♀️", + ["woman_raising_hand_medium_skin_tone"] = "🙋🏽‍♀️", + ["woman_raising_hand_tone4"] = "🙋🏾‍♀️", + ["woman_raising_hand_medium_dark_skin_tone"] = "🙋🏾‍♀️", + ["woman_raising_hand_tone5"] = "🙋🏿‍♀️", + ["woman_raising_hand_dark_skin_tone"] = "🙋🏿‍♀️", + ["man_raising_hand"] = "🙋‍♂️", + ["man_raising_hand_tone1"] = "🙋🏻‍♂️", + ["man_raising_hand_light_skin_tone"] = "🙋🏻‍♂️", + ["man_raising_hand_tone2"] = "🙋🏼‍♂️", + ["man_raising_hand_medium_light_skin_tone"] = "🙋🏼‍♂️", + ["man_raising_hand_tone3"] = "🙋🏽‍♂️", + ["man_raising_hand_medium_skin_tone"] = "🙋🏽‍♂️", + ["man_raising_hand_tone4"] = "🙋🏾‍♂️", + ["man_raising_hand_medium_dark_skin_tone"] = "🙋🏾‍♂️", + ["man_raising_hand_tone5"] = "🙋🏿‍♂️", + ["man_raising_hand_dark_skin_tone"] = "🙋🏿‍♂️", + ["deaf_person"] = "🧏", + ["deaf_person_tone1"] = "🧏🏻", + ["deaf_person_light_skin_tone"] = "🧏🏻", + ["deaf_person_tone2"] = "🧏🏼", + ["deaf_person_medium_light_skin_tone"] = "🧏🏼", + ["deaf_person_tone3"] = "🧏🏽", + ["deaf_person_medium_skin_tone"] = "🧏🏽", + ["deaf_person_tone4"] = "🧏🏾", + ["deaf_person_medium_dark_skin_tone"] = "🧏🏾", + ["deaf_person_tone5"] = "🧏🏿", + ["deaf_person_dark_skin_tone"] = "🧏🏿", + ["deaf_woman"] = "🧏‍♀️", + ["deaf_woman_tone1"] = "🧏🏻‍♀️", + ["deaf_woman_light_skin_tone"] = "🧏🏻‍♀️", + ["deaf_woman_tone2"] = "🧏🏼‍♀️", + ["deaf_woman_medium_light_skin_tone"] = "🧏🏼‍♀️", + ["deaf_woman_tone3"] = "🧏🏽‍♀️", + ["deaf_woman_medium_skin_tone"] = "🧏🏽‍♀️", + ["deaf_woman_tone4"] = "🧏🏾‍♀️", + ["deaf_woman_medium_dark_skin_tone"] = "🧏🏾‍♀️", + ["deaf_woman_tone5"] = "🧏🏿‍♀️", + ["deaf_woman_dark_skin_tone"] = "🧏🏿‍♀️", + ["deaf_man"] = "🧏‍♂️", + ["deaf_man_tone1"] = "🧏🏻‍♂️", + ["deaf_man_light_skin_tone"] = "🧏🏻‍♂️", + ["deaf_man_tone2"] = "🧏🏼‍♂️", + ["deaf_man_medium_light_skin_tone"] = "🧏🏼‍♂️", + ["deaf_man_tone3"] = "🧏🏽‍♂️", + ["deaf_man_medium_skin_tone"] = "🧏🏽‍♂️", + ["deaf_man_tone4"] = "🧏🏾‍♂️", + ["deaf_man_medium_dark_skin_tone"] = "🧏🏾‍♂️", + ["deaf_man_tone5"] = "🧏🏿‍♂️", + ["deaf_man_dark_skin_tone"] = "🧏🏿‍♂️", + ["person_facepalming"] = "🤦", + ["face_palm"] = "🤦", + ["facepalm"] = "🤦", + ["person_facepalming_tone1"] = "🤦🏻", + ["face_palm_tone1"] = "🤦🏻", + ["facepalm_tone1"] = "🤦🏻", + ["person_facepalming_tone2"] = "🤦🏼", + ["face_palm_tone2"] = "🤦🏼", + ["facepalm_tone2"] = "🤦🏼", + ["person_facepalming_tone3"] = "🤦🏽", + ["face_palm_tone3"] = "🤦🏽", + ["facepalm_tone3"] = "🤦🏽", + ["person_facepalming_tone4"] = "🤦🏾", + ["face_palm_tone4"] = "🤦🏾", + ["facepalm_tone4"] = "🤦🏾", + ["person_facepalming_tone5"] = "🤦🏿", + ["face_palm_tone5"] = "🤦🏿", + ["facepalm_tone5"] = "🤦🏿", + ["woman_facepalming"] = "🤦‍♀️", + ["woman_facepalming_tone1"] = "🤦🏻‍♀️", + ["woman_facepalming_light_skin_tone"] = "🤦🏻‍♀️", + ["woman_facepalming_tone2"] = "🤦🏼‍♀️", + ["woman_facepalming_medium_light_skin_tone"] = "🤦🏼‍♀️", + ["woman_facepalming_tone3"] = "🤦🏽‍♀️", + ["woman_facepalming_medium_skin_tone"] = "🤦🏽‍♀️", + ["woman_facepalming_tone4"] = "🤦🏾‍♀️", + ["woman_facepalming_medium_dark_skin_tone"] = "🤦🏾‍♀️", + ["woman_facepalming_tone5"] = "🤦🏿‍♀️", + ["woman_facepalming_dark_skin_tone"] = "🤦🏿‍♀️", + ["man_facepalming"] = "🤦‍♂️", + ["man_facepalming_tone1"] = "🤦🏻‍♂️", + ["man_facepalming_light_skin_tone"] = "🤦🏻‍♂️", + ["man_facepalming_tone2"] = "🤦🏼‍♂️", + ["man_facepalming_medium_light_skin_tone"] = "🤦🏼‍♂️", + ["man_facepalming_tone3"] = "🤦🏽‍♂️", + ["man_facepalming_medium_skin_tone"] = "🤦🏽‍♂️", + ["man_facepalming_tone4"] = "🤦🏾‍♂️", + ["man_facepalming_medium_dark_skin_tone"] = "🤦🏾‍♂️", + ["man_facepalming_tone5"] = "🤦🏿‍♂️", + ["man_facepalming_dark_skin_tone"] = "🤦🏿‍♂️", + ["person_shrugging"] = "🤷", + ["shrug"] = "🤷", + ["person_shrugging_tone1"] = "🤷🏻", + ["shrug_tone1"] = "🤷🏻", + ["person_shrugging_tone2"] = "🤷🏼", + ["shrug_tone2"] = "🤷🏼", + ["person_shrugging_tone3"] = "🤷🏽", + ["shrug_tone3"] = "🤷🏽", + ["person_shrugging_tone4"] = "🤷🏾", + ["shrug_tone4"] = "🤷🏾", + ["person_shrugging_tone5"] = "🤷🏿", + ["shrug_tone5"] = "🤷🏿", + ["woman_shrugging"] = "🤷‍♀️", + ["woman_shrugging_tone1"] = "🤷🏻‍♀️", + ["woman_shrugging_light_skin_tone"] = "🤷🏻‍♀️", + ["woman_shrugging_tone2"] = "🤷🏼‍♀️", + ["woman_shrugging_medium_light_skin_tone"] = "🤷🏼‍♀️", + ["woman_shrugging_tone3"] = "🤷🏽‍♀️", + ["woman_shrugging_medium_skin_tone"] = "🤷🏽‍♀️", + ["woman_shrugging_tone4"] = "🤷🏾‍♀️", + ["woman_shrugging_medium_dark_skin_tone"] = "🤷🏾‍♀️", + ["woman_shrugging_tone5"] = "🤷🏿‍♀️", + ["woman_shrugging_dark_skin_tone"] = "🤷🏿‍♀️", + ["man_shrugging"] = "🤷‍♂️", + ["man_shrugging_tone1"] = "🤷🏻‍♂️", + ["man_shrugging_light_skin_tone"] = "🤷🏻‍♂️", + ["man_shrugging_tone2"] = "🤷🏼‍♂️", + ["man_shrugging_medium_light_skin_tone"] = "🤷🏼‍♂️", + ["man_shrugging_tone3"] = "🤷🏽‍♂️", + ["man_shrugging_medium_skin_tone"] = "🤷🏽‍♂️", + ["man_shrugging_tone4"] = "🤷🏾‍♂️", + ["man_shrugging_medium_dark_skin_tone"] = "🤷🏾‍♂️", + ["man_shrugging_tone5"] = "🤷🏿‍♂️", + ["man_shrugging_dark_skin_tone"] = "🤷🏿‍♂️", + ["person_pouting"] = "🙎", + ["person_with_pouting_face"] = "🙎", + ["person_pouting_tone1"] = "🙎🏻", + ["person_with_pouting_face_tone1"] = "🙎🏻", + ["person_pouting_tone2"] = "🙎🏼", + ["person_with_pouting_face_tone2"] = "🙎🏼", + ["person_pouting_tone3"] = "🙎🏽", + ["person_with_pouting_face_tone3"] = "🙎🏽", + ["person_pouting_tone4"] = "🙎🏾", + ["person_with_pouting_face_tone4"] = "🙎🏾", + ["person_pouting_tone5"] = "🙎🏿", + ["person_with_pouting_face_tone5"] = "🙎🏿", + ["woman_pouting"] = "🙎‍♀️", + ["woman_pouting_tone1"] = "🙎🏻‍♀️", + ["woman_pouting_light_skin_tone"] = "🙎🏻‍♀️", + ["woman_pouting_tone2"] = "🙎🏼‍♀️", + ["woman_pouting_medium_light_skin_tone"] = "🙎🏼‍♀️", + ["woman_pouting_tone3"] = "🙎🏽‍♀️", + ["woman_pouting_medium_skin_tone"] = "🙎🏽‍♀️", + ["woman_pouting_tone4"] = "🙎🏾‍♀️", + ["woman_pouting_medium_dark_skin_tone"] = "🙎🏾‍♀️", + ["woman_pouting_tone5"] = "🙎🏿‍♀️", + ["woman_pouting_dark_skin_tone"] = "🙎🏿‍♀️", + ["man_pouting"] = "🙎‍♂️", + ["man_pouting_tone1"] = "🙎🏻‍♂️", + ["man_pouting_light_skin_tone"] = "🙎🏻‍♂️", + ["man_pouting_tone2"] = "🙎🏼‍♂️", + ["man_pouting_medium_light_skin_tone"] = "🙎🏼‍♂️", + ["man_pouting_tone3"] = "🙎🏽‍♂️", + ["man_pouting_medium_skin_tone"] = "🙎🏽‍♂️", + ["man_pouting_tone4"] = "🙎🏾‍♂️", + ["man_pouting_medium_dark_skin_tone"] = "🙎🏾‍♂️", + ["man_pouting_tone5"] = "🙎🏿‍♂️", + ["man_pouting_dark_skin_tone"] = "🙎🏿‍♂️", + ["person_frowning"] = "🙍", + ["person_frowning_tone1"] = "🙍🏻", + ["person_frowning_tone2"] = "🙍🏼", + ["person_frowning_tone3"] = "🙍🏽", + ["person_frowning_tone4"] = "🙍🏾", + ["person_frowning_tone5"] = "🙍🏿", + ["woman_frowning"] = "🙍‍♀️", + ["woman_frowning_tone1"] = "🙍🏻‍♀️", + ["woman_frowning_light_skin_tone"] = "🙍🏻‍♀️", + ["woman_frowning_tone2"] = "🙍🏼‍♀️", + ["woman_frowning_medium_light_skin_tone"] = "🙍🏼‍♀️", + ["woman_frowning_tone3"] = "🙍🏽‍♀️", + ["woman_frowning_medium_skin_tone"] = "🙍🏽‍♀️", + ["woman_frowning_tone4"] = "🙍🏾‍♀️", + ["woman_frowning_medium_dark_skin_tone"] = "🙍🏾‍♀️", + ["woman_frowning_tone5"] = "🙍🏿‍♀️", + ["woman_frowning_dark_skin_tone"] = "🙍🏿‍♀️", + ["man_frowning"] = "🙍‍♂️", + ["man_frowning_tone1"] = "🙍🏻‍♂️", + ["man_frowning_light_skin_tone"] = "🙍🏻‍♂️", + ["man_frowning_tone2"] = "🙍🏼‍♂️", + ["man_frowning_medium_light_skin_tone"] = "🙍🏼‍♂️", + ["man_frowning_tone3"] = "🙍🏽‍♂️", + ["man_frowning_medium_skin_tone"] = "🙍🏽‍♂️", + ["man_frowning_tone4"] = "🙍🏾‍♂️", + ["man_frowning_medium_dark_skin_tone"] = "🙍🏾‍♂️", + ["man_frowning_tone5"] = "🙍🏿‍♂️", + ["man_frowning_dark_skin_tone"] = "🙍🏿‍♂️", + ["person_getting_haircut"] = "💇", + ["haircut"] = "💇", + ["person_getting_haircut_tone1"] = "💇🏻", + ["haircut_tone1"] = "💇🏻", + ["person_getting_haircut_tone2"] = "💇🏼", + ["haircut_tone2"] = "💇🏼", + ["person_getting_haircut_tone3"] = "💇🏽", + ["haircut_tone3"] = "💇🏽", + ["person_getting_haircut_tone4"] = "💇🏾", + ["haircut_tone4"] = "💇🏾", + ["person_getting_haircut_tone5"] = "💇🏿", + ["haircut_tone5"] = "💇🏿", + ["woman_getting_haircut"] = "💇‍♀️", + ["woman_getting_haircut_tone1"] = "💇🏻‍♀️", + ["woman_getting_haircut_light_skin_tone"] = "💇🏻‍♀️", + ["woman_getting_haircut_tone2"] = "💇🏼‍♀️", + ["woman_getting_haircut_medium_light_skin_tone"] = "💇🏼‍♀️", + ["woman_getting_haircut_tone3"] = "💇🏽‍♀️", + ["woman_getting_haircut_medium_skin_tone"] = "💇🏽‍♀️", + ["woman_getting_haircut_tone4"] = "💇🏾‍♀️", + ["woman_getting_haircut_medium_dark_skin_tone"] = "💇🏾‍♀️", + ["woman_getting_haircut_tone5"] = "💇🏿‍♀️", + ["woman_getting_haircut_dark_skin_tone"] = "💇🏿‍♀️", + ["man_getting_haircut"] = "💇‍♂️", + ["man_getting_haircut_tone1"] = "💇🏻‍♂️", + ["man_getting_haircut_light_skin_tone"] = "💇🏻‍♂️", + ["man_getting_haircut_tone2"] = "💇🏼‍♂️", + ["man_getting_haircut_medium_light_skin_tone"] = "💇🏼‍♂️", + ["man_getting_haircut_tone3"] = "💇🏽‍♂️", + ["man_getting_haircut_medium_skin_tone"] = "💇🏽‍♂️", + ["man_getting_haircut_tone4"] = "💇🏾‍♂️", + ["man_getting_haircut_medium_dark_skin_tone"] = "💇🏾‍♂️", + ["man_getting_haircut_tone5"] = "💇🏿‍♂️", + ["man_getting_haircut_dark_skin_tone"] = "💇🏿‍♂️", + ["person_getting_massage"] = "💆", + ["massage"] = "💆", + ["person_getting_massage_tone1"] = "💆🏻", + ["massage_tone1"] = "💆🏻", + ["person_getting_massage_tone2"] = "💆🏼", + ["massage_tone2"] = "💆🏼", + ["person_getting_massage_tone3"] = "💆🏽", + ["massage_tone3"] = "💆🏽", + ["person_getting_massage_tone4"] = "💆🏾", + ["massage_tone4"] = "💆🏾", + ["person_getting_massage_tone5"] = "💆🏿", + ["massage_tone5"] = "💆🏿", + ["woman_getting_face_massage"] = "💆‍♀️", + ["woman_getting_face_massage_tone1"] = "💆🏻‍♀️", + ["woman_getting_face_massage_light_skin_tone"] = "💆🏻‍♀️", + ["woman_getting_face_massage_tone2"] = "💆🏼‍♀️", + ["woman_getting_face_massage_medium_light_skin_tone"] = "💆🏼‍♀️", + ["woman_getting_face_massage_tone3"] = "💆🏽‍♀️", + ["woman_getting_face_massage_medium_skin_tone"] = "💆🏽‍♀️", + ["woman_getting_face_massage_tone4"] = "💆🏾‍♀️", + ["woman_getting_face_massage_medium_dark_skin_tone"] = "💆🏾‍♀️", + ["woman_getting_face_massage_tone5"] = "💆🏿‍♀️", + ["woman_getting_face_massage_dark_skin_tone"] = "💆🏿‍♀️", + ["man_getting_face_massage"] = "💆‍♂️", + ["man_getting_face_massage_tone1"] = "💆🏻‍♂️", + ["man_getting_face_massage_light_skin_tone"] = "💆🏻‍♂️", + ["man_getting_face_massage_tone2"] = "💆🏼‍♂️", + ["man_getting_face_massage_medium_light_skin_tone"] = "💆🏼‍♂️", + ["man_getting_face_massage_tone3"] = "💆🏽‍♂️", + ["man_getting_face_massage_medium_skin_tone"] = "💆🏽‍♂️", + ["man_getting_face_massage_tone4"] = "💆🏾‍♂️", + ["man_getting_face_massage_medium_dark_skin_tone"] = "💆🏾‍♂️", + ["man_getting_face_massage_tone5"] = "💆🏿‍♂️", + ["man_getting_face_massage_dark_skin_tone"] = "💆🏿‍♂️", + ["person_in_steamy_room"] = "🧖", + ["person_in_steamy_room_tone1"] = "🧖🏻", + ["person_in_steamy_room_light_skin_tone"] = "🧖🏻", + ["person_in_steamy_room_tone2"] = "🧖🏼", + ["person_in_steamy_room_medium_light_skin_tone"] = "🧖🏼", + ["person_in_steamy_room_tone3"] = "🧖🏽", + ["person_in_steamy_room_medium_skin_tone"] = "🧖🏽", + ["person_in_steamy_room_tone4"] = "🧖🏾", + ["person_in_steamy_room_medium_dark_skin_tone"] = "🧖🏾", + ["person_in_steamy_room_tone5"] = "🧖🏿", + ["person_in_steamy_room_dark_skin_tone"] = "🧖🏿", + ["woman_in_steamy_room"] = "🧖‍♀️", + ["woman_in_steamy_room_tone1"] = "🧖🏻‍♀️", + ["woman_in_steamy_room_light_skin_tone"] = "🧖🏻‍♀️", + ["woman_in_steamy_room_tone2"] = "🧖🏼‍♀️", + ["woman_in_steamy_room_medium_light_skin_tone"] = "🧖🏼‍♀️", + ["woman_in_steamy_room_tone3"] = "🧖🏽‍♀️", + ["woman_in_steamy_room_medium_skin_tone"] = "🧖🏽‍♀️", + ["woman_in_steamy_room_tone4"] = "🧖🏾‍♀️", + ["woman_in_steamy_room_medium_dark_skin_tone"] = "🧖🏾‍♀️", + ["woman_in_steamy_room_tone5"] = "🧖🏿‍♀️", + ["woman_in_steamy_room_dark_skin_tone"] = "🧖🏿‍♀️", + ["man_in_steamy_room"] = "🧖‍♂️", + ["man_in_steamy_room_tone1"] = "🧖🏻‍♂️", + ["man_in_steamy_room_light_skin_tone"] = "🧖🏻‍♂️", + ["man_in_steamy_room_tone2"] = "🧖🏼‍♂️", + ["man_in_steamy_room_medium_light_skin_tone"] = "🧖🏼‍♂️", + ["man_in_steamy_room_tone3"] = "🧖🏽‍♂️", + ["man_in_steamy_room_medium_skin_tone"] = "🧖🏽‍♂️", + ["man_in_steamy_room_tone4"] = "🧖🏾‍♂️", + ["man_in_steamy_room_medium_dark_skin_tone"] = "🧖🏾‍♂️", + ["man_in_steamy_room_tone5"] = "🧖🏿‍♂️", + ["man_in_steamy_room_dark_skin_tone"] = "🧖🏿‍♂️", + ["nail_care"] = "💅", + ["nail_care_tone1"] = "💅🏻", + ["nail_care_tone2"] = "💅🏼", + ["nail_care_tone3"] = "💅🏽", + ["nail_care_tone4"] = "💅🏾", + ["nail_care_tone5"] = "💅🏿", + ["selfie"] = "🤳", + ["selfie_tone1"] = "🤳🏻", + ["selfie_tone2"] = "🤳🏼", + ["selfie_tone3"] = "🤳🏽", + ["selfie_tone4"] = "🤳🏾", + ["selfie_tone5"] = "🤳🏿", + ["dancer"] = "💃", + ["dancer_tone1"] = "💃🏻", + ["dancer_tone2"] = "💃🏼", + ["dancer_tone3"] = "💃🏽", + ["dancer_tone4"] = "💃🏾", + ["dancer_tone5"] = "💃🏿", + ["man_dancing"] = "🕺", + ["male_dancer"] = "🕺", + ["man_dancing_tone1"] = "🕺🏻", + ["male_dancer_tone1"] = "🕺🏻", + ["man_dancing_tone2"] = "🕺🏼", + ["male_dancer_tone2"] = "🕺🏼", + ["man_dancing_tone3"] = "🕺🏽", + ["male_dancer_tone3"] = "🕺🏽", + ["man_dancing_tone5"] = "🕺🏿", + ["male_dancer_tone5"] = "🕺🏿", + ["man_dancing_tone4"] = "🕺🏾", + ["male_dancer_tone4"] = "🕺🏾", + ["people_with_bunny_ears_partying"] = "👯", + ["dancers"] = "👯", + ["women_with_bunny_ears_partying"] = "👯‍♀️", + ["men_with_bunny_ears_partying"] = "👯‍♂️", + ["levitate"] = "🕴️", + ["man_in_business_suit_levitating"] = "🕴️", + ["levitate_tone1"] = "🕴🏻", + ["man_in_business_suit_levitating_tone1"] = "🕴🏻", + ["man_in_business_suit_levitating_light_skin_tone"] = "🕴🏻", + ["levitate_tone2"] = "🕴🏼", + ["man_in_business_suit_levitating_tone2"] = "🕴🏼", + ["man_in_business_suit_levitating_medium_light_skin_tone"] = "🕴🏼", + ["levitate_tone3"] = "🕴🏽", + ["man_in_business_suit_levitating_tone3"] = "🕴🏽", + ["man_in_business_suit_levitating_medium_skin_tone"] = "🕴🏽", + ["levitate_tone4"] = "🕴🏾", + ["man_in_business_suit_levitating_tone4"] = "🕴🏾", + ["man_in_business_suit_levitating_medium_dark_skin_tone"] = "🕴🏾", + ["levitate_tone5"] = "🕴🏿", + ["man_in_business_suit_levitating_tone5"] = "🕴🏿", + ["man_in_business_suit_levitating_dark_skin_tone"] = "🕴🏿", + ["person_in_manual_wheelchair"] = "🧑‍🦽", + ["person_in_manual_wheelchair_tone1"] = "🧑🏻‍🦽", + ["person_in_manual_wheelchair_light_skin_tone"] = "🧑🏻‍🦽", + ["person_in_manual_wheelchair_tone2"] = "🧑🏼‍🦽", + ["person_in_manual_wheelchair_medium_light_skin_tone"] = "🧑🏼‍🦽", + ["person_in_manual_wheelchair_tone3"] = "🧑🏽‍🦽", + ["person_in_manual_wheelchair_medium_skin_tone"] = "🧑🏽‍🦽", + ["person_in_manual_wheelchair_tone4"] = "🧑🏾‍🦽", + ["person_in_manual_wheelchair_medium_dark_skin_tone"] = "🧑🏾‍🦽", + ["person_in_manual_wheelchair_tone5"] = "🧑🏿‍🦽", + ["person_in_manual_wheelchair_dark_skin_tone"] = "🧑🏿‍🦽", + ["woman_in_manual_wheelchair"] = "👩‍🦽", + ["woman_in_manual_wheelchair_tone1"] = "👩🏻‍🦽", + ["woman_in_manual_wheelchair_light_skin_tone"] = "👩🏻‍🦽", + ["woman_in_manual_wheelchair_tone2"] = "👩🏼‍🦽", + ["woman_in_manual_wheelchair_medium_light_skin_tone"] = "👩🏼‍🦽", + ["woman_in_manual_wheelchair_tone3"] = "👩🏽‍🦽", + ["woman_in_manual_wheelchair_medium_skin_tone"] = "👩🏽‍🦽", + ["woman_in_manual_wheelchair_tone4"] = "👩🏾‍🦽", + ["woman_in_manual_wheelchair_medium_dark_skin_tone"] = "👩🏾‍🦽", + ["woman_in_manual_wheelchair_tone5"] = "👩🏿‍🦽", + ["woman_in_manual_wheelchair_dark_skin_tone"] = "👩🏿‍🦽", + ["man_in_manual_wheelchair"] = "👨‍🦽", + ["man_in_manual_wheelchair_tone1"] = "👨🏻‍🦽", + ["man_in_manual_wheelchair_light_skin_tone"] = "👨🏻‍🦽", + ["man_in_manual_wheelchair_tone2"] = "👨🏼‍🦽", + ["man_in_manual_wheelchair_medium_light_skin_tone"] = "👨🏼‍🦽", + ["man_in_manual_wheelchair_tone3"] = "👨🏽‍🦽", + ["man_in_manual_wheelchair_medium_skin_tone"] = "👨🏽‍🦽", + ["man_in_manual_wheelchair_tone4"] = "👨🏾‍🦽", + ["man_in_manual_wheelchair_medium_dark_skin_tone"] = "👨🏾‍🦽", + ["man_in_manual_wheelchair_tone5"] = "👨🏿‍🦽", + ["man_in_manual_wheelchair_dark_skin_tone"] = "👨🏿‍🦽", + ["person_in_motorized_wheelchair"] = "🧑‍🦼", + ["person_in_motorized_wheelchair_tone1"] = "🧑🏻‍🦼", + ["person_in_motorized_wheelchair_light_skin_tone"] = "🧑🏻‍🦼", + ["person_in_motorized_wheelchair_tone2"] = "🧑🏼‍🦼", + ["person_in_motorized_wheelchair_medium_light_skin_tone"] = "🧑🏼‍🦼", + ["person_in_motorized_wheelchair_tone3"] = "🧑🏽‍🦼", + ["person_in_motorized_wheelchair_medium_skin_tone"] = "🧑🏽‍🦼", + ["person_in_motorized_wheelchair_tone4"] = "🧑🏾‍🦼", + ["person_in_motorized_wheelchair_medium_dark_skin_tone"] = "🧑🏾‍🦼", + ["person_in_motorized_wheelchair_tone5"] = "🧑🏿‍🦼", + ["person_in_motorized_wheelchair_dark_skin_tone"] = "🧑🏿‍🦼", + ["woman_in_motorized_wheelchair"] = "👩‍🦼", + ["woman_in_motorized_wheelchair_tone1"] = "👩🏻‍🦼", + ["woman_in_motorized_wheelchair_light_skin_tone"] = "👩🏻‍🦼", + ["woman_in_motorized_wheelchair_tone2"] = "👩🏼‍🦼", + ["woman_in_motorized_wheelchair_medium_light_skin_tone"] = "👩🏼‍🦼", + ["woman_in_motorized_wheelchair_tone3"] = "👩🏽‍🦼", + ["woman_in_motorized_wheelchair_medium_skin_tone"] = "👩🏽‍🦼", + ["woman_in_motorized_wheelchair_tone4"] = "👩🏾‍🦼", + ["woman_in_motorized_wheelchair_medium_dark_skin_tone"] = "👩🏾‍🦼", + ["woman_in_motorized_wheelchair_tone5"] = "👩🏿‍🦼", + ["woman_in_motorized_wheelchair_dark_skin_tone"] = "👩🏿‍🦼", + ["man_in_motorized_wheelchair"] = "👨‍🦼", + ["man_in_motorized_wheelchair_tone1"] = "👨🏻‍🦼", + ["man_in_motorized_wheelchair_light_skin_tone"] = "👨🏻‍🦼", + ["man_in_motorized_wheelchair_tone2"] = "👨🏼‍🦼", + ["man_in_motorized_wheelchair_medium_light_skin_tone"] = "👨🏼‍🦼", + ["man_in_motorized_wheelchair_tone3"] = "👨🏽‍🦼", + ["man_in_motorized_wheelchair_medium_skin_tone"] = "👨🏽‍🦼", + ["man_in_motorized_wheelchair_tone4"] = "👨🏾‍🦼", + ["man_in_motorized_wheelchair_medium_dark_skin_tone"] = "👨🏾‍🦼", + ["man_in_motorized_wheelchair_tone5"] = "👨🏿‍🦼", + ["man_in_motorized_wheelchair_dark_skin_tone"] = "👨🏿‍🦼", + ["person_walking"] = "🚶", + ["walking"] = "🚶", + ["person_walking_tone1"] = "🚶🏻", + ["walking_tone1"] = "🚶🏻", + ["person_walking_tone2"] = "🚶🏼", + ["walking_tone2"] = "🚶🏼", + ["person_walking_tone3"] = "🚶🏽", + ["walking_tone3"] = "🚶🏽", + ["person_walking_tone4"] = "🚶🏾", + ["walking_tone4"] = "🚶🏾", + ["person_walking_tone5"] = "🚶🏿", + ["walking_tone5"] = "🚶🏿", + ["woman_walking"] = "🚶‍♀️", + ["woman_walking_tone1"] = "🚶🏻‍♀️", + ["woman_walking_light_skin_tone"] = "🚶🏻‍♀️", + ["woman_walking_tone2"] = "🚶🏼‍♀️", + ["woman_walking_medium_light_skin_tone"] = "🚶🏼‍♀️", + ["woman_walking_tone3"] = "🚶🏽‍♀️", + ["woman_walking_medium_skin_tone"] = "🚶🏽‍♀️", + ["woman_walking_tone4"] = "🚶🏾‍♀️", + ["woman_walking_medium_dark_skin_tone"] = "🚶🏾‍♀️", + ["woman_walking_tone5"] = "🚶🏿‍♀️", + ["woman_walking_dark_skin_tone"] = "🚶🏿‍♀️", + ["man_walking"] = "🚶‍♂️", + ["man_walking_tone1"] = "🚶🏻‍♂️", + ["man_walking_light_skin_tone"] = "🚶🏻‍♂️", + ["man_walking_tone2"] = "🚶🏼‍♂️", + ["man_walking_medium_light_skin_tone"] = "🚶🏼‍♂️", + ["man_walking_tone3"] = "🚶🏽‍♂️", + ["man_walking_medium_skin_tone"] = "🚶🏽‍♂️", + ["man_walking_tone4"] = "🚶🏾‍♂️", + ["man_walking_medium_dark_skin_tone"] = "🚶🏾‍♂️", + ["man_walking_tone5"] = "🚶🏿‍♂️", + ["man_walking_dark_skin_tone"] = "🚶🏿‍♂️", + ["person_with_probing_cane"] = "🧑‍🦯", + ["person_with_probing_cane_tone1"] = "🧑🏻‍🦯", + ["person_with_probing_cane_light_skin_tone"] = "🧑🏻‍🦯", + ["person_with_probing_cane_tone2"] = "🧑🏼‍🦯", + ["person_with_probing_cane_medium_light_skin_tone"] = "🧑🏼‍🦯", + ["person_with_probing_cane_tone3"] = "🧑🏽‍🦯", + ["person_with_probing_cane_medium_skin_tone"] = "🧑🏽‍🦯", + ["person_with_probing_cane_tone4"] = "🧑🏾‍🦯", + ["person_with_probing_cane_medium_dark_skin_tone"] = "🧑🏾‍🦯", + ["person_with_probing_cane_tone5"] = "🧑🏿‍🦯", + ["person_with_probing_cane_dark_skin_tone"] = "🧑🏿‍🦯", + ["woman_with_probing_cane"] = "👩‍🦯", + ["woman_with_probing_cane_tone1"] = "👩🏻‍🦯", + ["woman_with_probing_cane_light_skin_tone"] = "👩🏻‍🦯", + ["woman_with_probing_cane_tone2"] = "👩🏼‍🦯", + ["woman_with_probing_cane_medium_light_skin_tone"] = "👩🏼‍🦯", + ["woman_with_probing_cane_tone3"] = "👩🏽‍🦯", + ["woman_with_probing_cane_medium_skin_tone"] = "👩🏽‍🦯", + ["woman_with_probing_cane_tone4"] = "👩🏾‍🦯", + ["woman_with_probing_cane_medium_dark_skin_tone"] = "👩🏾‍🦯", + ["woman_with_probing_cane_tone5"] = "👩🏿‍🦯", + ["woman_with_probing_cane_dark_skin_tone"] = "👩🏿‍🦯", + ["man_with_probing_cane"] = "👨‍🦯", + ["man_with_probing_cane_tone1"] = "👨🏻‍🦯", + ["man_with_probing_cane_light_skin_tone"] = "👨🏻‍🦯", + ["man_with_probing_cane_tone3"] = "👨🏽‍🦯", + ["man_with_probing_cane_medium_skin_tone"] = "👨🏽‍🦯", + ["man_with_probing_cane_tone2"] = "👨🏼‍🦯", + ["man_with_probing_cane_medium_light_skin_tone"] = "👨🏼‍🦯", + ["man_with_probing_cane_tone4"] = "👨🏾‍🦯", + ["man_with_probing_cane_medium_dark_skin_tone"] = "👨🏾‍🦯", + ["man_with_probing_cane_tone5"] = "👨🏿‍🦯", + ["man_with_probing_cane_dark_skin_tone"] = "👨🏿‍🦯", + ["person_kneeling"] = "🧎", + ["person_kneeling_tone1"] = "🧎🏻", + ["person_kneeling_light_skin_tone"] = "🧎🏻", + ["person_kneeling_tone2"] = "🧎🏼", + ["person_kneeling_medium_light_skin_tone"] = "🧎🏼", + ["person_kneeling_tone3"] = "🧎🏽", + ["person_kneeling_medium_skin_tone"] = "🧎🏽", + ["person_kneeling_tone4"] = "🧎🏾", + ["person_kneeling_medium_dark_skin_tone"] = "🧎🏾", + ["person_kneeling_tone5"] = "🧎🏿", + ["person_kneeling_dark_skin_tone"] = "🧎🏿", + ["woman_kneeling"] = "🧎‍♀️", + ["woman_kneeling_tone1"] = "🧎🏻‍♀️", + ["woman_kneeling_light_skin_tone"] = "🧎🏻‍♀️", + ["woman_kneeling_tone2"] = "🧎🏼‍♀️", + ["woman_kneeling_medium_light_skin_tone"] = "🧎🏼‍♀️", + ["woman_kneeling_tone3"] = "🧎🏽‍♀️", + ["woman_kneeling_medium_skin_tone"] = "🧎🏽‍♀️", + ["woman_kneeling_tone4"] = "🧎🏾‍♀️", + ["woman_kneeling_medium_dark_skin_tone"] = "🧎🏾‍♀️", + ["woman_kneeling_tone5"] = "🧎🏿‍♀️", + ["woman_kneeling_dark_skin_tone"] = "🧎🏿‍♀️", + ["man_kneeling"] = "🧎‍♂️", + ["man_kneeling_tone1"] = "🧎🏻‍♂️", + ["man_kneeling_light_skin_tone"] = "🧎🏻‍♂️", + ["man_kneeling_tone2"] = "🧎🏼‍♂️", + ["man_kneeling_medium_light_skin_tone"] = "🧎🏼‍♂️", + ["man_kneeling_tone3"] = "🧎🏽‍♂️", + ["man_kneeling_medium_skin_tone"] = "🧎🏽‍♂️", + ["man_kneeling_tone4"] = "🧎🏾‍♂️", + ["man_kneeling_medium_dark_skin_tone"] = "🧎🏾‍♂️", + ["man_kneeling_tone5"] = "🧎🏿‍♂️", + ["man_kneeling_dark_skin_tone"] = "🧎🏿‍♂️", + ["person_running"] = "🏃", + ["runner"] = "🏃", + ["person_running_tone1"] = "🏃🏻", + ["runner_tone1"] = "🏃🏻", + ["person_running_tone2"] = "🏃🏼", + ["runner_tone2"] = "🏃🏼", + ["person_running_tone3"] = "🏃🏽", + ["runner_tone3"] = "🏃🏽", + ["person_running_tone4"] = "🏃🏾", + ["runner_tone4"] = "🏃🏾", + ["person_running_tone5"] = "🏃🏿", + ["runner_tone5"] = "🏃🏿", + ["woman_running"] = "🏃‍♀️", + ["woman_running_tone1"] = "🏃🏻‍♀️", + ["woman_running_light_skin_tone"] = "🏃🏻‍♀️", + ["woman_running_tone2"] = "🏃🏼‍♀️", + ["woman_running_medium_light_skin_tone"] = "🏃🏼‍♀️", + ["woman_running_tone3"] = "🏃🏽‍♀️", + ["woman_running_medium_skin_tone"] = "🏃🏽‍♀️", + ["woman_running_tone4"] = "🏃🏾‍♀️", + ["woman_running_medium_dark_skin_tone"] = "🏃🏾‍♀️", + ["woman_running_tone5"] = "🏃🏿‍♀️", + ["woman_running_dark_skin_tone"] = "🏃🏿‍♀️", + ["man_running"] = "🏃‍♂️", + ["man_running_tone1"] = "🏃🏻‍♂️", + ["man_running_light_skin_tone"] = "🏃🏻‍♂️", + ["man_running_tone2"] = "🏃🏼‍♂️", + ["man_running_medium_light_skin_tone"] = "🏃🏼‍♂️", + ["man_running_tone3"] = "🏃🏽‍♂️", + ["man_running_medium_skin_tone"] = "🏃🏽‍♂️", + ["man_running_tone4"] = "🏃🏾‍♂️", + ["man_running_medium_dark_skin_tone"] = "🏃🏾‍♂️", + ["man_running_tone5"] = "🏃🏿‍♂️", + ["man_running_dark_skin_tone"] = "🏃🏿‍♂️", + ["person_standing"] = "🧍", + ["person_standing_tone1"] = "🧍🏻", + ["person_standing_light_skin_tone"] = "🧍🏻", + ["person_standing_tone2"] = "🧍🏼", + ["person_standing_medium_light_skin_tone"] = "🧍🏼", + ["person_standing_tone3"] = "🧍🏽", + ["person_standing_medium_skin_tone"] = "🧍🏽", + ["person_standing_tone4"] = "🧍🏾", + ["person_standing_medium_dark_skin_tone"] = "🧍🏾", + ["person_standing_tone5"] = "🧍🏿", + ["person_standing_dark_skin_tone"] = "🧍🏿", + ["woman_standing"] = "🧍‍♀️", + ["woman_standing_tone1"] = "🧍🏻‍♀️", + ["woman_standing_light_skin_tone"] = "🧍🏻‍♀️", + ["woman_standing_tone2"] = "🧍🏼‍♀️", + ["woman_standing_medium_light_skin_tone"] = "🧍🏼‍♀️", + ["woman_standing_tone3"] = "🧍🏽‍♀️", + ["woman_standing_medium_skin_tone"] = "🧍🏽‍♀️", + ["woman_standing_tone4"] = "🧍🏾‍♀️", + ["woman_standing_medium_dark_skin_tone"] = "🧍🏾‍♀️", + ["woman_standing_tone5"] = "🧍🏿‍♀️", + ["woman_standing_dark_skin_tone"] = "🧍🏿‍♀️", + ["man_standing"] = "🧍‍♂️", + ["man_standing_tone1"] = "🧍🏻‍♂️", + ["man_standing_light_skin_tone"] = "🧍🏻‍♂️", + ["man_standing_tone2"] = "🧍🏼‍♂️", + ["man_standing_medium_light_skin_tone"] = "🧍🏼‍♂️", + ["man_standing_tone3"] = "🧍🏽‍♂️", + ["man_standing_medium_skin_tone"] = "🧍🏽‍♂️", + ["man_standing_tone4"] = "🧍🏾‍♂️", + ["man_standing_medium_dark_skin_tone"] = "🧍🏾‍♂️", + ["man_standing_tone5"] = "🧍🏿‍♂️", + ["man_standing_dark_skin_tone"] = "🧍🏿‍♂️", + ["people_holding_hands"] = "🧑‍🤝‍🧑", + ["people_holding_hands_tone1"] = "🧑🏻‍🤝‍🧑🏻", + ["people_holding_hands_light_skin_tone"] = "🧑🏻‍🤝‍🧑🏻", + ["people_holding_hands_tone1_tone2"] = "🧑🏻‍🤝‍🧑🏼", + ["people_holding_hands_light_skin_tone_medium_light_skin_tone"] = "🧑🏻‍🤝‍🧑🏼", + ["people_holding_hands_tone1_tone3"] = "🧑🏻‍🤝‍🧑🏽", + ["people_holding_hands_light_skin_tone_medium_skin_tone"] = "🧑🏻‍🤝‍🧑🏽", + ["people_holding_hands_tone1_tone4"] = "🧑🏻‍🤝‍🧑🏾", + ["people_holding_hands_light_skin_tone_medium_dark_skin_tone"] = "🧑🏻‍🤝‍🧑🏾", + ["people_holding_hands_tone1_tone5"] = "🧑🏻‍🤝‍🧑🏿", + ["people_holding_hands_light_skin_tone_dark_skin_tone"] = "🧑🏻‍🤝‍🧑🏿", + ["people_holding_hands_tone2_tone1"] = "🧑🏼‍🤝‍🧑🏻", + ["people_holding_hands_medium_light_skin_tone_light_skin_tone"] = "🧑🏼‍🤝‍🧑🏻", + ["people_holding_hands_tone2"] = "🧑🏼‍🤝‍🧑🏼", + ["people_holding_hands_medium_light_skin_tone"] = "🧑🏼‍🤝‍🧑🏼", + ["people_holding_hands_tone2_tone3"] = "🧑🏼‍🤝‍🧑🏽", + ["people_holding_hands_medium_light_skin_tone_medium_skin_tone"] = "🧑🏼‍🤝‍🧑🏽", + ["people_holding_hands_tone2_tone4"] = "🧑🏼‍🤝‍🧑🏾", + ["people_holding_hands_medium_light_skin_tone_medium_dark_skin_tone"] = "🧑🏼‍🤝‍🧑🏾", + ["people_holding_hands_tone2_tone5"] = "🧑🏼‍🤝‍🧑🏿", + ["people_holding_hands_medium_light_skin_tone_dark_skin_tone"] = "🧑🏼‍🤝‍🧑🏿", + ["people_holding_hands_tone3_tone1"] = "🧑🏽‍🤝‍🧑🏻", + ["people_holding_hands_medium_skin_tone_light_skin_tone"] = "🧑🏽‍🤝‍🧑🏻", + ["people_holding_hands_tone3_tone2"] = "🧑🏽‍🤝‍🧑🏼", + ["people_holding_hands_medium_skin_tone_medium_light_skin_tone"] = "🧑🏽‍🤝‍🧑🏼", + ["people_holding_hands_tone3"] = "🧑🏽‍🤝‍🧑🏽", + ["people_holding_hands_medium_skin_tone"] = "🧑🏽‍🤝‍🧑🏽", + ["people_holding_hands_tone3_tone4"] = "🧑🏽‍🤝‍🧑🏾", + ["people_holding_hands_medium_skin_tone_medium_dark_skin_tone"] = "🧑🏽‍🤝‍🧑🏾", + ["people_holding_hands_tone3_tone5"] = "🧑🏽‍🤝‍🧑🏿", + ["people_holding_hands_medium_skin_tone_dark_skin_tone"] = "🧑🏽‍🤝‍🧑🏿", + ["people_holding_hands_tone4_tone1"] = "🧑🏾‍🤝‍🧑🏻", + ["people_holding_hands_medium_dark_skin_tone_light_skin_tone"] = "🧑🏾‍🤝‍🧑🏻", + ["people_holding_hands_tone4_tone2"] = "🧑🏾‍🤝‍🧑🏼", + ["people_holding_hands_medium_dark_skin_tone_medium_light_skin_tone"] = "🧑🏾‍🤝‍🧑🏼", + ["people_holding_hands_tone4_tone3"] = "🧑🏾‍🤝‍🧑🏽", + ["people_holding_hands_medium_dark_skin_tone_medium_skin_tone"] = "🧑🏾‍🤝‍🧑🏽", + ["people_holding_hands_tone4"] = "🧑🏾‍🤝‍🧑🏾", + ["people_holding_hands_medium_dark_skin_tone"] = "🧑🏾‍🤝‍🧑🏾", + ["people_holding_hands_tone4_tone5"] = "🧑🏾‍🤝‍🧑🏿", + ["people_holding_hands_medium_dark_skin_tone_dark_skin_tone"] = "🧑🏾‍🤝‍🧑🏿", + ["people_holding_hands_tone5_tone1"] = "🧑🏿‍🤝‍🧑🏻", + ["people_holding_hands_dark_skin_tone_light_skin_tone"] = "🧑🏿‍🤝‍🧑🏻", + ["people_holding_hands_tone5_tone2"] = "🧑🏿‍🤝‍🧑🏼", + ["people_holding_hands_dark_skin_tone_medium_light_skin_tone"] = "🧑🏿‍🤝‍🧑🏼", + ["people_holding_hands_tone5_tone3"] = "🧑🏿‍🤝‍🧑🏽", + ["people_holding_hands_dark_skin_tone_medium_skin_tone"] = "🧑🏿‍🤝‍🧑🏽", + ["people_holding_hands_tone5_tone4"] = "🧑🏿‍🤝‍🧑🏾", + ["people_holding_hands_dark_skin_tone_medium_dark_skin_tone"] = "🧑🏿‍🤝‍🧑🏾", + ["people_holding_hands_tone5"] = "🧑🏿‍🤝‍🧑🏿", + ["people_holding_hands_dark_skin_tone"] = "🧑🏿‍🤝‍🧑🏿", + ["couple"] = "👫", + ["woman_and_man_holding_hands_tone1"] = "👫🏻", + ["woman_and_man_holding_hands_light_skin_tone"] = "👫🏻", + ["woman_and_man_holding_hands_tone1_tone2"] = "👩🏻‍🤝‍👨🏼", + ["woman_and_man_holding_hands_light_skin_tone_medium_light_skin_tone"] = "👩🏻‍🤝‍👨🏼", + ["woman_and_man_holding_hands_tone1_tone3"] = "👩🏻‍🤝‍👨🏽", + ["woman_and_man_holding_hands_light_skin_tone_medium_skin_tone"] = "👩🏻‍🤝‍👨🏽", + ["woman_and_man_holding_hands_tone1_tone4"] = "👩🏻‍🤝‍👨🏾", + ["woman_and_man_holding_hands_light_skin_tone_medium_dark_skin_tone"] = "👩🏻‍🤝‍👨🏾", + ["woman_and_man_holding_hands_tone1_tone5"] = "👩🏻‍🤝‍👨🏿", + ["woman_and_man_holding_hands_light_skin_tone_dark_skin_tone"] = "👩🏻‍🤝‍👨🏿", + ["woman_and_man_holding_hands_tone2_tone1"] = "👩🏼‍🤝‍👨🏻", + ["woman_and_man_holding_hands_medium_light_skin_tone_light_skin_tone"] = "👩🏼‍🤝‍👨🏻", + ["woman_and_man_holding_hands_tone2"] = "👫🏼", + ["woman_and_man_holding_hands_medium_light_skin_tone"] = "👫🏼", + ["woman_and_man_holding_hands_tone2_tone3"] = "👩🏼‍🤝‍👨🏽", + ["woman_and_man_holding_hands_medium_light_skin_tone_medium_skin_tone"] = "👩🏼‍🤝‍👨🏽", + ["woman_and_man_holding_hands_tone2_tone4"] = "👩🏼‍🤝‍👨🏾", + ["woman_and_man_holding_hands_medium_light_skin_tone_medium_dark_skin_tone"] = + "👩🏼‍🤝‍👨🏾", + ["woman_and_man_holding_hands_tone2_tone5"] = "👩🏼‍🤝‍👨🏿", + ["woman_and_man_holding_hands_medium_light_skin_tone_dark_skin_tone"] = "👩🏼‍🤝‍👨🏿", + ["woman_and_man_holding_hands_tone3_tone1"] = "👩🏽‍🤝‍👨🏻", + ["woman_and_man_holding_hands_medium_skin_tone_light_skin_tone"] = "👩🏽‍🤝‍👨🏻", + ["woman_and_man_holding_hands_tone3_tone2"] = "👩🏽‍🤝‍👨🏼", + ["woman_and_man_holding_hands_medium_skin_tone_medium_light_skin_tone"] = "👩🏽‍🤝‍👨🏼", + ["woman_and_man_holding_hands_tone3"] = "👫🏽", + ["woman_and_man_holding_hands_medium_skin_tone"] = "👫🏽", + ["woman_and_man_holding_hands_tone3_tone4"] = "👩🏽‍🤝‍👨🏾", + ["woman_and_man_holding_hands_medium_skin_tone_medium_dark_skin_tone"] = "👩🏽‍🤝‍👨🏾", + ["woman_and_man_holding_hands_tone3_tone5"] = "👩🏽‍🤝‍👨🏿", + ["woman_and_man_holding_hands_medium_skin_tone_dark_skin_tone"] = "👩🏽‍🤝‍👨🏿", + ["woman_and_man_holding_hands_tone4_tone1"] = "👩🏾‍🤝‍👨🏻", + ["woman_and_man_holding_hands_medium_dark_skin_tone_light_skin_tone"] = "👩🏾‍🤝‍👨🏻", + ["woman_and_man_holding_hands_tone4_tone2"] = "👩🏾‍🤝‍👨🏼", + ["woman_and_man_holding_hands_medium_dark_skin_tone_medium_light_skin_tone"] = + "👩🏾‍🤝‍👨🏼", + ["woman_and_man_holding_hands_tone4_tone3"] = "👩🏾‍🤝‍👨🏽", + ["woman_and_man_holding_hands_medium_dark_skin_tone_medium_skin_tone"] = "👩🏾‍🤝‍👨🏽", + ["woman_and_man_holding_hands_tone4"] = "👫🏾", + ["woman_and_man_holding_hands_medium_dark_skin_tone"] = "👫🏾", + ["woman_and_man_holding_hands_tone4_tone5"] = "👩🏾‍🤝‍👨🏿", + ["woman_and_man_holding_hands_medium_dark_skin_tone_dark_skin_tone"] = "👩🏾‍🤝‍👨🏿", + ["woman_and_man_holding_hands_tone5_tone1"] = "👩🏿‍🤝‍👨🏻", + ["woman_and_man_holding_hands_dark_skin_tone_light_skin_tone"] = "👩🏿‍🤝‍👨🏻", + ["woman_and_man_holding_hands_tone5_tone2"] = "👩🏿‍🤝‍👨🏼", + ["woman_and_man_holding_hands_dark_skin_tone_medium_light_skin_tone"] = "👩🏿‍🤝‍👨🏼", + ["woman_and_man_holding_hands_tone5_tone3"] = "👩🏿‍🤝‍👨🏽", + ["woman_and_man_holding_hands_dark_skin_tone_medium_skin_tone"] = "👩🏿‍🤝‍👨🏽", + ["woman_and_man_holding_hands_tone5_tone4"] = "👩🏿‍🤝‍👨🏾", + ["woman_and_man_holding_hands_dark_skin_tone_medium_dark_skin_tone"] = "👩🏿‍🤝‍👨🏾", + ["woman_and_man_holding_hands_tone5"] = "👫🏿", + ["woman_and_man_holding_hands_dark_skin_tone"] = "👫🏿", + ["two_women_holding_hands"] = "👭", + ["women_holding_hands_tone1"] = "👭🏻", + ["women_holding_hands_light_skin_tone"] = "👭🏻", + ["women_holding_hands_tone1_tone2"] = "👩🏻‍🤝‍👩🏼", + ["women_holding_hands_light_skin_tone_medium_light_skin_tone"] = "👩🏻‍🤝‍👩🏼", + ["women_holding_hands_tone1_tone3"] = "👩🏻‍🤝‍👩🏽", + ["women_holding_hands_light_skin_tone_medium_skin_tone"] = "👩🏻‍🤝‍👩🏽", + ["women_holding_hands_tone1_tone4"] = "👩🏻‍🤝‍👩🏾", + ["women_holding_hands_light_skin_tone_medium_dark_skin_tone"] = "👩🏻‍🤝‍👩🏾", + ["women_holding_hands_tone1_tone5"] = "👩🏻‍🤝‍👩🏿", + ["women_holding_hands_light_skin_tone_dark_skin_tone"] = "👩🏻‍🤝‍👩🏿", + ["women_holding_hands_tone2_tone1"] = "👩🏼‍🤝‍👩🏻", + ["women_holding_hands_medium_light_skin_tone_light_skin_tone"] = "👩🏼‍🤝‍👩🏻", + ["women_holding_hands_tone2"] = "👭🏼", + ["women_holding_hands_medium_light_skin_tone"] = "👭🏼", + ["women_holding_hands_tone2_tone3"] = "👩🏼‍🤝‍👩🏽", + ["women_holding_hands_medium_light_skin_tone_medium_skin_tone"] = "👩🏼‍🤝‍👩🏽", + ["women_holding_hands_tone2_tone4"] = "👩🏼‍🤝‍👩🏾", + ["women_holding_hands_medium_light_skin_tone_medium_dark_skin_tone"] = "👩🏼‍🤝‍👩🏾", + ["women_holding_hands_tone2_tone5"] = "👩🏼‍🤝‍👩🏿", + ["women_holding_hands_medium_light_skin_tone_dark_skin_tone"] = "👩🏼‍🤝‍👩🏿", + ["women_holding_hands_tone3_tone1"] = "👩🏽‍🤝‍👩🏻", + ["women_holding_hands_medium_skin_tone_light_skin_tone"] = "👩🏽‍🤝‍👩🏻", + ["women_holding_hands_tone3_tone2"] = "👩🏽‍🤝‍👩🏼", + ["women_holding_hands_medium_skin_tone_medium_light_skin_tone"] = "👩🏽‍🤝‍👩🏼", + ["women_holding_hands_tone3"] = "👭🏽", + ["women_holding_hands_medium_skin_tone"] = "👭🏽", + ["women_holding_hands_tone3_tone4"] = "👩🏽‍🤝‍👩🏾", + ["women_holding_hands_medium_skin_tone_medium_dark_skin_tone"] = "👩🏽‍🤝‍👩🏾", + ["women_holding_hands_tone3_tone5"] = "👩🏽‍🤝‍👩🏿", + ["women_holding_hands_medium_skin_tone_dark_skin_tone"] = "👩🏽‍🤝‍👩🏿", + ["women_holding_hands_tone4_tone1"] = "👩🏾‍🤝‍👩🏻", + ["women_holding_hands_medium_dark_skin_tone_light_skin_tone"] = "👩🏾‍🤝‍👩🏻", + ["women_holding_hands_tone4_tone2"] = "👩🏾‍🤝‍👩🏼", + ["women_holding_hands_medium_dark_skin_tone_medium_light_skin_tone"] = "👩🏾‍🤝‍👩🏼", + ["women_holding_hands_tone4_tone3"] = "👩🏾‍🤝‍👩🏽", + ["women_holding_hands_medium_dark_skin_tone_medium_skin_tone"] = "👩🏾‍🤝‍👩🏽", + ["women_holding_hands_tone4"] = "👭🏾", + ["women_holding_hands_medium_dark_skin_tone"] = "👭🏾", + ["women_holding_hands_tone4_tone5"] = "👩🏾‍🤝‍👩🏿", + ["women_holding_hands_medium_dark_skin_tone_dark_skin_tone"] = "👩🏾‍🤝‍👩🏿", + ["women_holding_hands_tone5_tone1"] = "👩🏿‍🤝‍👩🏻", + ["women_holding_hands_dark_skin_tone_light_skin_tone"] = "👩🏿‍🤝‍👩🏻", + ["women_holding_hands_tone5_tone2"] = "👩🏿‍🤝‍👩🏼", + ["women_holding_hands_dark_skin_tone_medium_light_skin_tone"] = "👩🏿‍🤝‍👩🏼", + ["women_holding_hands_tone5_tone3"] = "👩🏿‍🤝‍👩🏽", + ["women_holding_hands_dark_skin_tone_medium_skin_tone"] = "👩🏿‍🤝‍👩🏽", + ["women_holding_hands_tone5_tone4"] = "👩🏿‍🤝‍👩🏾", + ["women_holding_hands_dark_skin_tone_medium_dark_skin_tone"] = "👩🏿‍🤝‍👩🏾", + ["women_holding_hands_tone5"] = "👭🏿", + ["women_holding_hands_dark_skin_tone"] = "👭🏿", + ["two_men_holding_hands"] = "👬", + ["men_holding_hands_tone1"] = "👬🏻", + ["men_holding_hands_light_skin_tone"] = "👬🏻", + ["men_holding_hands_tone1_tone2"] = "👨🏻‍🤝‍👨🏼", + ["men_holding_hands_light_skin_tone_medium_light_skin_tone"] = "👨🏻‍🤝‍👨🏼", + ["men_holding_hands_tone1_tone3"] = "👨🏻‍🤝‍👨🏽", + ["men_holding_hands_light_skin_tone_medium_skin_tone"] = "👨🏻‍🤝‍👨🏽", + ["men_holding_hands_tone1_tone4"] = "👨🏻‍🤝‍👨🏾", + ["men_holding_hands_light_skin_tone_medium_dark_skin_tone"] = "👨🏻‍🤝‍👨🏾", + ["men_holding_hands_tone1_tone5"] = "👨🏻‍🤝‍👨🏿", + ["men_holding_hands_light_skin_tone_dark_skin_tone"] = "👨🏻‍🤝‍👨🏿", + ["men_holding_hands_tone2_tone1"] = "👨🏼‍🤝‍👨🏻", + ["men_holding_hands_medium_light_skin_tone_light_skin_tone"] = "👨🏼‍🤝‍👨🏻", + ["men_holding_hands_tone2"] = "👬🏼", + ["men_holding_hands_medium_light_skin_tone"] = "👬🏼", + ["men_holding_hands_tone2_tone3"] = "👨🏼‍🤝‍👨🏽", + ["men_holding_hands_medium_light_skin_tone_medium_skin_tone"] = "👨🏼‍🤝‍👨🏽", + ["men_holding_hands_tone2_tone4"] = "👨🏼‍🤝‍👨🏾", + ["men_holding_hands_medium_light_skin_tone_medium_dark_skin_tone"] = "👨🏼‍🤝‍👨🏾", + ["men_holding_hands_tone2_tone5"] = "👨🏼‍🤝‍👨🏿", + ["men_holding_hands_medium_light_skin_tone_dark_skin_tone"] = "👨🏼‍🤝‍👨🏿", + ["men_holding_hands_tone3_tone1"] = "👨🏽‍🤝‍👨🏻", + ["men_holding_hands_medium_skin_tone_light_skin_tone"] = "👨🏽‍🤝‍👨🏻", + ["men_holding_hands_tone3_tone2"] = "👨🏽‍🤝‍👨🏼", + ["men_holding_hands_medium_skin_tone_medium_light_skin_tone"] = "👨🏽‍🤝‍👨🏼", + ["men_holding_hands_tone3"] = "👬🏽", + ["men_holding_hands_medium_skin_tone"] = "👬🏽", + ["men_holding_hands_tone3_tone4"] = "👨🏽‍🤝‍👨🏾", + ["men_holding_hands_medium_skin_tone_medium_dark_skin_tone"] = "👨🏽‍🤝‍👨🏾", + ["men_holding_hands_tone3_tone5"] = "👨🏽‍🤝‍👨🏿", + ["men_holding_hands_medium_skin_tone_dark_skin_tone"] = "👨🏽‍🤝‍👨🏿", + ["men_holding_hands_tone4_tone1"] = "👨🏾‍🤝‍👨🏻", + ["men_holding_hands_medium_dark_skin_tone_light_skin_tone"] = "👨🏾‍🤝‍👨🏻", + ["men_holding_hands_tone4_tone2"] = "👨🏾‍🤝‍👨🏼", + ["men_holding_hands_medium_dark_skin_tone_medium_light_skin_tone"] = "👨🏾‍🤝‍👨🏼", + ["men_holding_hands_tone4_tone3"] = "👨🏾‍🤝‍👨🏽", + ["men_holding_hands_medium_dark_skin_tone_medium_skin_tone"] = "👨🏾‍🤝‍👨🏽", + ["men_holding_hands_tone4"] = "👬🏾", + ["men_holding_hands_medium_dark_skin_tone"] = "👬🏾", + ["men_holding_hands_tone4_tone5"] = "👨🏾‍🤝‍👨🏿", + ["men_holding_hands_medium_dark_skin_tone_dark_skin_tone"] = "👨🏾‍🤝‍👨🏿", + ["men_holding_hands_tone5_tone1"] = "👨🏿‍🤝‍👨🏻", + ["men_holding_hands_dark_skin_tone_light_skin_tone"] = "👨🏿‍🤝‍👨🏻", + ["men_holding_hands_tone5_tone2"] = "👨🏿‍🤝‍👨🏼", + ["men_holding_hands_dark_skin_tone_medium_light_skin_tone"] = "👨🏿‍🤝‍👨🏼", + ["men_holding_hands_tone5_tone3"] = "👨🏿‍🤝‍👨🏽", + ["men_holding_hands_dark_skin_tone_medium_skin_tone"] = "👨🏿‍🤝‍👨🏽", + ["men_holding_hands_tone5_tone4"] = "👨🏿‍🤝‍👨🏾", + ["men_holding_hands_dark_skin_tone_medium_dark_skin_tone"] = "👨🏿‍🤝‍👨🏾", + ["men_holding_hands_tone5"] = "👬🏿", + ["men_holding_hands_dark_skin_tone"] = "👬🏿", + ["couple_with_heart"] = "💑", + ["couple_with_heart_tone1"] = "💑🏻", + ["couple_with_heart_light_skin_tone"] = "💑🏻", + ["couple_with_heart_person_person_tone1_tone2"] = "🧑🏻‍❤️‍🧑🏼", + ["couple_with_heart_person_person_light_skin_tone_medium_light_skin_tone"] = "🧑🏻‍❤️‍🧑🏼", + ["couple_with_heart_person_person_tone1_tone3"] = "🧑🏻‍❤️‍🧑🏽", + ["couple_with_heart_person_person_light_skin_tone_medium_skin_tone"] = "🧑🏻‍❤️‍🧑🏽", + ["couple_with_heart_person_person_tone1_tone4"] = "🧑🏻‍❤️‍🧑🏾", + ["couple_with_heart_person_person_light_skin_tone_medium_dark_skin_tone"] = "🧑🏻‍❤️‍🧑🏾", + ["couple_with_heart_person_person_tone1_tone5"] = "🧑🏻‍❤️‍🧑🏿", + ["couple_with_heart_person_person_light_skin_tone_dark_skin_tone"] = "🧑🏻‍❤️‍🧑🏿", + ["couple_with_heart_person_person_tone2_tone1"] = "🧑🏼‍❤️‍🧑🏻", + ["couple_with_heart_person_person_medium_light_skin_tone_light_skin_tone"] = "🧑🏼‍❤️‍🧑🏻", + ["couple_with_heart_tone2"] = "💑🏼", + ["couple_with_heart_medium_light_skin_tone"] = "💑🏼", + ["couple_with_heart_person_person_tone2_tone3"] = "🧑🏼‍❤️‍🧑🏽", + ["couple_with_heart_person_person_medium_light_skin_tone_medium_skin_tone"] = + "🧑🏼‍❤️‍🧑🏽", + ["couple_with_heart_person_person_tone2_tone4"] = "🧑🏼‍❤️‍🧑🏾", + ["couple_with_heart_person_person_medium_light_skin_tone_medium_dark_skin_tone"] = + "🧑🏼‍❤️‍🧑🏾", + ["couple_with_heart_person_person_tone2_tone5"] = "🧑🏼‍❤️‍🧑🏿", + ["couple_with_heart_person_person_medium_light_skin_tone_dark_skin_tone"] = "🧑🏼‍❤️‍🧑🏿", + ["couple_with_heart_person_person_tone3_tone1"] = "🧑🏽‍❤️‍🧑🏻", + ["couple_with_heart_person_person_medium_skin_tone_light_skin_tone"] = "🧑🏽‍❤️‍🧑🏻", + ["couple_with_heart_person_person_tone3_tone2"] = "🧑🏽‍❤️‍🧑🏼", + ["couple_with_heart_person_person_medium_skin_tone_medium_light_skin_tone"] = + "🧑🏽‍❤️‍🧑🏼", + ["couple_with_heart_tone3"] = "💑🏽", + ["couple_with_heart_medium_skin_tone"] = "💑🏽", + ["couple_with_heart_person_person_tone3_tone4"] = "🧑🏽‍❤️‍🧑🏾", + ["couple_with_heart_person_person_medium_skin_tone_medium_dark_skin_tone"] = "🧑🏽‍❤️‍🧑🏾", + ["couple_with_heart_person_person_tone3_tone5"] = "🧑🏽‍❤️‍🧑🏿", + ["couple_with_heart_person_person_medium_skin_tone_dark_skin_tone"] = "🧑🏽‍❤️‍🧑🏿", + ["couple_with_heart_person_person_tone4_tone1"] = "🧑🏾‍❤️‍🧑🏻", + ["couple_with_heart_person_person_medium_dark_skin_tone_light_skin_tone"] = "🧑🏾‍❤️‍🧑🏻", + ["couple_with_heart_person_person_tone4_tone2"] = "🧑🏾‍❤️‍🧑🏼", + ["couple_with_heart_person_person_medium_dark_skin_tone_medium_light_skin_tone"] = + "🧑🏾‍❤️‍🧑🏼", + ["couple_with_heart_person_person_tone4_tone3"] = "🧑🏾‍❤️‍🧑🏽", + ["couple_with_heart_person_person_medium_dark_skin_tone_medium_skin_tone"] = "🧑🏾‍❤️‍🧑🏽", + ["couple_with_heart_tone4"] = "💑🏾", + ["couple_with_heart_medium_dark_skin_tone"] = "💑🏾", + ["couple_with_heart_person_person_tone4_tone5"] = "🧑🏾‍❤️‍🧑🏿", + ["couple_with_heart_person_person_medium_dark_skin_tone_dark_skin_tone"] = "🧑🏾‍❤️‍🧑🏿", + ["couple_with_heart_person_person_tone5_tone1"] = "🧑🏿‍❤️‍🧑🏻", + ["couple_with_heart_person_person_dark_skin_tone_light_skin_tone"] = "🧑🏿‍❤️‍🧑🏻", + ["couple_with_heart_person_person_tone5_tone2"] = "🧑🏿‍❤️‍🧑🏼", + ["couple_with_heart_person_person_dark_skin_tone_medium_light_skin_tone"] = "🧑🏿‍❤️‍🧑🏼", + ["couple_with_heart_person_person_tone5_tone3"] = "🧑🏿‍❤️‍🧑🏽", + ["couple_with_heart_person_person_dark_skin_tone_medium_skin_tone"] = "🧑🏿‍❤️‍🧑🏽", + ["couple_with_heart_person_person_tone5_tone4"] = "🧑🏿‍❤️‍🧑🏾", + ["couple_with_heart_person_person_dark_skin_tone_medium_dark_skin_tone"] = "🧑🏿‍❤️‍🧑🏾", + ["couple_with_heart_tone5"] = "💑🏿", + ["couple_with_heart_dark_skin_tone"] = "💑🏿", + ["couple_with_heart_woman_man"] = "👩‍❤️‍👨", + ["couple_with_heart_woman_man_tone1"] = "👩🏻‍❤️‍👨🏻", + ["couple_with_heart_woman_man_light_skin_tone"] = "👩🏻‍❤️‍👨🏻", + ["couple_with_heart_woman_man_tone1_tone2"] = "👩🏻‍❤️‍👨🏼", + ["couple_with_heart_woman_man_light_skin_tone_medium_light_skin_tone"] = "👩🏻‍❤️‍👨🏼", + ["couple_with_heart_woman_man_tone1_tone3"] = "👩🏻‍❤️‍👨🏽", + ["couple_with_heart_woman_man_light_skin_tone_medium_skin_tone"] = "👩🏻‍❤️‍👨🏽", + ["couple_with_heart_woman_man_tone1_tone4"] = "👩🏻‍❤️‍👨🏾", + ["couple_with_heart_woman_man_light_skin_tone_medium_dark_skin_tone"] = "👩🏻‍❤️‍👨🏾", + ["couple_with_heart_woman_man_tone1_tone5"] = "👩🏻‍❤️‍👨🏿", + ["couple_with_heart_woman_man_light_skin_tone_dark_skin_tone"] = "👩🏻‍❤️‍👨🏿", + ["couple_with_heart_woman_man_tone2_tone1"] = "👩🏼‍❤️‍👨🏻", + ["couple_with_heart_woman_man_medium_light_skin_tone_light_skin_tone"] = "👩🏼‍❤️‍👨🏻", + ["couple_with_heart_woman_man_tone2"] = "👩🏼‍❤️‍👨🏼", + ["couple_with_heart_woman_man_medium_light_skin_tone"] = "👩🏼‍❤️‍👨🏼", + ["couple_with_heart_woman_man_tone2_tone3"] = "👩🏼‍❤️‍👨🏽", + ["couple_with_heart_woman_man_medium_light_skin_tone_medium_skin_tone"] = "👩🏼‍❤️‍👨🏽", + ["couple_with_heart_woman_man_tone2_tone4"] = "👩🏼‍❤️‍👨🏾", + ["couple_with_heart_woman_man_medium_light_skin_tone_medium_dark_skin_tone"] = + "👩🏼‍❤️‍👨🏾", + ["couple_with_heart_woman_man_tone2_tone5"] = "👩🏼‍❤️‍👨🏿", + ["couple_with_heart_woman_man_medium_light_skin_tone_dark_skin_tone"] = "👩🏼‍❤️‍👨🏿", + ["couple_with_heart_woman_man_tone3_tone1"] = "👩🏽‍❤️‍👨🏻", + ["couple_with_heart_woman_man_medium_skin_tone_light_skin_tone"] = "👩🏽‍❤️‍👨🏻", + ["couple_with_heart_woman_man_tone3_tone2"] = "👩🏽‍❤️‍👨🏼", + ["couple_with_heart_woman_man_medium_skin_tone_medium_light_skin_tone"] = "👩🏽‍❤️‍👨🏼", + ["couple_with_heart_woman_man_tone3"] = "👩🏽‍❤️‍👨🏽", + ["couple_with_heart_woman_man_medium_skin_tone"] = "👩🏽‍❤️‍👨🏽", + ["couple_with_heart_woman_man_tone3_tone4"] = "👩🏽‍❤️‍👨🏾", + ["couple_with_heart_woman_man_medium_skin_tone_medium_dark_skin_tone"] = "👩🏽‍❤️‍👨🏾", + ["couple_with_heart_woman_man_tone3_tone5"] = "👩🏽‍❤️‍👨🏿", + ["couple_with_heart_woman_man_medium_skin_tone_dark_skin_tone"] = "👩🏽‍❤️‍👨🏿", + ["couple_with_heart_woman_man_tone4_tone1"] = "👩🏾‍❤️‍👨🏻", + ["couple_with_heart_woman_man_medium_dark_skin_tone_light_skin_tone"] = "👩🏾‍❤️‍👨🏻", + ["couple_with_heart_woman_man_tone4_tone2"] = "👩🏾‍❤️‍👨🏼", + ["couple_with_heart_woman_man_medium_dark_skin_tone_medium_light_skin_tone"] = + "👩🏾‍❤️‍👨🏼", + ["couple_with_heart_woman_man_tone4_tone3"] = "👩🏾‍❤️‍👨🏽", + ["couple_with_heart_woman_man_medium_dark_skin_tone_medium_skin_tone"] = "👩🏾‍❤️‍👨🏽", + ["couple_with_heart_woman_man_tone4"] = "👩🏾‍❤️‍👨🏾", + ["couple_with_heart_woman_man_medium_dark_skin_tone"] = "👩🏾‍❤️‍👨🏾", + ["couple_with_heart_woman_man_tone4_tone5"] = "👩🏾‍❤️‍👨🏿", + ["couple_with_heart_woman_man_medium_dark_skin_tone_dark_skin_tone"] = "👩🏾‍❤️‍👨🏿", + ["couple_with_heart_woman_man_tone5_tone1"] = "👩🏿‍❤️‍👨🏻", + ["couple_with_heart_woman_man_dark_skin_tone_light_skin_tone"] = "👩🏿‍❤️‍👨🏻", + ["couple_with_heart_woman_man_tone5_tone2"] = "👩🏿‍❤️‍👨🏼", + ["couple_with_heart_woman_man_dark_skin_tone_medium_light_skin_tone"] = "👩🏿‍❤️‍👨🏼", + ["couple_with_heart_woman_man_tone5_tone3"] = "👩🏿‍❤️‍👨🏽", + ["couple_with_heart_woman_man_dark_skin_tone_medium_skin_tone"] = "👩🏿‍❤️‍👨🏽", + ["couple_with_heart_woman_man_tone5_tone4"] = "👩🏿‍❤️‍👨🏾", + ["couple_with_heart_woman_man_dark_skin_tone_medium_dark_skin_tone"] = "👩🏿‍❤️‍👨🏾", + ["couple_with_heart_woman_man_tone5"] = "👩🏿‍❤️‍👨🏿", + ["couple_with_heart_woman_man_dark_skin_tone"] = "👩🏿‍❤️‍👨🏿", + ["couple_ww"] = "👩‍❤️‍👩", + ["couple_with_heart_ww"] = "👩‍❤️‍👩", + ["couple_with_heart_woman_woman_tone1"] = "👩🏻‍❤️‍👩🏻", + ["couple_with_heart_woman_woman_light_skin_tone"] = "👩🏻‍❤️‍👩🏻", + ["couple_with_heart_woman_woman_tone1_tone2"] = "👩🏻‍❤️‍👩🏼", + ["couple_with_heart_woman_woman_light_skin_tone_medium_light_skin_tone"] = "👩🏻‍❤️‍👩🏼", + ["couple_with_heart_woman_woman_tone1_tone3"] = "👩🏻‍❤️‍👩🏽", + ["couple_with_heart_woman_woman_light_skin_tone_medium_skin_tone"] = "👩🏻‍❤️‍👩🏽", + ["couple_with_heart_woman_woman_tone1_tone4"] = "👩🏻‍❤️‍👩🏾", + ["couple_with_heart_woman_woman_light_skin_tone_medium_dark_skin_tone"] = "👩🏻‍❤️‍👩🏾", + ["couple_with_heart_woman_woman_tone1_tone5"] = "👩🏻‍❤️‍👩🏿", + ["couple_with_heart_woman_woman_light_skin_tone_dark_skin_tone"] = "👩🏻‍❤️‍👩🏿", + ["couple_with_heart_woman_woman_tone2_tone1"] = "👩🏼‍❤️‍👩🏻", + ["couple_with_heart_woman_woman_medium_light_skin_tone_light_skin_tone"] = "👩🏼‍❤️‍👩🏻", + ["couple_with_heart_woman_woman_tone2"] = "👩🏼‍❤️‍👩🏼", + ["couple_with_heart_woman_woman_medium_light_skin_tone"] = "👩🏼‍❤️‍👩🏼", + ["couple_with_heart_woman_woman_tone2_tone3"] = "👩🏼‍❤️‍👩🏽", + ["couple_with_heart_woman_woman_medium_light_skin_tone_medium_skin_tone"] = "👩🏼‍❤️‍👩🏽", + ["couple_with_heart_woman_woman_tone2_tone4"] = "👩🏼‍❤️‍👩🏾", + ["couple_with_heart_woman_woman_medium_light_skin_tone_medium_dark_skin_tone"] = + "👩🏼‍❤️‍👩🏾", + ["couple_with_heart_woman_woman_tone2_tone5"] = "👩🏼‍❤️‍👩🏿", + ["couple_with_heart_woman_woman_medium_light_skin_tone_dark_skin_tone"] = "👩🏼‍❤️‍👩🏿", + ["couple_with_heart_woman_woman_tone3_tone1"] = "👩🏽‍❤️‍👩🏻", + ["couple_with_heart_woman_woman_medium_skin_tone_light_skin_tone"] = "👩🏽‍❤️‍👩🏻", + ["couple_with_heart_woman_woman_tone3_tone2"] = "👩🏽‍❤️‍👩🏼", + ["couple_with_heart_woman_woman_medium_skin_tone_medium_light_skin_tone"] = "👩🏽‍❤️‍👩🏼", + ["couple_with_heart_woman_woman_tone3"] = "👩🏽‍❤️‍👩🏽", + ["couple_with_heart_woman_woman_medium_skin_tone"] = "👩🏽‍❤️‍👩🏽", + ["couple_with_heart_woman_woman_tone3_tone4"] = "👩🏽‍❤️‍👩🏾", + ["couple_with_heart_woman_woman_medium_skin_tone_medium_dark_skin_tone"] = "👩🏽‍❤️‍👩🏾", + ["couple_with_heart_woman_woman_tone3_tone5"] = "👩🏽‍❤️‍👩🏿", + ["couple_with_heart_woman_woman_medium_skin_tone_dark_skin_tone"] = "👩🏽‍❤️‍👩🏿", + ["couple_with_heart_woman_woman_tone4_tone1"] = "👩🏾‍❤️‍👩🏻", + ["couple_with_heart_woman_woman_medium_dark_skin_tone_light_skin_tone"] = "👩🏾‍❤️‍👩🏻", + ["couple_with_heart_woman_woman_tone4_tone2"] = "👩🏾‍❤️‍👩🏼", + ["couple_with_heart_woman_woman_medium_dark_skin_tone_medium_light_skin_tone"] = + "👩🏾‍❤️‍👩🏼", + ["couple_with_heart_woman_woman_tone4_tone3"] = "👩🏾‍❤️‍👩🏽", + ["couple_with_heart_woman_woman_medium_dark_skin_tone_medium_skin_tone"] = "👩🏾‍❤️‍👩🏽", + ["couple_with_heart_woman_woman_tone4"] = "👩🏾‍❤️‍👩🏾", + ["couple_with_heart_woman_woman_medium_dark_skin_tone"] = "👩🏾‍❤️‍👩🏾", + ["couple_with_heart_woman_woman_tone4_tone5"] = "👩🏾‍❤️‍👩🏿", + ["couple_with_heart_woman_woman_medium_dark_skin_tone_dark_skin_tone"] = "👩🏾‍❤️‍👩🏿", + ["couple_with_heart_woman_woman_tone5_tone1"] = "👩🏿‍❤️‍👩🏻", + ["couple_with_heart_woman_woman_dark_skin_tone_light_skin_tone"] = "👩🏿‍❤️‍👩🏻", + ["couple_with_heart_woman_woman_tone5_tone2"] = "👩🏿‍❤️‍👩🏼", + ["couple_with_heart_woman_woman_dark_skin_tone_medium_light_skin_tone"] = "👩🏿‍❤️‍👩🏼", + ["couple_with_heart_woman_woman_tone5_tone3"] = "👩🏿‍❤️‍👩🏽", + ["couple_with_heart_woman_woman_dark_skin_tone_medium_skin_tone"] = "👩🏿‍❤️‍👩🏽", + ["couple_with_heart_woman_woman_tone5_tone4"] = "👩🏿‍❤️‍👩🏾", + ["couple_with_heart_woman_woman_dark_skin_tone_medium_dark_skin_tone"] = "👩🏿‍❤️‍👩🏾", + ["couple_with_heart_woman_woman_tone5"] = "👩🏿‍❤️‍👩🏿", + ["couple_with_heart_woman_woman_dark_skin_tone"] = "👩🏿‍❤️‍👩🏿", + ["couple_mm"] = "👨‍❤️‍👨", + ["couple_with_heart_mm"] = "👨‍❤️‍👨", + ["couple_with_heart_man_man_tone1"] = "👨🏻‍❤️‍👨🏻", + ["couple_with_heart_man_man_light_skin_tone"] = "👨🏻‍❤️‍👨🏻", + ["couple_with_heart_man_man_tone1_tone2"] = "👨🏻‍❤️‍👨🏼", + ["couple_with_heart_man_man_light_skin_tone_medium_light_skin_tone"] = "👨🏻‍❤️‍👨🏼", + ["couple_with_heart_man_man_tone1_tone3"] = "👨🏻‍❤️‍👨🏽", + ["couple_with_heart_man_man_light_skin_tone_medium_skin_tone"] = "👨🏻‍❤️‍👨🏽", + ["couple_with_heart_man_man_tone1_tone4"] = "👨🏻‍❤️‍👨🏾", + ["couple_with_heart_man_man_light_skin_tone_medium_dark_skin_tone"] = "👨🏻‍❤️‍👨🏾", + ["couple_with_heart_man_man_tone1_tone5"] = "👨🏻‍❤️‍👨🏿", + ["couple_with_heart_man_man_light_skin_tone_dark_skin_tone"] = "👨🏻‍❤️‍👨🏿", + ["couple_with_heart_man_man_tone2_tone1"] = "👨🏼‍❤️‍👨🏻", + ["couple_with_heart_man_man_medium_light_skin_tone_light_skin_tone"] = "👨🏼‍❤️‍👨🏻", + ["couple_with_heart_man_man_tone2"] = "👨🏼‍❤️‍👨🏼", + ["couple_with_heart_man_man_medium_light_skin_tone"] = "👨🏼‍❤️‍👨🏼", + ["couple_with_heart_man_man_tone2_tone3"] = "👨🏼‍❤️‍👨🏽", + ["couple_with_heart_man_man_medium_light_skin_tone_medium_skin_tone"] = "👨🏼‍❤️‍👨🏽", + ["couple_with_heart_man_man_tone2_tone4"] = "👨🏼‍❤️‍👨🏾", + ["couple_with_heart_man_man_medium_light_skin_tone_medium_dark_skin_tone"] = "👨🏼‍❤️‍👨🏾", + ["couple_with_heart_man_man_tone2_tone5"] = "👨🏼‍❤️‍👨🏿", + ["couple_with_heart_man_man_medium_light_skin_tone_dark_skin_tone"] = "👨🏼‍❤️‍👨🏿", + ["couple_with_heart_man_man_tone3_tone1"] = "👨🏽‍❤️‍👨🏻", + ["couple_with_heart_man_man_medium_skin_tone_light_skin_tone"] = "👨🏽‍❤️‍👨🏻", + ["couple_with_heart_man_man_tone3_tone2"] = "👨🏽‍❤️‍👨🏼", + ["couple_with_heart_man_man_medium_skin_tone_medium_light_skin_tone"] = "👨🏽‍❤️‍👨🏼", + ["couple_with_heart_man_man_tone3"] = "👨🏽‍❤️‍👨🏽", + ["couple_with_heart_man_man_medium_skin_tone"] = "👨🏽‍❤️‍👨🏽", + ["couple_with_heart_man_man_tone3_tone4"] = "👨🏽‍❤️‍👨🏾", + ["couple_with_heart_man_man_medium_skin_tone_medium_dark_skin_tone"] = "👨🏽‍❤️‍👨🏾", + ["couple_with_heart_man_man_tone3_tone5"] = "👨🏽‍❤️‍👨🏿", + ["couple_with_heart_man_man_medium_skin_tone_dark_skin_tone"] = "👨🏽‍❤️‍👨🏿", + ["couple_with_heart_man_man_tone4_tone1"] = "👨🏾‍❤️‍👨🏻", + ["couple_with_heart_man_man_medium_dark_skin_tone_light_skin_tone"] = "👨🏾‍❤️‍👨🏻", + ["couple_with_heart_man_man_tone4_tone2"] = "👨🏾‍❤️‍👨🏼", + ["couple_with_heart_man_man_medium_dark_skin_tone_medium_light_skin_tone"] = "👨🏾‍❤️‍👨🏼", + ["couple_with_heart_man_man_tone4_tone3"] = "👨🏾‍❤️‍👨🏽", + ["couple_with_heart_man_man_medium_dark_skin_tone_medium_skin_tone"] = "👨🏾‍❤️‍👨🏽", + ["couple_with_heart_man_man_tone4"] = "👨🏾‍❤️‍👨🏾", + ["couple_with_heart_man_man_medium_dark_skin_tone"] = "👨🏾‍❤️‍👨🏾", + ["couple_with_heart_man_man_tone4_tone5"] = "👨🏾‍❤️‍👨🏿", + ["couple_with_heart_man_man_medium_dark_skin_tone_dark_skin_tone"] = "👨🏾‍❤️‍👨🏿", + ["couple_with_heart_man_man_tone5_tone1"] = "👨🏿‍❤️‍👨🏻", + ["couple_with_heart_man_man_dark_skin_tone_light_skin_tone"] = "👨🏿‍❤️‍👨🏻", + ["couple_with_heart_man_man_tone5_tone2"] = "👨🏿‍❤️‍👨🏼", + ["couple_with_heart_man_man_dark_skin_tone_medium_light_skin_tone"] = "👨🏿‍❤️‍👨🏼", + ["couple_with_heart_man_man_tone5_tone3"] = "👨🏿‍❤️‍👨🏽", + ["couple_with_heart_man_man_dark_skin_tone_medium_skin_tone"] = "👨🏿‍❤️‍👨🏽", + ["couple_with_heart_man_man_tone5_tone4"] = "👨🏿‍❤️‍👨🏾", + ["couple_with_heart_man_man_dark_skin_tone_medium_dark_skin_tone"] = "👨🏿‍❤️‍👨🏾", + ["couple_with_heart_man_man_tone5"] = "👨🏿‍❤️‍👨🏿", + ["couple_with_heart_man_man_dark_skin_tone"] = "👨🏿‍❤️‍👨🏿", + ["couplekiss"] = "💏", + ["kiss_person_person_tone5_tone4"] = "🧑🏿‍❤️‍💋‍🧑🏾", + ["kiss_person_person_dark_skin_tone_medium_dark_skin_tone"] = "🧑🏿‍❤️‍💋‍🧑🏾", + ["kiss_tone1"] = "💏🏻", + ["kiss_light_skin_tone"] = "💏🏻", + ["kiss_person_person_tone1_tone2"] = "🧑🏻‍❤️‍💋‍🧑🏼", + ["kiss_person_person_light_skin_tone_medium_light_skin_tone"] = "🧑🏻‍❤️‍💋‍🧑🏼", + ["kiss_person_person_tone1_tone3"] = "🧑🏻‍❤️‍💋‍🧑🏽", + ["kiss_person_person_light_skin_tone_medium_skin_tone"] = "🧑🏻‍❤️‍💋‍🧑🏽", + ["kiss_person_person_tone1_tone4"] = "🧑🏻‍❤️‍💋‍🧑🏾", + ["kiss_person_person_light_skin_tone_medium_dark_skin_tone"] = "🧑🏻‍❤️‍💋‍🧑🏾", + ["kiss_person_person_tone1_tone5"] = "🧑🏻‍❤️‍💋‍🧑🏿", + ["kiss_person_person_light_skin_tone_dark_skin_tone"] = "🧑🏻‍❤️‍💋‍🧑🏿", + ["kiss_person_person_tone2_tone1"] = "🧑🏼‍❤️‍💋‍🧑🏻", + ["kiss_person_person_medium_light_skin_tone_light_skin_tone"] = "🧑🏼‍❤️‍💋‍🧑🏻", + ["kiss_tone2"] = "💏🏼", + ["kiss_medium_light_skin_tone"] = "💏🏼", + ["kiss_person_person_tone2_tone3"] = "🧑🏼‍❤️‍💋‍🧑🏽", + ["kiss_person_person_medium_light_skin_tone_medium_skin_tone"] = "🧑🏼‍❤️‍💋‍🧑🏽", + ["kiss_person_person_tone2_tone4"] = "🧑🏼‍❤️‍💋‍🧑🏾", + ["kiss_person_person_medium_light_skin_tone_medium_dark_skin_tone"] = "🧑🏼‍❤️‍💋‍🧑🏾", + ["kiss_person_person_tone2_tone5"] = "🧑🏼‍❤️‍💋‍🧑🏿", + ["kiss_person_person_medium_light_skin_tone_dark_skin_tone"] = "🧑🏼‍❤️‍💋‍🧑🏿", + ["kiss_person_person_tone3_tone1"] = "🧑🏽‍❤️‍💋‍🧑🏻", + ["kiss_person_person_medium_skin_tone_light_skin_tone"] = "🧑🏽‍❤️‍💋‍🧑🏻", + ["kiss_person_person_tone3_tone2"] = "🧑🏽‍❤️‍💋‍🧑🏼", + ["kiss_person_person_medium_skin_tone_medium_light_skin_tone"] = "🧑🏽‍❤️‍💋‍🧑🏼", + ["kiss_tone3"] = "💏🏽", + ["kiss_medium_skin_tone"] = "💏🏽", + ["kiss_person_person_tone3_tone4"] = "🧑🏽‍❤️‍💋‍🧑🏾", + ["kiss_person_person_medium_skin_tone_medium_dark_skin_tone"] = "🧑🏽‍❤️‍💋‍🧑🏾", + ["kiss_person_person_tone3_tone5"] = "🧑🏽‍❤️‍💋‍🧑🏿", + ["kiss_person_person_medium_skin_tone_dark_skin_tone"] = "🧑🏽‍❤️‍💋‍🧑🏿", + ["kiss_person_person_tone4_tone1"] = "🧑🏾‍❤️‍💋‍🧑🏻", + ["kiss_person_person_medium_dark_skin_tone_light_skin_tone"] = "🧑🏾‍❤️‍💋‍🧑🏻", + ["kiss_person_person_tone4_tone2"] = "🧑🏾‍❤️‍💋‍🧑🏼", + ["kiss_person_person_medium_dark_skin_tone_medium_light_skin_tone"] = "🧑🏾‍❤️‍💋‍🧑🏼", + ["kiss_person_person_tone4_tone3"] = "🧑🏾‍❤️‍💋‍🧑🏽", + ["kiss_person_person_medium_dark_skin_tone_medium_skin_tone"] = "🧑🏾‍❤️‍💋‍🧑🏽", + ["kiss_tone4"] = "💏🏾", + ["kiss_medium_dark_skin_tone"] = "💏🏾", + ["kiss_person_person_tone4_tone5"] = "🧑🏾‍❤️‍💋‍🧑🏿", + ["kiss_person_person_medium_dark_skin_tone_dark_skin_tone"] = "🧑🏾‍❤️‍💋‍🧑🏿", + ["kiss_person_person_tone5_tone1"] = "🧑🏿‍❤️‍💋‍🧑🏻", + ["kiss_person_person_dark_skin_tone_light_skin_tone"] = "🧑🏿‍❤️‍💋‍🧑🏻", + ["kiss_person_person_tone5_tone2"] = "🧑🏿‍❤️‍💋‍🧑🏼", + ["kiss_person_person_dark_skin_tone_medium_light_skin_tone"] = "🧑🏿‍❤️‍💋‍🧑🏼", + ["kiss_person_person_tone5_tone3"] = "🧑🏿‍❤️‍💋‍🧑🏽", + ["kiss_person_person_dark_skin_tone_medium_skin_tone"] = "🧑🏿‍❤️‍💋‍🧑🏽", + ["kiss_tone5"] = "💏🏿", + ["kiss_dark_skin_tone"] = "💏🏿", + ["kiss_woman_man"] = "👩‍❤️‍💋‍👨", + ["kiss_woman_man_tone1"] = "👩🏻‍❤️‍💋‍👨🏻", + ["kiss_woman_man_light_skin_tone"] = "👩🏻‍❤️‍💋‍👨🏻", + ["kiss_woman_man_tone1_tone2"] = "👩🏻‍❤️‍💋‍👨🏼", + ["kiss_woman_man_light_skin_tone_medium_light_skin_tone"] = "👩🏻‍❤️‍💋‍👨🏼", + ["kiss_woman_man_tone1_tone3"] = "👩🏻‍❤️‍💋‍👨🏽", + ["kiss_woman_man_light_skin_tone_medium_skin_tone"] = "👩🏻‍❤️‍💋‍👨🏽", + ["kiss_woman_man_tone1_tone4"] = "👩🏻‍❤️‍💋‍👨🏾", + ["kiss_woman_man_light_skin_tone_medium_dark_skin_tone"] = "👩🏻‍❤️‍💋‍👨🏾", + ["kiss_woman_man_tone1_tone5"] = "👩🏻‍❤️‍💋‍👨🏿", + ["kiss_woman_man_light_skin_tone_dark_skin_tone"] = "👩🏻‍❤️‍💋‍👨🏿", + ["kiss_woman_man_tone2_tone1"] = "👩🏼‍❤️‍💋‍👨🏻", + ["kiss_woman_man_medium_light_skin_tone_light_skin_tone"] = "👩🏼‍❤️‍💋‍👨🏻", + ["kiss_woman_man_tone2"] = "👩🏼‍❤️‍💋‍👨🏼", + ["kiss_woman_man_medium_light_skin_tone"] = "👩🏼‍❤️‍💋‍👨🏼", + ["kiss_woman_man_tone2_tone3"] = "👩🏼‍❤️‍💋‍👨🏽", + ["kiss_woman_man_medium_light_skin_tone_medium_skin_tone"] = "👩🏼‍❤️‍💋‍👨🏽", + ["kiss_woman_man_tone2_tone4"] = "👩🏼‍❤️‍💋‍👨🏾", + ["kiss_woman_man_medium_light_skin_tone_medium_dark_skin_tone"] = "👩🏼‍❤️‍💋‍👨🏾", + ["kiss_woman_man_tone2_tone5"] = "👩🏼‍❤️‍💋‍👨🏿", + ["kiss_woman_man_medium_light_skin_tone_dark_skin_tone"] = "👩🏼‍❤️‍💋‍👨🏿", + ["kiss_woman_man_tone3_tone1"] = "👩🏽‍❤️‍💋‍👨🏻", + ["kiss_woman_man_medium_skin_tone_light_skin_tone"] = "👩🏽‍❤️‍💋‍👨🏻", + ["kiss_woman_man_tone3_tone2"] = "👩🏽‍❤️‍💋‍👨🏼", + ["kiss_woman_man_medium_skin_tone_medium_light_skin_tone"] = "👩🏽‍❤️‍💋‍👨🏼", + ["kiss_woman_man_tone3"] = "👩🏽‍❤️‍💋‍👨🏽", + ["kiss_woman_man_medium_skin_tone"] = "👩🏽‍❤️‍💋‍👨🏽", + ["kiss_woman_man_tone3_tone4"] = "👩🏽‍❤️‍💋‍👨🏾", + ["kiss_woman_man_medium_skin_tone_medium_dark_skin_tone"] = "👩🏽‍❤️‍💋‍👨🏾", + ["kiss_woman_man_tone3_tone5"] = "👩🏽‍❤️‍💋‍👨🏿", + ["kiss_woman_man_medium_skin_tone_dark_skin_tone"] = "👩🏽‍❤️‍💋‍👨🏿", + ["kiss_woman_man_tone4_tone1"] = "👩🏾‍❤️‍💋‍👨🏻", + ["kiss_woman_man_medium_dark_skin_tone_light_skin_tone"] = "👩🏾‍❤️‍💋‍👨🏻", + ["kiss_woman_man_tone4_tone2"] = "👩🏾‍❤️‍💋‍👨🏼", + ["kiss_woman_man_medium_dark_skin_tone_medium_light_skin_tone"] = "👩🏾‍❤️‍💋‍👨🏼", + ["kiss_woman_man_tone4_tone3"] = "👩🏾‍❤️‍💋‍👨🏽", + ["kiss_woman_man_medium_dark_skin_tone_medium_skin_tone"] = "👩🏾‍❤️‍💋‍👨🏽", + ["kiss_woman_man_tone4"] = "👩🏾‍❤️‍💋‍👨🏾", + ["kiss_woman_man_medium_dark_skin_tone"] = "👩🏾‍❤️‍💋‍👨🏾", + ["kiss_woman_man_tone4_tone5"] = "👩🏾‍❤️‍💋‍👨🏿", + ["kiss_woman_man_medium_dark_skin_tone_dark_skin_tone"] = "👩🏾‍❤️‍💋‍👨🏿", + ["kiss_woman_man_tone5_tone1"] = "👩🏿‍❤️‍💋‍👨🏻", + ["kiss_woman_man_dark_skin_tone_light_skin_tone"] = "👩🏿‍❤️‍💋‍👨🏻", + ["kiss_woman_man_tone5_tone2"] = "👩🏿‍❤️‍💋‍👨🏼", + ["kiss_woman_man_dark_skin_tone_medium_light_skin_tone"] = "👩🏿‍❤️‍💋‍👨🏼", + ["kiss_woman_man_tone5_tone3"] = "👩🏿‍❤️‍💋‍👨🏽", + ["kiss_woman_man_dark_skin_tone_medium_skin_tone"] = "👩🏿‍❤️‍💋‍👨🏽", + ["kiss_woman_man_tone5_tone4"] = "👩🏿‍❤️‍💋‍👨🏾", + ["kiss_woman_man_dark_skin_tone_medium_dark_skin_tone"] = "👩🏿‍❤️‍💋‍👨🏾", + ["kiss_woman_man_tone5"] = "👩🏿‍❤️‍💋‍👨🏿", + ["kiss_woman_man_dark_skin_tone"] = "👩🏿‍❤️‍💋‍👨🏿", + ["kiss_ww"] = "👩‍❤️‍💋‍👩", + ["couplekiss_ww"] = "👩‍❤️‍💋‍👩", + ["kiss_woman_woman_tone1"] = "👩🏻‍❤️‍💋‍👩🏻", + ["kiss_woman_woman_light_skin_tone"] = "👩🏻‍❤️‍💋‍👩🏻", + ["kiss_woman_woman_tone1_tone2"] = "👩🏻‍❤️‍💋‍👩🏼", + ["kiss_woman_woman_light_skin_tone_medium_light_skin_tone"] = "👩🏻‍❤️‍💋‍👩🏼", + ["kiss_woman_woman_tone1_tone3"] = "👩🏻‍❤️‍💋‍👩🏽", + ["kiss_woman_woman_light_skin_tone_medium_skin_tone"] = "👩🏻‍❤️‍💋‍👩🏽", + ["kiss_woman_woman_tone1_tone4"] = "👩🏻‍❤️‍💋‍👩🏾", + ["kiss_woman_woman_light_skin_tone_medium_dark_skin_tone"] = "👩🏻‍❤️‍💋‍👩🏾", + ["kiss_woman_woman_tone1_tone5"] = "👩🏻‍❤️‍💋‍👩🏿", + ["kiss_woman_woman_light_skin_tone_dark_skin_tone"] = "👩🏻‍❤️‍💋‍👩🏿", + ["kiss_woman_woman_tone2_tone1"] = "👩🏼‍❤️‍💋‍👩🏻", + ["kiss_woman_woman_medium_light_skin_tone_light_skin_tone"] = "👩🏼‍❤️‍💋‍👩🏻", + ["kiss_woman_woman_tone2"] = "👩🏼‍❤️‍💋‍👩🏼", + ["kiss_woman_woman_medium_light_skin_tone"] = "👩🏼‍❤️‍💋‍👩🏼", + ["kiss_woman_woman_tone2_tone3"] = "👩🏼‍❤️‍💋‍👩🏽", + ["kiss_woman_woman_medium_light_skin_tone_medium_skin_tone"] = "👩🏼‍❤️‍💋‍👩🏽", + ["kiss_woman_woman_tone2_tone4"] = "👩🏼‍❤️‍💋‍👩🏾", + ["kiss_woman_woman_medium_light_skin_tone_medium_dark_skin_tone"] = "👩🏼‍❤️‍💋‍👩🏾", + ["kiss_woman_woman_tone2_tone5"] = "👩🏼‍❤️‍💋‍👩🏿", + ["kiss_woman_woman_medium_light_skin_tone_dark_skin_tone"] = "👩🏼‍❤️‍💋‍👩🏿", + ["kiss_woman_woman_tone3_tone1"] = "👩🏽‍❤️‍💋‍👩🏻", + ["kiss_woman_woman_medium_skin_tone_light_skin_tone"] = "👩🏽‍❤️‍💋‍👩🏻", + ["kiss_woman_woman_tone3_tone2"] = "👩🏽‍❤️‍💋‍👩🏼", + ["kiss_woman_woman_medium_skin_tone_medium_light_skin_tone"] = "👩🏽‍❤️‍💋‍👩🏼", + ["kiss_woman_woman_tone3"] = "👩🏽‍❤️‍💋‍👩🏽", + ["kiss_woman_woman_medium_skin_tone"] = "👩🏽‍❤️‍💋‍👩🏽", + ["kiss_woman_woman_tone3_tone4"] = "👩🏽‍❤️‍💋‍👩🏾", + ["kiss_woman_woman_medium_skin_tone_medium_dark_skin_tone"] = "👩🏽‍❤️‍💋‍👩🏾", + ["kiss_woman_woman_tone3_tone5"] = "👩🏽‍❤️‍💋‍👩🏿", + ["kiss_woman_woman_medium_skin_tone_dark_skin_tone"] = "👩🏽‍❤️‍💋‍👩🏿", + ["kiss_woman_woman_tone4_tone1"] = "👩🏾‍❤️‍💋‍👩🏻", + ["kiss_woman_woman_medium_dark_skin_tone_light_skin_tone"] = "👩🏾‍❤️‍💋‍👩🏻", + ["kiss_woman_woman_tone4_tone2"] = "👩🏾‍❤️‍💋‍👩🏼", + ["kiss_woman_woman_medium_dark_skin_tone_medium_light_skin_tone"] = "👩🏾‍❤️‍💋‍👩🏼", + ["kiss_woman_woman_tone4_tone3"] = "👩🏾‍❤️‍💋‍👩🏽", + ["kiss_woman_woman_medium_dark_skin_tone_medium_skin_tone"] = "👩🏾‍❤️‍💋‍👩🏽", + ["kiss_woman_woman_tone4"] = "👩🏾‍❤️‍💋‍👩🏾", + ["kiss_woman_woman_medium_dark_skin_tone"] = "👩🏾‍❤️‍💋‍👩🏾", + ["kiss_woman_woman_tone4_tone5"] = "👩🏾‍❤️‍💋‍👩🏿", + ["kiss_woman_woman_medium_dark_skin_tone_dark_skin_tone"] = "👩🏾‍❤️‍💋‍👩🏿", + ["kiss_woman_woman_tone5_tone1"] = "👩🏿‍❤️‍💋‍👩🏻", + ["kiss_woman_woman_dark_skin_tone_light_skin_tone"] = "👩🏿‍❤️‍💋‍👩🏻", + ["kiss_woman_woman_tone5_tone2"] = "👩🏿‍❤️‍💋‍👩🏼", + ["kiss_woman_woman_dark_skin_tone_medium_light_skin_tone"] = "👩🏿‍❤️‍💋‍👩🏼", + ["kiss_woman_woman_tone5_tone3"] = "👩🏿‍❤️‍💋‍👩🏽", + ["kiss_woman_woman_dark_skin_tone_medium_skin_tone"] = "👩🏿‍❤️‍💋‍👩🏽", + ["kiss_woman_woman_tone5_tone4"] = "👩🏿‍❤️‍💋‍👩🏾", + ["kiss_woman_woman_dark_skin_tone_medium_dark_skin_tone"] = "👩🏿‍❤️‍💋‍👩🏾", + ["kiss_woman_woman_tone5"] = "👩🏿‍❤️‍💋‍👩🏿", + ["kiss_woman_woman_dark_skin_tone"] = "👩🏿‍❤️‍💋‍👩🏿", + ["kiss_mm"] = "👨‍❤️‍💋‍👨", + ["couplekiss_mm"] = "👨‍❤️‍💋‍👨", + ["kiss_man_man_tone1"] = "👨🏻‍❤️‍💋‍👨🏻", + ["kiss_man_man_light_skin_tone"] = "👨🏻‍❤️‍💋‍👨🏻", + ["kiss_man_man_tone1_tone2"] = "👨🏻‍❤️‍💋‍👨🏼", + ["kiss_man_man_light_skin_tone_medium_light_skin_tone"] = "👨🏻‍❤️‍💋‍👨🏼", + ["kiss_man_man_tone1_tone3"] = "👨🏻‍❤️‍💋‍👨🏽", + ["kiss_man_man_light_skin_tone_medium_skin_tone"] = "👨🏻‍❤️‍💋‍👨🏽", + ["kiss_man_man_tone1_tone4"] = "👨🏻‍❤️‍💋‍👨🏾", + ["kiss_man_man_light_skin_tone_medium_dark_skin_tone"] = "👨🏻‍❤️‍💋‍👨🏾", + ["kiss_man_man_tone1_tone5"] = "👨🏻‍❤️‍💋‍👨🏿", + ["kiss_man_man_light_skin_tone_dark_skin_tone"] = "👨🏻‍❤️‍💋‍👨🏿", + ["kiss_man_man_tone2_tone1"] = "👨🏼‍❤️‍💋‍👨🏻", + ["kiss_man_man_medium_light_skin_tone_light_skin_tone"] = "👨🏼‍❤️‍💋‍👨🏻", + ["kiss_man_man_tone2"] = "👨🏼‍❤️‍💋‍👨🏼", + ["kiss_man_man_medium_light_skin_tone"] = "👨🏼‍❤️‍💋‍👨🏼", + ["kiss_man_man_tone2_tone3"] = "👨🏼‍❤️‍💋‍👨🏽", + ["kiss_man_man_medium_light_skin_tone_medium_skin_tone"] = "👨🏼‍❤️‍💋‍👨🏽", + ["kiss_man_man_tone2_tone4"] = "👨🏼‍❤️‍💋‍👨🏾", + ["kiss_man_man_medium_light_skin_tone_medium_dark_skin_tone"] = "👨🏼‍❤️‍💋‍👨🏾", + ["kiss_man_man_tone2_tone5"] = "👨🏼‍❤️‍💋‍👨🏿", + ["kiss_man_man_medium_light_skin_tone_dark_skin_tone"] = "👨🏼‍❤️‍💋‍👨🏿", + ["kiss_man_man_tone3_tone1"] = "👨🏽‍❤️‍💋‍👨🏻", + ["kiss_man_man_medium_skin_tone_light_skin_tone"] = "👨🏽‍❤️‍💋‍👨🏻", + ["kiss_man_man_tone3_tone2"] = "👨🏽‍❤️‍💋‍👨🏼", + ["kiss_man_man_medium_skin_tone_medium_light_skin_tone"] = "👨🏽‍❤️‍💋‍👨🏼", + ["kiss_man_man_tone3"] = "👨🏽‍❤️‍💋‍👨🏽", + ["kiss_man_man_medium_skin_tone"] = "👨🏽‍❤️‍💋‍👨🏽", + ["kiss_man_man_tone3_tone4"] = "👨🏽‍❤️‍💋‍👨🏾", + ["kiss_man_man_medium_skin_tone_medium_dark_skin_tone"] = "👨🏽‍❤️‍💋‍👨🏾", + ["kiss_man_man_tone3_tone5"] = "👨🏽‍❤️‍💋‍👨🏿", + ["kiss_man_man_medium_skin_tone_dark_skin_tone"] = "👨🏽‍❤️‍💋‍👨🏿", + ["kiss_man_man_tone4_tone1"] = "👨🏾‍❤️‍💋‍👨🏻", + ["kiss_man_man_medium_dark_skin_tone_light_skin_tone"] = "👨🏾‍❤️‍💋‍👨🏻", + ["kiss_man_man_tone4_tone2"] = "👨🏾‍❤️‍💋‍👨🏼", + ["kiss_man_man_medium_dark_skin_tone_medium_light_skin_tone"] = "👨🏾‍❤️‍💋‍👨🏼", + ["kiss_man_man_tone4_tone3"] = "👨🏾‍❤️‍💋‍👨🏽", + ["kiss_man_man_medium_dark_skin_tone_medium_skin_tone"] = "👨🏾‍❤️‍💋‍👨🏽", + ["kiss_man_man_tone4"] = "👨🏾‍❤️‍💋‍👨🏾", + ["kiss_man_man_medium_dark_skin_tone"] = "👨🏾‍❤️‍💋‍👨🏾", + ["kiss_man_man_tone4_tone5"] = "👨🏾‍❤️‍💋‍👨🏿", + ["kiss_man_man_medium_dark_skin_tone_dark_skin_tone"] = "👨🏾‍❤️‍💋‍👨🏿", + ["kiss_man_man_tone5_tone1"] = "👨🏿‍❤️‍💋‍👨🏻", + ["kiss_man_man_dark_skin_tone_light_skin_tone"] = "👨🏿‍❤️‍💋‍👨🏻", + ["kiss_man_man_tone5_tone2"] = "👨🏿‍❤️‍💋‍👨🏼", + ["kiss_man_man_dark_skin_tone_medium_light_skin_tone"] = "👨🏿‍❤️‍💋‍👨🏼", + ["kiss_man_man_tone5_tone3"] = "👨🏿‍❤️‍💋‍👨🏽", + ["kiss_man_man_dark_skin_tone_medium_skin_tone"] = "👨🏿‍❤️‍💋‍👨🏽", + ["kiss_man_man_tone5_tone4"] = "👨🏿‍❤️‍💋‍👨🏾", + ["kiss_man_man_dark_skin_tone_medium_dark_skin_tone"] = "👨🏿‍❤️‍💋‍👨🏾", + ["kiss_man_man_tone5"] = "👨🏿‍❤️‍💋‍👨🏿", + ["kiss_man_man_dark_skin_tone"] = "👨🏿‍❤️‍💋‍👨🏿", + ["family"] = "👪", + ["family_man_woman_boy"] = "👨‍👩‍👦", + ["family_mwg"] = "👨‍👩‍👧", + ["family_mwgb"] = "👨‍👩‍👧‍👦", + ["family_mwbb"] = "👨‍👩‍👦‍👦", + ["family_mwgg"] = "👨‍👩‍👧‍👧", + ["family_wwb"] = "👩‍👩‍👦", + ["family_wwg"] = "👩‍👩‍👧", + ["family_wwgb"] = "👩‍👩‍👧‍👦", + ["family_wwbb"] = "👩‍👩‍👦‍👦", + ["family_wwgg"] = "👩‍👩‍👧‍👧", + ["family_mmb"] = "👨‍👨‍👦", + ["family_mmg"] = "👨‍👨‍👧", + ["family_mmgb"] = "👨‍👨‍👧‍👦", + ["family_mmbb"] = "👨‍👨‍👦‍👦", + ["family_mmgg"] = "👨‍👨‍👧‍👧", + ["family_woman_boy"] = "👩‍👦", + ["family_woman_girl"] = "👩‍👧", + ["family_woman_girl_boy"] = "👩‍👧‍👦", + ["family_woman_boy_boy"] = "👩‍👦‍👦", + ["family_woman_girl_girl"] = "👩‍👧‍👧", + ["family_man_boy"] = "👨‍👦", + ["family_man_girl"] = "👨‍👧", + ["family_man_girl_boy"] = "👨‍👧‍👦", + ["family_man_boy_boy"] = "👨‍👦‍👦", + ["family_man_girl_girl"] = "👨‍👧‍👧", + ["yarn"] = "🧶", + ["thread"] = "🧵", + ["coat"] = "🧥", + ["lab_coat"] = "🥼", + ["safety_vest"] = "🦺", + ["womans_clothes"] = "👚", + ["shirt"] = "👕", + ["jeans"] = "👖", + ["briefs"] = "🩲", + ["shorts"] = "🩳", + ["necktie"] = "👔", + ["dress"] = "👗", + ["bikini"] = "👙", + ["one_piece_swimsuit"] = "🩱", + ["kimono"] = "👘", + ["sari"] = "🥻", + ["womans_flat_shoe"] = "🥿", + ["high_heel"] = "👠", + ["sandal"] = "👡", + ["boot"] = "👢", + ["mans_shoe"] = "👞", + ["athletic_shoe"] = "👟", + ["hiking_boot"] = "🥾", + ["thong_sandal"] = "🩴", + ["socks"] = "🧦", + ["gloves"] = "🧤", + ["scarf"] = "🧣", + ["tophat"] = "🎩", + ["billed_cap"] = "🧢", + ["womans_hat"] = "👒", + ["mortar_board"] = "🎓", + ["helmet_with_cross"] = "⛑️", + ["helmet_with_white_cross"] = "⛑️", + ["military_helmet"] = "🪖", + ["crown"] = "👑", + ["ring"] = "💍", + ["pouch"] = "👝", + ["purse"] = "👛", + ["handbag"] = "👜", + ["briefcase"] = "💼", + ["school_satchel"] = "🎒", + ["luggage"] = "🧳", + ["eyeglasses"] = "👓", + ["dark_sunglasses"] = "🕶️", + ["goggles"] = "🥽", + ["closed_umbrella"] = "🌂", + ["dog"] = "🐶", + ["cat"] = "🐱", + ["mouse"] = "🐭", + ["hamster"] = "🐹", + ["rabbit"] = "🐰", + ["fox"] = "🦊", + ["fox_face"] = "🦊", + ["bear"] = "🐻", + ["panda_face"] = "🐼", + ["polar_bear"] = "🐻‍❄️", + ["koala"] = "🐨", + ["tiger"] = "🐯", + ["lion_face"] = "🦁", + ["lion"] = "🦁", + ["cow"] = "🐮", + ["pig"] = "🐷", + ["pig_nose"] = "🐽", + ["frog"] = "🐸", + ["monkey_face"] = "🐵", + ["see_no_evil"] = "🙈", + ["hear_no_evil"] = "🙉", + ["speak_no_evil"] = "🙊", + ["monkey"] = "🐒", + ["chicken"] = "🐔", + ["penguin"] = "🐧", + ["bird"] = "🐦", + ["baby_chick"] = "🐤", + ["hatching_chick"] = "🐣", + ["hatched_chick"] = "🐥", + ["duck"] = "🦆", + ["dodo"] = "🦤", + ["eagle"] = "🦅", + ["owl"] = "🦉", + ["bat"] = "🦇", + ["wolf"] = "🐺", + ["boar"] = "🐗", + ["horse"] = "🐴", + ["unicorn"] = "🦄", + ["unicorn_face"] = "🦄", + ["bee"] = "🐝", + ["bug"] = "🐛", + ["butterfly"] = "🦋", + ["snail"] = "🐌", + ["worm"] = "🪱", + ["lady_beetle"] = "🐞", + ["ant"] = "🐜", + ["fly"] = "🪰", + ["mosquito"] = "🦟", + ["cockroach"] = "🪳", + ["beetle"] = "🪲", + ["cricket"] = "🦗", + ["spider"] = "🕷️", + ["spider_web"] = "🕸️", + ["scorpion"] = "🦂", + ["turtle"] = "🐢", + ["snake"] = "🐍", + ["lizard"] = "🦎", + ["t_rex"] = "🦖", + ["sauropod"] = "🦕", + ["octopus"] = "🐙", + ["squid"] = "🦑", + ["shrimp"] = "🦐", + ["lobster"] = "🦞", + ["crab"] = "🦀", + ["blowfish"] = "🐡", + ["tropical_fish"] = "🐠", + ["fish"] = "🐟", + ["seal"] = "🦭", + ["dolphin"] = "🐬", + ["whale"] = "🐳", + ["whale2"] = "🐋", + ["shark"] = "🦈", + ["crocodile"] = "🐊", + ["tiger2"] = "🐅", + ["leopard"] = "🐆", + ["zebra"] = "🦓", + ["gorilla"] = "🦍", + ["orangutan"] = "🦧", + ["elephant"] = "🐘", + ["mammoth"] = "🦣", + ["bison"] = "🦬", + ["hippopotamus"] = "🦛", + ["rhino"] = "🦏", + ["rhinoceros"] = "🦏", + ["dromedary_camel"] = "🐪", + ["camel"] = "🐫", + ["giraffe"] = "🦒", + ["kangaroo"] = "🦘", + ["water_buffalo"] = "🐃", + ["ox"] = "🐂", + ["cow2"] = "🐄", + ["racehorse"] = "🐎", + ["pig2"] = "🐖", + ["ram"] = "🐏", + ["sheep"] = "🐑", + ["llama"] = "🦙", + ["goat"] = "🐐", + ["deer"] = "🦌", + ["dog2"] = "🐕", + ["poodle"] = "🐩", + ["guide_dog"] = "🦮", + ["service_dog"] = "🐕‍🦺", + ["cat2"] = "🐈", + ["black_cat"] = "🐈‍⬛", + ["rooster"] = "🐓", + ["turkey"] = "🦃", + ["peacock"] = "🦚", + ["parrot"] = "🦜", + ["swan"] = "🦢", + ["flamingo"] = "🦩", + ["dove"] = "🕊️", + ["dove_of_peace"] = "🕊️", + ["rabbit2"] = "🐇", + ["raccoon"] = "🦝", + ["skunk"] = "🦨", + ["badger"] = "🦡", + ["beaver"] = "🦫", + ["otter"] = "🦦", + ["sloth"] = "🦥", + ["mouse2"] = "🐁", + ["rat"] = "🐀", + ["chipmunk"] = "🐿️", + ["hedgehog"] = "🦔", + ["feet"] = "🐾", + ["paw_prints"] = "🐾", + ["dragon"] = "🐉", + ["dragon_face"] = "🐲", + ["cactus"] = "🌵", + ["christmas_tree"] = "🎄", + ["evergreen_tree"] = "🌲", + ["deciduous_tree"] = "🌳", + ["palm_tree"] = "🌴", + ["seedling"] = "🌱", + ["herb"] = "🌿", + ["shamrock"] = "☘️", + ["four_leaf_clover"] = "🍀", + ["bamboo"] = "🎍", + ["tanabata_tree"] = "🎋", + ["leaves"] = "🍃", + ["fallen_leaf"] = "🍂", + ["maple_leaf"] = "🍁", + ["feather"] = "🪶", + ["mushroom"] = "🍄", + ["shell"] = "🐚", + ["rock"] = "🪨", + ["wood"] = "🪵", + ["ear_of_rice"] = "🌾", + ["potted_plant"] = "🪴", + ["bouquet"] = "💐", + ["tulip"] = "🌷", + ["rose"] = "🌹", + ["wilted_rose"] = "🥀", + ["wilted_flower"] = "🥀", + ["hibiscus"] = "🌺", + ["cherry_blossom"] = "🌸", + ["blossom"] = "🌼", + ["sunflower"] = "🌻", + ["sun_with_face"] = "🌞", + ["full_moon_with_face"] = "🌝", + ["first_quarter_moon_with_face"] = "🌛", + ["last_quarter_moon_with_face"] = "🌜", + ["new_moon_with_face"] = "🌚", + ["full_moon"] = "🌕", + ["waning_gibbous_moon"] = "🌖", + ["last_quarter_moon"] = "🌗", + ["waning_crescent_moon"] = "🌘", + ["new_moon"] = "🌑", + ["waxing_crescent_moon"] = "🌒", + ["first_quarter_moon"] = "🌓", + ["waxing_gibbous_moon"] = "🌔", + ["crescent_moon"] = "🌙", + ["earth_americas"] = "🌎", + ["earth_africa"] = "🌍", + ["earth_asia"] = "🌏", + ["ringed_planet"] = "🪐", + ["dizzy"] = "💫", + ["star"] = "⭐", + ["star2"] = "🌟", + ["sparkles"] = "✨", + ["zap"] = "⚡", + ["comet"] = "☄️", + ["boom"] = "💥", + ["fire"] = "🔥", + ["flame"] = "🔥", + ["cloud_tornado"] = "🌪️", + ["cloud_with_tornado"] = "🌪️", + ["rainbow"] = "🌈", + ["sunny"] = "☀️", + ["white_sun_small_cloud"] = "🌤️", + ["white_sun_with_small_cloud"] = "🌤️", + ["partly_sunny"] = "⛅", + ["white_sun_cloud"] = "🌥️", + ["white_sun_behind_cloud"] = "🌥️", + ["cloud"] = "☁️", + ["white_sun_rain_cloud"] = "🌦️", + ["white_sun_behind_cloud_with_rain"] = "🌦️", + ["cloud_rain"] = "🌧️", + ["cloud_with_rain"] = "🌧️", + ["thunder_cloud_rain"] = "⛈️", + ["thunder_cloud_and_rain"] = "⛈️", + ["cloud_lightning"] = "🌩️", + ["cloud_with_lightning"] = "🌩️", + ["cloud_snow"] = "🌨️", + ["cloud_with_snow"] = "🌨️", + ["snowflake"] = "❄️", + ["snowman2"] = "☃️", + ["snowman"] = "⛄", + ["wind_blowing_face"] = "🌬️", + ["dash"] = "💨", + ["droplet"] = "💧", + ["sweat_drops"] = "💦", + ["umbrella"] = "☔", + ["umbrella2"] = "☂️", + ["ocean"] = "🌊", + ["fog"] = "🌫️", + ["green_apple"] = "🍏", + ["apple"] = "🍎", + ["pear"] = "🍐", + ["tangerine"] = "🍊", + ["lemon"] = "🍋", + ["banana"] = "🍌", + ["watermelon"] = "🍉", + ["grapes"] = "🍇", + ["blueberries"] = "🫐", + ["strawberry"] = "🍓", + ["melon"] = "🍈", + ["cherries"] = "🍒", + ["peach"] = "🍑", + ["mango"] = "🥭", + ["pineapple"] = "🍍", + ["coconut"] = "🥥", + ["kiwi"] = "🥝", + ["kiwifruit"] = "🥝", + ["tomato"] = "🍅", + ["eggplant"] = "🍆", + ["avocado"] = "🥑", + ["olive"] = "🫒", + ["broccoli"] = "🥦", + ["leafy_green"] = "🥬", + ["bell_pepper"] = "🫑", + ["cucumber"] = "🥒", + ["hot_pepper"] = "🌶️", + ["corn"] = "🌽", + ["carrot"] = "🥕", + ["garlic"] = "🧄", + ["onion"] = "🧅", + ["potato"] = "🥔", + ["sweet_potato"] = "🍠", + ["croissant"] = "🥐", + ["bagel"] = "🥯", + ["bread"] = "🍞", + ["french_bread"] = "🥖", + ["baguette_bread"] = "🥖", + ["flatbread"] = "🫓", + ["pretzel"] = "🥨", + ["cheese"] = "🧀", + ["cheese_wedge"] = "🧀", + ["egg"] = "🥚", + ["cooking"] = "🍳", + ["butter"] = "🧈", + ["pancakes"] = "🥞", + ["waffle"] = "🧇", + ["bacon"] = "🥓", + ["cut_of_meat"] = "🥩", + ["poultry_leg"] = "🍗", + ["meat_on_bone"] = "🍖", + ["hotdog"] = "🌭", + ["hot_dog"] = "🌭", + ["hamburger"] = "🍔", + ["fries"] = "🍟", + ["pizza"] = "🍕", + ["sandwich"] = "🥪", + ["stuffed_flatbread"] = "🥙", + ["stuffed_pita"] = "🥙", + ["falafel"] = "🧆", + ["taco"] = "🌮", + ["burrito"] = "🌯", + ["tamale"] = "🫔", + ["salad"] = "🥗", + ["green_salad"] = "🥗", + ["shallow_pan_of_food"] = "🥘", + ["paella"] = "🥘", + ["fondue"] = "🫕", + ["canned_food"] = "🥫", + ["spaghetti"] = "🍝", + ["ramen"] = "🍜", + ["stew"] = "🍲", + ["curry"] = "🍛", + ["sushi"] = "🍣", + ["bento"] = "🍱", + ["dumpling"] = "🥟", + ["oyster"] = "🦪", + ["fried_shrimp"] = "🍤", + ["rice_ball"] = "🍙", + ["rice"] = "🍚", + ["rice_cracker"] = "🍘", + ["fish_cake"] = "🍥", + ["fortune_cookie"] = "🥠", + ["moon_cake"] = "🥮", + ["oden"] = "🍢", + ["dango"] = "🍡", + ["shaved_ice"] = "🍧", + ["ice_cream"] = "🍨", + ["icecream"] = "🍦", + ["pie"] = "🥧", + ["cupcake"] = "🧁", + ["cake"] = "🍰", + ["birthday"] = "🎂", + ["custard"] = "🍮", + ["pudding"] = "🍮", + ["flan"] = "🍮", + ["lollipop"] = "🍭", + ["candy"] = "🍬", + ["chocolate_bar"] = "🍫", + ["popcorn"] = "🍿", + ["doughnut"] = "🍩", + ["cookie"] = "🍪", + ["chestnut"] = "🌰", + ["peanuts"] = "🥜", + ["shelled_peanut"] = "🥜", + ["honey_pot"] = "🍯", + ["milk"] = "🥛", + ["glass_of_milk"] = "🥛", + ["baby_bottle"] = "🍼", + ["coffee"] = "☕", + ["tea"] = "🍵", + ["teapot"] = "🫖", + ["mate"] = "🧉", + ["bubble_tea"] = "🧋", + ["beverage_box"] = "🧃", + ["cup_with_straw"] = "🥤", + ["sake"] = "🍶", + ["beer"] = "🍺", + ["beers"] = "🍻", + ["champagne_glass"] = "🥂", + ["clinking_glass"] = "🥂", + ["wine_glass"] = "🍷", + ["tumbler_glass"] = "🥃", + ["whisky"] = "🥃", + ["cocktail"] = "🍸", + ["tropical_drink"] = "🍹", + ["champagne"] = "🍾", + ["bottle_with_popping_cork"] = "🍾", + ["ice_cube"] = "🧊", + ["spoon"] = "🥄", + ["fork_and_knife"] = "🍴", + ["fork_knife_plate"] = "🍽️", + ["fork_and_knife_with_plate"] = "🍽️", + ["bowl_with_spoon"] = "🥣", + ["takeout_box"] = "🥡", + ["chopsticks"] = "🥢", + ["salt"] = "🧂", + ["soccer"] = "⚽", + ["basketball"] = "🏀", + ["football"] = "🏈", + ["baseball"] = "⚾", + ["softball"] = "🥎", + ["tennis"] = "🎾", + ["volleyball"] = "🏐", + ["rugby_football"] = "🏉", + ["flying_disc"] = "🥏", + ["boomerang"] = "🪃", + ["8ball"] = "🎱", + ["yo_yo"] = "🪀", + ["ping_pong"] = "🏓", + ["table_tennis"] = "🏓", + ["badminton"] = "🏸", + ["hockey"] = "🏒", + ["field_hockey"] = "🏑", + ["lacrosse"] = "🥍", + ["cricket_game"] = "🏏", + ["cricket_bat_ball"] = "🏏", + ["goal"] = "🥅", + ["goal_net"] = "🥅", + ["golf"] = "⛳", + ["kite"] = "🪁", + ["bow_and_arrow"] = "🏹", + ["archery"] = "🏹", + ["fishing_pole_and_fish"] = "🎣", + ["diving_mask"] = "🤿", + ["boxing_glove"] = "🥊", + ["boxing_gloves"] = "🥊", + ["martial_arts_uniform"] = "🥋", + ["karate_uniform"] = "🥋", + ["running_shirt_with_sash"] = "🎽", + ["skateboard"] = "🛹", + ["roller_skate"] = "🛼", + ["sled"] = "🛷", + ["ice_skate"] = "⛸️", + ["curling_stone"] = "🥌", + ["ski"] = "🎿", + ["skier"] = "⛷️", + ["snowboarder"] = "🏂", + ["snowboarder_tone1"] = "🏂🏻", + ["snowboarder_light_skin_tone"] = "🏂🏻", + ["snowboarder_tone2"] = "🏂🏼", + ["snowboarder_medium_light_skin_tone"] = "🏂🏼", + ["snowboarder_tone3"] = "🏂🏽", + ["snowboarder_medium_skin_tone"] = "🏂🏽", + ["snowboarder_tone4"] = "🏂🏾", + ["snowboarder_medium_dark_skin_tone"] = "🏂🏾", + ["snowboarder_tone5"] = "🏂🏿", + ["snowboarder_dark_skin_tone"] = "🏂🏿", + ["parachute"] = "🪂", + ["person_lifting_weights"] = "🏋️", + ["lifter"] = "🏋️", + ["weight_lifter"] = "🏋️", + ["person_lifting_weights_tone1"] = "🏋🏻", + ["lifter_tone1"] = "🏋🏻", + ["weight_lifter_tone1"] = "🏋🏻", + ["person_lifting_weights_tone2"] = "🏋🏼", + ["lifter_tone2"] = "🏋🏼", + ["weight_lifter_tone2"] = "🏋🏼", + ["person_lifting_weights_tone3"] = "🏋🏽", + ["lifter_tone3"] = "🏋🏽", + ["weight_lifter_tone3"] = "🏋🏽", + ["person_lifting_weights_tone4"] = "🏋🏾", + ["lifter_tone4"] = "🏋🏾", + ["weight_lifter_tone4"] = "🏋🏾", + ["person_lifting_weights_tone5"] = "🏋🏿", + ["lifter_tone5"] = "🏋🏿", + ["weight_lifter_tone5"] = "🏋🏿", + ["woman_lifting_weights"] = "🏋️‍♀️", + ["woman_lifting_weights_tone1"] = "🏋🏻‍♀️", + ["woman_lifting_weights_light_skin_tone"] = "🏋🏻‍♀️", + ["woman_lifting_weights_tone2"] = "🏋🏼‍♀️", + ["woman_lifting_weights_medium_light_skin_tone"] = "🏋🏼‍♀️", + ["woman_lifting_weights_tone3"] = "🏋🏽‍♀️", + ["woman_lifting_weights_medium_skin_tone"] = "🏋🏽‍♀️", + ["woman_lifting_weights_tone4"] = "🏋🏾‍♀️", + ["woman_lifting_weights_medium_dark_skin_tone"] = "🏋🏾‍♀️", + ["woman_lifting_weights_tone5"] = "🏋🏿‍♀️", + ["woman_lifting_weights_dark_skin_tone"] = "🏋🏿‍♀️", + ["man_lifting_weights"] = "🏋️‍♂️", + ["man_lifting_weights_tone1"] = "🏋🏻‍♂️", + ["man_lifting_weights_light_skin_tone"] = "🏋🏻‍♂️", + ["man_lifting_weights_tone2"] = "🏋🏼‍♂️", + ["man_lifting_weights_medium_light_skin_tone"] = "🏋🏼‍♂️", + ["man_lifting_weights_tone3"] = "🏋🏽‍♂️", + ["man_lifting_weights_medium_skin_tone"] = "🏋🏽‍♂️", + ["man_lifting_weights_tone4"] = "🏋🏾‍♂️", + ["man_lifting_weights_medium_dark_skin_tone"] = "🏋🏾‍♂️", + ["man_lifting_weights_tone5"] = "🏋🏿‍♂️", + ["man_lifting_weights_dark_skin_tone"] = "🏋🏿‍♂️", + ["people_wrestling"] = "🤼", + ["wrestlers"] = "🤼", + ["wrestling"] = "🤼", + ["women_wrestling"] = "🤼‍♀️", + ["men_wrestling"] = "🤼‍♂️", + ["person_doing_cartwheel"] = "🤸", + ["cartwheel"] = "🤸", + ["person_doing_cartwheel_tone1"] = "🤸🏻", + ["cartwheel_tone1"] = "🤸🏻", + ["person_doing_cartwheel_tone2"] = "🤸🏼", + ["cartwheel_tone2"] = "🤸🏼", + ["person_doing_cartwheel_tone3"] = "🤸🏽", + ["cartwheel_tone3"] = "🤸🏽", + ["person_doing_cartwheel_tone4"] = "🤸🏾", + ["cartwheel_tone4"] = "🤸🏾", + ["person_doing_cartwheel_tone5"] = "🤸🏿", + ["cartwheel_tone5"] = "🤸🏿", + ["woman_cartwheeling"] = "🤸‍♀️", + ["woman_cartwheeling_tone1"] = "🤸🏻‍♀️", + ["woman_cartwheeling_light_skin_tone"] = "🤸🏻‍♀️", + ["woman_cartwheeling_tone2"] = "🤸🏼‍♀️", + ["woman_cartwheeling_medium_light_skin_tone"] = "🤸🏼‍♀️", + ["woman_cartwheeling_tone3"] = "🤸🏽‍♀️", + ["woman_cartwheeling_medium_skin_tone"] = "🤸🏽‍♀️", + ["woman_cartwheeling_tone4"] = "🤸🏾‍♀️", + ["woman_cartwheeling_medium_dark_skin_tone"] = "🤸🏾‍♀️", + ["woman_cartwheeling_tone5"] = "🤸🏿‍♀️", + ["woman_cartwheeling_dark_skin_tone"] = "🤸🏿‍♀️", + ["man_cartwheeling"] = "🤸‍♂️", + ["man_cartwheeling_tone1"] = "🤸🏻‍♂️", + ["man_cartwheeling_light_skin_tone"] = "🤸🏻‍♂️", + ["man_cartwheeling_tone2"] = "🤸🏼‍♂️", + ["man_cartwheeling_medium_light_skin_tone"] = "🤸🏼‍♂️", + ["man_cartwheeling_tone3"] = "🤸🏽‍♂️", + ["man_cartwheeling_medium_skin_tone"] = "🤸🏽‍♂️", + ["man_cartwheeling_tone4"] = "🤸🏾‍♂️", + ["man_cartwheeling_medium_dark_skin_tone"] = "🤸🏾‍♂️", + ["man_cartwheeling_tone5"] = "🤸🏿‍♂️", + ["man_cartwheeling_dark_skin_tone"] = "🤸🏿‍♂️", + ["person_bouncing_ball"] = "⛹️", + ["basketball_player"] = "⛹️", + ["person_with_ball"] = "⛹️", + ["person_bouncing_ball_tone1"] = "⛹🏻", + ["basketball_player_tone1"] = "⛹🏻", + ["person_with_ball_tone1"] = "⛹🏻", + ["person_bouncing_ball_tone2"] = "⛹🏼", + ["basketball_player_tone2"] = "⛹🏼", + ["person_with_ball_tone2"] = "⛹🏼", + ["person_bouncing_ball_tone3"] = "⛹🏽", + ["basketball_player_tone3"] = "⛹🏽", + ["person_with_ball_tone3"] = "⛹🏽", + ["person_bouncing_ball_tone4"] = "⛹🏾", + ["basketball_player_tone4"] = "⛹🏾", + ["person_with_ball_tone4"] = "⛹🏾", + ["person_bouncing_ball_tone5"] = "⛹🏿", + ["basketball_player_tone5"] = "⛹🏿", + ["person_with_ball_tone5"] = "⛹🏿", + ["woman_bouncing_ball"] = "⛹️‍♀️", + ["woman_bouncing_ball_tone1"] = "⛹🏻‍♀️", + ["woman_bouncing_ball_light_skin_tone"] = "⛹🏻‍♀️", + ["woman_bouncing_ball_tone2"] = "⛹🏼‍♀️", + ["woman_bouncing_ball_medium_light_skin_tone"] = "⛹🏼‍♀️", + ["woman_bouncing_ball_tone3"] = "⛹🏽‍♀️", + ["woman_bouncing_ball_medium_skin_tone"] = "⛹🏽‍♀️", + ["woman_bouncing_ball_tone4"] = "⛹🏾‍♀️", + ["woman_bouncing_ball_medium_dark_skin_tone"] = "⛹🏾‍♀️", + ["woman_bouncing_ball_tone5"] = "⛹🏿‍♀️", + ["woman_bouncing_ball_dark_skin_tone"] = "⛹🏿‍♀️", + ["man_bouncing_ball"] = "⛹️‍♂️", + ["man_bouncing_ball_tone1"] = "⛹🏻‍♂️", + ["man_bouncing_ball_light_skin_tone"] = "⛹🏻‍♂️", + ["man_bouncing_ball_tone2"] = "⛹🏼‍♂️", + ["man_bouncing_ball_medium_light_skin_tone"] = "⛹🏼‍♂️", + ["man_bouncing_ball_tone3"] = "⛹🏽‍♂️", + ["man_bouncing_ball_medium_skin_tone"] = "⛹🏽‍♂️", + ["man_bouncing_ball_tone4"] = "⛹🏾‍♂️", + ["man_bouncing_ball_medium_dark_skin_tone"] = "⛹🏾‍♂️", + ["man_bouncing_ball_tone5"] = "⛹🏿‍♂️", + ["man_bouncing_ball_dark_skin_tone"] = "⛹🏿‍♂️", + ["person_fencing"] = "🤺", + ["fencer"] = "🤺", + ["fencing"] = "🤺", + ["person_playing_handball"] = "🤾", + ["handball"] = "🤾", + ["person_playing_handball_tone1"] = "🤾🏻", + ["handball_tone1"] = "🤾🏻", + ["person_playing_handball_tone2"] = "🤾🏼", + ["handball_tone2"] = "🤾🏼", + ["person_playing_handball_tone3"] = "🤾🏽", + ["handball_tone3"] = "🤾🏽", + ["person_playing_handball_tone4"] = "🤾🏾", + ["handball_tone4"] = "🤾🏾", + ["person_playing_handball_tone5"] = "🤾🏿", + ["handball_tone5"] = "🤾🏿", + ["woman_playing_handball"] = "🤾‍♀️", + ["woman_playing_handball_tone1"] = "🤾🏻‍♀️", + ["woman_playing_handball_light_skin_tone"] = "🤾🏻‍♀️", + ["woman_playing_handball_tone2"] = "🤾🏼‍♀️", + ["woman_playing_handball_medium_light_skin_tone"] = "🤾🏼‍♀️", + ["woman_playing_handball_tone3"] = "🤾🏽‍♀️", + ["woman_playing_handball_medium_skin_tone"] = "🤾🏽‍♀️", + ["woman_playing_handball_tone4"] = "🤾🏾‍♀️", + ["woman_playing_handball_medium_dark_skin_tone"] = "🤾🏾‍♀️", + ["woman_playing_handball_tone5"] = "🤾🏿‍♀️", + ["woman_playing_handball_dark_skin_tone"] = "🤾🏿‍♀️", + ["man_playing_handball"] = "🤾‍♂️", + ["man_playing_handball_tone1"] = "🤾🏻‍♂️", + ["man_playing_handball_light_skin_tone"] = "🤾🏻‍♂️", + ["man_playing_handball_tone2"] = "🤾🏼‍♂️", + ["man_playing_handball_medium_light_skin_tone"] = "🤾🏼‍♂️", + ["man_playing_handball_tone3"] = "🤾🏽‍♂️", + ["man_playing_handball_medium_skin_tone"] = "🤾🏽‍♂️", + ["man_playing_handball_tone4"] = "🤾🏾‍♂️", + ["man_playing_handball_medium_dark_skin_tone"] = "🤾🏾‍♂️", + ["man_playing_handball_tone5"] = "🤾🏿‍♂️", + ["man_playing_handball_dark_skin_tone"] = "🤾🏿‍♂️", + ["person_golfing"] = "🏌️", + ["golfer"] = "🏌️", + ["person_golfing_tone1"] = "🏌🏻", + ["person_golfing_light_skin_tone"] = "🏌🏻", + ["person_golfing_tone2"] = "🏌🏼", + ["person_golfing_medium_light_skin_tone"] = "🏌🏼", + ["person_golfing_tone3"] = "🏌🏽", + ["person_golfing_medium_skin_tone"] = "🏌🏽", + ["person_golfing_tone4"] = "🏌🏾", + ["person_golfing_medium_dark_skin_tone"] = "🏌🏾", + ["person_golfing_tone5"] = "🏌🏿", + ["person_golfing_dark_skin_tone"] = "🏌🏿", + ["woman_golfing"] = "🏌️‍♀️", + ["woman_golfing_tone1"] = "🏌🏻‍♀️", + ["woman_golfing_light_skin_tone"] = "🏌🏻‍♀️", + ["woman_golfing_tone2"] = "🏌🏼‍♀️", + ["woman_golfing_medium_light_skin_tone"] = "🏌🏼‍♀️", + ["woman_golfing_tone3"] = "🏌🏽‍♀️", + ["woman_golfing_medium_skin_tone"] = "🏌🏽‍♀️", + ["woman_golfing_tone4"] = "🏌🏾‍♀️", + ["woman_golfing_medium_dark_skin_tone"] = "🏌🏾‍♀️", + ["woman_golfing_tone5"] = "🏌🏿‍♀️", + ["woman_golfing_dark_skin_tone"] = "🏌🏿‍♀️", + ["man_golfing"] = "🏌️‍♂️", + ["man_golfing_tone1"] = "🏌🏻‍♂️", + ["man_golfing_light_skin_tone"] = "🏌🏻‍♂️", + ["man_golfing_tone2"] = "🏌🏼‍♂️", + ["man_golfing_medium_light_skin_tone"] = "🏌🏼‍♂️", + ["man_golfing_tone3"] = "🏌🏽‍♂️", + ["man_golfing_medium_skin_tone"] = "🏌🏽‍♂️", + ["man_golfing_tone4"] = "🏌🏾‍♂️", + ["man_golfing_medium_dark_skin_tone"] = "🏌🏾‍♂️", + ["man_golfing_tone5"] = "🏌🏿‍♂️", + ["man_golfing_dark_skin_tone"] = "🏌🏿‍♂️", + ["horse_racing"] = "🏇", + ["horse_racing_tone1"] = "🏇🏻", + ["horse_racing_tone2"] = "🏇🏼", + ["horse_racing_tone3"] = "🏇🏽", + ["horse_racing_tone4"] = "🏇🏾", + ["horse_racing_tone5"] = "🏇🏿", + ["person_in_lotus_position"] = "🧘", + ["person_in_lotus_position_tone1"] = "🧘🏻", + ["person_in_lotus_position_light_skin_tone"] = "🧘🏻", + ["person_in_lotus_position_tone2"] = "🧘🏼", + ["person_in_lotus_position_medium_light_skin_tone"] = "🧘🏼", + ["person_in_lotus_position_tone3"] = "🧘🏽", + ["person_in_lotus_position_medium_skin_tone"] = "🧘🏽", + ["person_in_lotus_position_tone4"] = "🧘🏾", + ["person_in_lotus_position_medium_dark_skin_tone"] = "🧘🏾", + ["person_in_lotus_position_tone5"] = "🧘🏿", + ["person_in_lotus_position_dark_skin_tone"] = "🧘🏿", + ["woman_in_lotus_position"] = "🧘‍♀️", + ["woman_in_lotus_position_tone1"] = "🧘🏻‍♀️", + ["woman_in_lotus_position_light_skin_tone"] = "🧘🏻‍♀️", + ["woman_in_lotus_position_tone2"] = "🧘🏼‍♀️", + ["woman_in_lotus_position_medium_light_skin_tone"] = "🧘🏼‍♀️", + ["woman_in_lotus_position_tone3"] = "🧘🏽‍♀️", + ["woman_in_lotus_position_medium_skin_tone"] = "🧘🏽‍♀️", + ["woman_in_lotus_position_tone4"] = "🧘🏾‍♀️", + ["woman_in_lotus_position_medium_dark_skin_tone"] = "🧘🏾‍♀️", + ["woman_in_lotus_position_tone5"] = "🧘🏿‍♀️", + ["woman_in_lotus_position_dark_skin_tone"] = "🧘🏿‍♀️", + ["man_in_lotus_position"] = "🧘‍♂️", + ["man_in_lotus_position_tone1"] = "🧘🏻‍♂️", + ["man_in_lotus_position_light_skin_tone"] = "🧘🏻‍♂️", + ["man_in_lotus_position_tone2"] = "🧘🏼‍♂️", + ["man_in_lotus_position_medium_light_skin_tone"] = "🧘🏼‍♂️", + ["man_in_lotus_position_tone3"] = "🧘🏽‍♂️", + ["man_in_lotus_position_medium_skin_tone"] = "🧘🏽‍♂️", + ["man_in_lotus_position_tone4"] = "🧘🏾‍♂️", + ["man_in_lotus_position_medium_dark_skin_tone"] = "🧘🏾‍♂️", + ["man_in_lotus_position_tone5"] = "🧘🏿‍♂️", + ["man_in_lotus_position_dark_skin_tone"] = "🧘🏿‍♂️", + ["person_surfing"] = "🏄", + ["surfer"] = "🏄", + ["person_surfing_tone1"] = "🏄🏻", + ["surfer_tone1"] = "🏄🏻", + ["person_surfing_tone2"] = "🏄🏼", + ["surfer_tone2"] = "🏄🏼", + ["person_surfing_tone3"] = "🏄🏽", + ["surfer_tone3"] = "🏄🏽", + ["person_surfing_tone4"] = "🏄🏾", + ["surfer_tone4"] = "🏄🏾", + ["person_surfing_tone5"] = "🏄🏿", + ["surfer_tone5"] = "🏄🏿", + ["woman_surfing"] = "🏄‍♀️", + ["woman_surfing_tone1"] = "🏄🏻‍♀️", + ["woman_surfing_light_skin_tone"] = "🏄🏻‍♀️", + ["woman_surfing_tone2"] = "🏄🏼‍♀️", + ["woman_surfing_medium_light_skin_tone"] = "🏄🏼‍♀️", + ["woman_surfing_tone3"] = "🏄🏽‍♀️", + ["woman_surfing_medium_skin_tone"] = "🏄🏽‍♀️", + ["woman_surfing_tone4"] = "🏄🏾‍♀️", + ["woman_surfing_medium_dark_skin_tone"] = "🏄🏾‍♀️", + ["woman_surfing_tone5"] = "🏄🏿‍♀️", + ["woman_surfing_dark_skin_tone"] = "🏄🏿‍♀️", + ["man_surfing"] = "🏄‍♂️", + ["man_surfing_tone1"] = "🏄🏻‍♂️", + ["man_surfing_light_skin_tone"] = "🏄🏻‍♂️", + ["man_surfing_tone2"] = "🏄🏼‍♂️", + ["man_surfing_medium_light_skin_tone"] = "🏄🏼‍♂️", + ["man_surfing_tone3"] = "🏄🏽‍♂️", + ["man_surfing_medium_skin_tone"] = "🏄🏽‍♂️", + ["man_surfing_tone4"] = "🏄🏾‍♂️", + ["man_surfing_medium_dark_skin_tone"] = "🏄🏾‍♂️", + ["man_surfing_tone5"] = "🏄🏿‍♂️", + ["man_surfing_dark_skin_tone"] = "🏄🏿‍♂️", + ["person_swimming"] = "🏊", + ["swimmer"] = "🏊", + ["person_swimming_tone1"] = "🏊🏻", + ["swimmer_tone1"] = "🏊🏻", + ["person_swimming_tone2"] = "🏊🏼", + ["swimmer_tone2"] = "🏊🏼", + ["person_swimming_tone3"] = "🏊🏽", + ["swimmer_tone3"] = "🏊🏽", + ["person_swimming_tone4"] = "🏊🏾", + ["swimmer_tone4"] = "🏊🏾", + ["person_swimming_tone5"] = "🏊🏿", + ["swimmer_tone5"] = "🏊🏿", + ["woman_swimming"] = "🏊‍♀️", + ["woman_swimming_tone1"] = "🏊🏻‍♀️", + ["woman_swimming_light_skin_tone"] = "🏊🏻‍♀️", + ["woman_swimming_tone2"] = "🏊🏼‍♀️", + ["woman_swimming_medium_light_skin_tone"] = "🏊🏼‍♀️", + ["woman_swimming_tone3"] = "🏊🏽‍♀️", + ["woman_swimming_medium_skin_tone"] = "🏊🏽‍♀️", + ["woman_swimming_tone4"] = "🏊🏾‍♀️", + ["woman_swimming_medium_dark_skin_tone"] = "🏊🏾‍♀️", + ["woman_swimming_tone5"] = "🏊🏿‍♀️", + ["woman_swimming_dark_skin_tone"] = "🏊🏿‍♀️", + ["man_swimming"] = "🏊‍♂️", + ["man_swimming_tone1"] = "🏊🏻‍♂️", + ["man_swimming_light_skin_tone"] = "🏊🏻‍♂️", + ["man_swimming_tone2"] = "🏊🏼‍♂️", + ["man_swimming_medium_light_skin_tone"] = "🏊🏼‍♂️", + ["man_swimming_tone3"] = "🏊🏽‍♂️", + ["man_swimming_medium_skin_tone"] = "🏊🏽‍♂️", + ["man_swimming_tone4"] = "🏊🏾‍♂️", + ["man_swimming_medium_dark_skin_tone"] = "🏊🏾‍♂️", + ["man_swimming_tone5"] = "🏊🏿‍♂️", + ["man_swimming_dark_skin_tone"] = "🏊🏿‍♂️", + ["person_playing_water_polo"] = "🤽", + ["water_polo"] = "🤽", + ["person_playing_water_polo_tone1"] = "🤽🏻", + ["water_polo_tone1"] = "🤽🏻", + ["person_playing_water_polo_tone2"] = "🤽🏼", + ["water_polo_tone2"] = "🤽🏼", + ["person_playing_water_polo_tone3"] = "🤽🏽", + ["water_polo_tone3"] = "🤽🏽", + ["person_playing_water_polo_tone4"] = "🤽🏾", + ["water_polo_tone4"] = "🤽🏾", + ["person_playing_water_polo_tone5"] = "🤽🏿", + ["water_polo_tone5"] = "🤽🏿", + ["woman_playing_water_polo"] = "🤽‍♀️", + ["woman_playing_water_polo_tone1"] = "🤽🏻‍♀️", + ["woman_playing_water_polo_light_skin_tone"] = "🤽🏻‍♀️", + ["woman_playing_water_polo_tone2"] = "🤽🏼‍♀️", + ["woman_playing_water_polo_medium_light_skin_tone"] = "🤽🏼‍♀️", + ["woman_playing_water_polo_tone3"] = "🤽🏽‍♀️", + ["woman_playing_water_polo_medium_skin_tone"] = "🤽🏽‍♀️", + ["woman_playing_water_polo_tone4"] = "🤽🏾‍♀️", + ["woman_playing_water_polo_medium_dark_skin_tone"] = "🤽🏾‍♀️", + ["woman_playing_water_polo_tone5"] = "🤽🏿‍♀️", + ["woman_playing_water_polo_dark_skin_tone"] = "🤽🏿‍♀️", + ["man_playing_water_polo"] = "🤽‍♂️", + ["man_playing_water_polo_tone1"] = "🤽🏻‍♂️", + ["man_playing_water_polo_light_skin_tone"] = "🤽🏻‍♂️", + ["man_playing_water_polo_tone2"] = "🤽🏼‍♂️", + ["man_playing_water_polo_medium_light_skin_tone"] = "🤽🏼‍♂️", + ["man_playing_water_polo_tone3"] = "🤽🏽‍♂️", + ["man_playing_water_polo_medium_skin_tone"] = "🤽🏽‍♂️", + ["man_playing_water_polo_tone4"] = "🤽🏾‍♂️", + ["man_playing_water_polo_medium_dark_skin_tone"] = "🤽🏾‍♂️", + ["man_playing_water_polo_tone5"] = "🤽🏿‍♂️", + ["man_playing_water_polo_dark_skin_tone"] = "🤽🏿‍♂️", + ["person_rowing_boat"] = "🚣", + ["rowboat"] = "🚣", + ["person_rowing_boat_tone1"] = "🚣🏻", + ["rowboat_tone1"] = "🚣🏻", + ["person_rowing_boat_tone2"] = "🚣🏼", + ["rowboat_tone2"] = "🚣🏼", + ["person_rowing_boat_tone3"] = "🚣🏽", + ["rowboat_tone3"] = "🚣🏽", + ["person_rowing_boat_tone4"] = "🚣🏾", + ["rowboat_tone4"] = "🚣🏾", + ["person_rowing_boat_tone5"] = "🚣🏿", + ["rowboat_tone5"] = "🚣🏿", + ["woman_rowing_boat"] = "🚣‍♀️", + ["woman_rowing_boat_tone1"] = "🚣🏻‍♀️", + ["woman_rowing_boat_light_skin_tone"] = "🚣🏻‍♀️", + ["woman_rowing_boat_tone2"] = "🚣🏼‍♀️", + ["woman_rowing_boat_medium_light_skin_tone"] = "🚣🏼‍♀️", + ["woman_rowing_boat_tone3"] = "🚣🏽‍♀️", + ["woman_rowing_boat_medium_skin_tone"] = "🚣🏽‍♀️", + ["woman_rowing_boat_tone4"] = "🚣🏾‍♀️", + ["woman_rowing_boat_medium_dark_skin_tone"] = "🚣🏾‍♀️", + ["woman_rowing_boat_tone5"] = "🚣🏿‍♀️", + ["woman_rowing_boat_dark_skin_tone"] = "🚣🏿‍♀️", + ["man_rowing_boat"] = "🚣‍♂️", + ["man_rowing_boat_tone1"] = "🚣🏻‍♂️", + ["man_rowing_boat_light_skin_tone"] = "🚣🏻‍♂️", + ["man_rowing_boat_tone2"] = "🚣🏼‍♂️", + ["man_rowing_boat_medium_light_skin_tone"] = "🚣🏼‍♂️", + ["man_rowing_boat_tone3"] = "🚣🏽‍♂️", + ["man_rowing_boat_medium_skin_tone"] = "🚣🏽‍♂️", + ["man_rowing_boat_tone4"] = "🚣🏾‍♂️", + ["man_rowing_boat_medium_dark_skin_tone"] = "🚣🏾‍♂️", + ["man_rowing_boat_tone5"] = "🚣🏿‍♂️", + ["man_rowing_boat_dark_skin_tone"] = "🚣🏿‍♂️", + ["person_climbing"] = "🧗", + ["person_climbing_tone1"] = "🧗🏻", + ["person_climbing_light_skin_tone"] = "🧗🏻", + ["person_climbing_tone2"] = "🧗🏼", + ["person_climbing_medium_light_skin_tone"] = "🧗🏼", + ["person_climbing_tone3"] = "🧗🏽", + ["person_climbing_medium_skin_tone"] = "🧗🏽", + ["person_climbing_tone4"] = "🧗🏾", + ["person_climbing_medium_dark_skin_tone"] = "🧗🏾", + ["person_climbing_tone5"] = "🧗🏿", + ["person_climbing_dark_skin_tone"] = "🧗🏿", + ["woman_climbing"] = "🧗‍♀️", + ["woman_climbing_tone1"] = "🧗🏻‍♀️", + ["woman_climbing_light_skin_tone"] = "🧗🏻‍♀️", + ["woman_climbing_tone2"] = "🧗🏼‍♀️", + ["woman_climbing_medium_light_skin_tone"] = "🧗🏼‍♀️", + ["woman_climbing_tone3"] = "🧗🏽‍♀️", + ["woman_climbing_medium_skin_tone"] = "🧗🏽‍♀️", + ["woman_climbing_tone4"] = "🧗🏾‍♀️", + ["woman_climbing_medium_dark_skin_tone"] = "🧗🏾‍♀️", + ["woman_climbing_tone5"] = "🧗🏿‍♀️", + ["woman_climbing_dark_skin_tone"] = "🧗🏿‍♀️", + ["man_climbing"] = "🧗‍♂️", + ["man_climbing_tone1"] = "🧗🏻‍♂️", + ["man_climbing_light_skin_tone"] = "🧗🏻‍♂️", + ["man_climbing_tone2"] = "🧗🏼‍♂️", + ["man_climbing_medium_light_skin_tone"] = "🧗🏼‍♂️", + ["man_climbing_tone3"] = "🧗🏽‍♂️", + ["man_climbing_medium_skin_tone"] = "🧗🏽‍♂️", + ["man_climbing_tone4"] = "🧗🏾‍♂️", + ["man_climbing_medium_dark_skin_tone"] = "🧗🏾‍♂️", + ["man_climbing_tone5"] = "🧗🏿‍♂️", + ["man_climbing_dark_skin_tone"] = "🧗🏿‍♂️", + ["person_mountain_biking"] = "🚵", + ["mountain_bicyclist"] = "🚵", + ["person_mountain_biking_tone1"] = "🚵🏻", + ["mountain_bicyclist_tone1"] = "🚵🏻", + ["person_mountain_biking_tone2"] = "🚵🏼", + ["mountain_bicyclist_tone2"] = "🚵🏼", + ["person_mountain_biking_tone3"] = "🚵🏽", + ["mountain_bicyclist_tone3"] = "🚵🏽", + ["person_mountain_biking_tone4"] = "🚵🏾", + ["mountain_bicyclist_tone4"] = "🚵🏾", + ["person_mountain_biking_tone5"] = "🚵🏿", + ["mountain_bicyclist_tone5"] = "🚵🏿", + ["woman_mountain_biking"] = "🚵‍♀️", + ["woman_mountain_biking_tone1"] = "🚵🏻‍♀️", + ["woman_mountain_biking_light_skin_tone"] = "🚵🏻‍♀️", + ["woman_mountain_biking_tone2"] = "🚵🏼‍♀️", + ["woman_mountain_biking_medium_light_skin_tone"] = "🚵🏼‍♀️", + ["woman_mountain_biking_tone3"] = "🚵🏽‍♀️", + ["woman_mountain_biking_medium_skin_tone"] = "🚵🏽‍♀️", + ["woman_mountain_biking_tone4"] = "🚵🏾‍♀️", + ["woman_mountain_biking_medium_dark_skin_tone"] = "🚵🏾‍♀️", + ["woman_mountain_biking_tone5"] = "🚵🏿‍♀️", + ["woman_mountain_biking_dark_skin_tone"] = "🚵🏿‍♀️", + ["man_mountain_biking"] = "🚵‍♂️", + ["man_mountain_biking_tone1"] = "🚵🏻‍♂️", + ["man_mountain_biking_light_skin_tone"] = "🚵🏻‍♂️", + ["man_mountain_biking_tone2"] = "🚵🏼‍♂️", + ["man_mountain_biking_medium_light_skin_tone"] = "🚵🏼‍♂️", + ["man_mountain_biking_tone3"] = "🚵🏽‍♂️", + ["man_mountain_biking_medium_skin_tone"] = "🚵🏽‍♂️", + ["man_mountain_biking_tone4"] = "🚵🏾‍♂️", + ["man_mountain_biking_medium_dark_skin_tone"] = "🚵🏾‍♂️", + ["man_mountain_biking_tone5"] = "🚵🏿‍♂️", + ["man_mountain_biking_dark_skin_tone"] = "🚵🏿‍♂️", + ["person_biking"] = "🚴", + ["bicyclist"] = "🚴", + ["person_biking_tone1"] = "🚴🏻", + ["bicyclist_tone1"] = "🚴🏻", + ["person_biking_tone2"] = "🚴🏼", + ["bicyclist_tone2"] = "🚴🏼", + ["person_biking_tone3"] = "🚴🏽", + ["bicyclist_tone3"] = "🚴🏽", + ["person_biking_tone4"] = "🚴🏾", + ["bicyclist_tone4"] = "🚴🏾", + ["person_biking_tone5"] = "🚴🏿", + ["bicyclist_tone5"] = "🚴🏿", + ["woman_biking"] = "🚴‍♀️", + ["woman_biking_tone1"] = "🚴🏻‍♀️", + ["woman_biking_light_skin_tone"] = "🚴🏻‍♀️", + ["woman_biking_tone2"] = "🚴🏼‍♀️", + ["woman_biking_medium_light_skin_tone"] = "🚴🏼‍♀️", + ["woman_biking_tone3"] = "🚴🏽‍♀️", + ["woman_biking_medium_skin_tone"] = "🚴🏽‍♀️", + ["woman_biking_tone4"] = "🚴🏾‍♀️", + ["woman_biking_medium_dark_skin_tone"] = "🚴🏾‍♀️", + ["woman_biking_tone5"] = "🚴🏿‍♀️", + ["woman_biking_dark_skin_tone"] = "🚴🏿‍♀️", + ["man_biking"] = "🚴‍♂️", + ["man_biking_tone1"] = "🚴🏻‍♂️", + ["man_biking_light_skin_tone"] = "🚴🏻‍♂️", + ["man_biking_tone2"] = "🚴🏼‍♂️", + ["man_biking_medium_light_skin_tone"] = "🚴🏼‍♂️", + ["man_biking_tone3"] = "🚴🏽‍♂️", + ["man_biking_medium_skin_tone"] = "🚴🏽‍♂️", + ["man_biking_tone4"] = "🚴🏾‍♂️", + ["man_biking_medium_dark_skin_tone"] = "🚴🏾‍♂️", + ["man_biking_tone5"] = "🚴🏿‍♂️", + ["man_biking_dark_skin_tone"] = "🚴🏿‍♂️", + ["trophy"] = "🏆", + ["first_place"] = "🥇", + ["first_place_medal"] = "🥇", + ["second_place"] = "🥈", + ["second_place_medal"] = "🥈", + ["third_place"] = "🥉", + ["third_place_medal"] = "🥉", + ["medal"] = "🏅", + ["sports_medal"] = "🏅", + ["military_medal"] = "🎖️", + ["rosette"] = "🏵️", + ["reminder_ribbon"] = "🎗️", + ["ticket"] = "🎫", + ["tickets"] = "🎟️", + ["admission_tickets"] = "🎟️", + ["circus_tent"] = "🎪", + ["person_juggling"] = "🤹", + ["juggling"] = "🤹", + ["juggler"] = "🤹", + ["person_juggling_tone1"] = "🤹🏻", + ["juggling_tone1"] = "🤹🏻", + ["juggler_tone1"] = "🤹🏻", + ["person_juggling_tone2"] = "🤹🏼", + ["juggling_tone2"] = "🤹🏼", + ["juggler_tone2"] = "🤹🏼", + ["person_juggling_tone3"] = "🤹🏽", + ["juggling_tone3"] = "🤹🏽", + ["juggler_tone3"] = "🤹🏽", + ["person_juggling_tone4"] = "🤹🏾", + ["juggling_tone4"] = "🤹🏾", + ["juggler_tone4"] = "🤹🏾", + ["person_juggling_tone5"] = "🤹🏿", + ["juggling_tone5"] = "🤹🏿", + ["juggler_tone5"] = "🤹🏿", + ["woman_juggling"] = "🤹‍♀️", + ["woman_juggling_tone1"] = "🤹🏻‍♀️", + ["woman_juggling_light_skin_tone"] = "🤹🏻‍♀️", + ["woman_juggling_tone2"] = "🤹🏼‍♀️", + ["woman_juggling_medium_light_skin_tone"] = "🤹🏼‍♀️", + ["woman_juggling_tone3"] = "🤹🏽‍♀️", + ["woman_juggling_medium_skin_tone"] = "🤹🏽‍♀️", + ["woman_juggling_tone4"] = "🤹🏾‍♀️", + ["woman_juggling_medium_dark_skin_tone"] = "🤹🏾‍♀️", + ["woman_juggling_tone5"] = "🤹🏿‍♀️", + ["woman_juggling_dark_skin_tone"] = "🤹🏿‍♀️", + ["man_juggling"] = "🤹‍♂️", + ["man_juggling_tone1"] = "🤹🏻‍♂️", + ["man_juggling_light_skin_tone"] = "🤹🏻‍♂️", + ["man_juggling_tone2"] = "🤹🏼‍♂️", + ["man_juggling_medium_light_skin_tone"] = "🤹🏼‍♂️", + ["man_juggling_tone3"] = "🤹🏽‍♂️", + ["man_juggling_medium_skin_tone"] = "🤹🏽‍♂️", + ["man_juggling_tone4"] = "🤹🏾‍♂️", + ["man_juggling_medium_dark_skin_tone"] = "🤹🏾‍♂️", + ["man_juggling_tone5"] = "🤹🏿‍♂️", + ["man_juggling_dark_skin_tone"] = "🤹🏿‍♂️", + ["performing_arts"] = "🎭", + ["ballet_shoes"] = "🩰", + ["art"] = "🎨", + ["clapper"] = "🎬", + ["microphone"] = "🎤", + ["headphones"] = "🎧", + ["musical_score"] = "🎼", + ["musical_keyboard"] = "🎹", + ["drum"] = "🥁", + ["drum_with_drumsticks"] = "🥁", + ["long_drum"] = "🪘", + ["saxophone"] = "🎷", + ["trumpet"] = "🎺", + ["guitar"] = "🎸", + ["banjo"] = "🪕", + ["violin"] = "🎻", + ["accordion"] = "🪗", + ["game_die"] = "🎲", + ["chess_pawn"] = "♟️", + ["dart"] = "🎯", + ["bowling"] = "🎳", + ["video_game"] = "🎮", + ["slot_machine"] = "🎰", + ["jigsaw"] = "🧩", + ["red_car"] = "🚗", + ["taxi"] = "🚕", + ["blue_car"] = "🚙", + ["pickup_truck"] = "🛻", + ["bus"] = "🚌", + ["trolleybus"] = "🚎", + ["race_car"] = "🏎️", + ["racing_car"] = "🏎️", + ["police_car"] = "🚓", + ["ambulance"] = "🚑", + ["fire_engine"] = "🚒", + ["minibus"] = "🚐", + ["truck"] = "🚚", + ["articulated_lorry"] = "🚛", + ["tractor"] = "🚜", + ["probing_cane"] = "🦯", + ["manual_wheelchair"] = "🦽", + ["motorized_wheelchair"] = "🦼", + ["scooter"] = "🛴", + ["bike"] = "🚲", + ["motor_scooter"] = "🛵", + ["motorbike"] = "🛵", + ["motorcycle"] = "🏍️", + ["racing_motorcycle"] = "🏍️", + ["auto_rickshaw"] = "🛺", + ["rotating_light"] = "🚨", + ["oncoming_police_car"] = "🚔", + ["oncoming_bus"] = "🚍", + ["oncoming_automobile"] = "🚘", + ["oncoming_taxi"] = "🚖", + ["aerial_tramway"] = "🚡", + ["mountain_cableway"] = "🚠", + ["suspension_railway"] = "🚟", + ["railway_car"] = "🚃", + ["train"] = "🚋", + ["mountain_railway"] = "🚞", + ["monorail"] = "🚝", + ["bullettrain_side"] = "🚄", + ["bullettrain_front"] = "🚅", + ["light_rail"] = "🚈", + ["steam_locomotive"] = "🚂", + ["train2"] = "🚆", + ["metro"] = "🚇", + ["tram"] = "🚊", + ["station"] = "🚉", + ["airplane"] = "✈️", + ["airplane_departure"] = "🛫", + ["airplane_arriving"] = "🛬", + ["airplane_small"] = "🛩️", + ["small_airplane"] = "🛩️", + ["seat"] = "💺", + ["satellite_orbital"] = "🛰️", + ["rocket"] = "🚀", + ["flying_saucer"] = "🛸", + ["helicopter"] = "🚁", + ["canoe"] = "🛶", + ["kayak"] = "🛶", + ["sailboat"] = "⛵", + ["speedboat"] = "🚤", + ["motorboat"] = "🛥️", + ["cruise_ship"] = "🛳️", + ["passenger_ship"] = "🛳️", + ["ferry"] = "⛴️", + ["ship"] = "🚢", + ["anchor"] = "⚓", + ["fuelpump"] = "⛽", + ["construction"] = "🚧", + ["vertical_traffic_light"] = "🚦", + ["traffic_light"] = "🚥", + ["busstop"] = "🚏", + ["map"] = "🗺️", + ["world_map"] = "🗺️", + ["moyai"] = "🗿", + ["statue_of_liberty"] = "🗽", + ["tokyo_tower"] = "🗼", + ["european_castle"] = "🏰", + ["japanese_castle"] = "🏯", + ["stadium"] = "🏟️", + ["ferris_wheel"] = "🎡", + ["roller_coaster"] = "🎢", + ["carousel_horse"] = "🎠", + ["fountain"] = "⛲", + ["beach_umbrella"] = "⛱️", + ["umbrella_on_ground"] = "⛱️", + ["beach"] = "🏖️", + ["beach_with_umbrella"] = "🏖️", + ["island"] = "🏝️", + ["desert_island"] = "🏝️", + ["desert"] = "🏜️", + ["volcano"] = "🌋", + ["mountain"] = "⛰️", + ["mountain_snow"] = "🏔️", + ["snow_capped_mountain"] = "🏔️", + ["mount_fuji"] = "🗻", + ["camping"] = "🏕️", + ["tent"] = "⛺", + ["house"] = "🏠", + ["house_with_garden"] = "🏡", + ["homes"] = "🏘️", + ["house_buildings"] = "🏘️", + ["house_abandoned"] = "🏚️", + ["derelict_house_building"] = "🏚️", + ["hut"] = "🛖", + ["construction_site"] = "🏗️", + ["building_construction"] = "🏗️", + ["factory"] = "🏭", + ["office"] = "🏢", + ["department_store"] = "🏬", + ["post_office"] = "🏣", + ["european_post_office"] = "🏤", + ["hospital"] = "🏥", + ["bank"] = "🏦", + ["hotel"] = "🏨", + ["convenience_store"] = "🏪", + ["school"] = "🏫", + ["love_hotel"] = "🏩", + ["wedding"] = "💒", + ["classical_building"] = "🏛️", + ["church"] = "⛪", + ["mosque"] = "🕌", + ["synagogue"] = "🕍", + ["hindu_temple"] = "🛕", + ["kaaba"] = "🕋", + ["shinto_shrine"] = "⛩️", + ["railway_track"] = "🛤️", + ["railroad_track"] = "🛤️", + ["motorway"] = "🛣️", + ["japan"] = "🗾", + ["rice_scene"] = "🎑", + ["park"] = "🏞️", + ["national_park"] = "🏞️", + ["sunrise"] = "🌅", + ["sunrise_over_mountains"] = "🌄", + ["stars"] = "🌠", + ["sparkler"] = "🎇", + ["fireworks"] = "🎆", + ["city_sunset"] = "🌇", + ["city_sunrise"] = "🌇", + ["city_dusk"] = "🌆", + ["cityscape"] = "🏙️", + ["night_with_stars"] = "🌃", + ["milky_way"] = "🌌", + ["bridge_at_night"] = "🌉", + ["foggy"] = "🌁", + ["watch"] = "⌚", + ["mobile_phone"] = "📱", + ["iphone"] = "📱", + ["calling"] = "📲", + ["computer"] = "💻", + ["keyboard"] = "⌨️", + ["desktop"] = "🖥️", + ["desktop_computer"] = "🖥️", + ["printer"] = "🖨️", + ["mouse_three_button"] = "🖱️", + ["three_button_mouse"] = "🖱️", + ["trackball"] = "🖲️", + ["joystick"] = "🕹️", + ["compression"] = "🗜️", + ["minidisc"] = "💽", + ["floppy_disk"] = "💾", + ["cd"] = "💿", + ["dvd"] = "📀", + ["vhs"] = "📼", + ["camera"] = "📷", + ["camera_with_flash"] = "📸", + ["video_camera"] = "📹", + ["movie_camera"] = "🎥", + ["projector"] = "📽️", + ["film_projector"] = "📽️", + ["film_frames"] = "🎞️", + ["telephone_receiver"] = "📞", + ["telephone"] = "☎️", + ["pager"] = "📟", + ["fax"] = "📠", + ["tv"] = "📺", + ["radio"] = "📻", + ["microphone2"] = "🎙️", + ["studio_microphone"] = "🎙️", + ["level_slider"] = "🎚️", + ["control_knobs"] = "🎛️", + ["compass"] = "🧭", + ["stopwatch"] = "⏱️", + ["timer"] = "⏲️", + ["timer_clock"] = "⏲️", + ["alarm_clock"] = "⏰", + ["clock"] = "🕰️", + ["mantlepiece_clock"] = "🕰️", + ["hourglass"] = "⌛", + ["hourglass_flowing_sand"] = "⏳", + ["satellite"] = "📡", + ["battery"] = "🔋", + ["electric_plug"] = "🔌", + ["bulb"] = "💡", + ["flashlight"] = "🔦", + ["candle"] = "🕯️", + ["diya_lamp"] = "🪔", + ["fire_extinguisher"] = "🧯", + ["oil"] = "🛢️", + ["oil_drum"] = "🛢️", + ["money_with_wings"] = "💸", + ["dollar"] = "💵", + ["yen"] = "💴", + ["euro"] = "💶", + ["pound"] = "💷", + ["coin"] = "🪙", + ["moneybag"] = "💰", + ["credit_card"] = "💳", + ["gem"] = "💎", + ["scales"] = "⚖️", + ["ladder"] = "🪜", + ["toolbox"] = "🧰", + ["screwdriver"] = "🪛", + ["wrench"] = "🔧", + ["hammer"] = "🔨", + ["hammer_pick"] = "⚒️", + ["hammer_and_pick"] = "⚒️", + ["tools"] = "🛠️", + ["hammer_and_wrench"] = "🛠️", + ["pick"] = "⛏️", + ["nut_and_bolt"] = "🔩", + ["gear"] = "⚙️", + ["bricks"] = "🧱", + ["chains"] = "⛓️", + ["hook"] = "🪝", + ["knot"] = "🪢", + ["magnet"] = "🧲", + ["gun"] = "🔫", + ["bomb"] = "💣", + ["firecracker"] = "🧨", + ["axe"] = "🪓", + ["carpentry_saw"] = "🪚", + ["knife"] = "🔪", + ["dagger"] = "🗡️", + ["dagger_knife"] = "🗡️", + ["crossed_swords"] = "⚔️", + ["shield"] = "🛡️", + ["smoking"] = "🚬", + ["coffin"] = "⚰️", + ["headstone"] = "🪦", + ["urn"] = "⚱️", + ["funeral_urn"] = "⚱️", + ["amphora"] = "🏺", + ["magic_wand"] = "🪄", + ["crystal_ball"] = "🔮", + ["prayer_beads"] = "📿", + ["nazar_amulet"] = "🧿", + ["barber"] = "💈", + ["alembic"] = "⚗️", + ["telescope"] = "🔭", + ["microscope"] = "🔬", + ["hole"] = "🕳️", + ["window"] = "🪟", + ["adhesive_bandage"] = "🩹", + ["stethoscope"] = "🩺", + ["pill"] = "💊", + ["syringe"] = "💉", + ["drop_of_blood"] = "🩸", + ["dna"] = "🧬", + ["microbe"] = "🦠", + ["petri_dish"] = "🧫", + ["test_tube"] = "🧪", + ["thermometer"] = "🌡️", + ["mouse_trap"] = "🪤", + ["broom"] = "🧹", + ["basket"] = "🧺", + ["sewing_needle"] = "🪡", + ["roll_of_paper"] = "🧻", + ["toilet"] = "🚽", + ["plunger"] = "🪠", + ["bucket"] = "🪣", + ["potable_water"] = "🚰", + ["shower"] = "🚿", + ["bathtub"] = "🛁", + ["bath"] = "🛀", + ["bath_tone1"] = "🛀🏻", + ["bath_tone2"] = "🛀🏼", + ["bath_tone3"] = "🛀🏽", + ["bath_tone4"] = "🛀🏾", + ["bath_tone5"] = "🛀🏿", + ["toothbrush"] = "🪥", + ["soap"] = "🧼", + ["razor"] = "🪒", + ["sponge"] = "🧽", + ["squeeze_bottle"] = "🧴", + ["bellhop"] = "🛎️", + ["bellhop_bell"] = "🛎️", + ["key"] = "🔑", + ["key2"] = "🗝️", + ["old_key"] = "🗝️", + ["door"] = "🚪", + ["chair"] = "🪑", + ["mirror"] = "🪞", + ["couch"] = "🛋️", + ["couch_and_lamp"] = "🛋️", + ["bed"] = "🛏️", + ["sleeping_accommodation"] = "🛌", + ["person_in_bed_tone1"] = "🛌🏻", + ["person_in_bed_light_skin_tone"] = "🛌🏻", + ["person_in_bed_tone2"] = "🛌🏼", + ["person_in_bed_medium_light_skin_tone"] = "🛌🏼", + ["person_in_bed_tone3"] = "🛌🏽", + ["person_in_bed_medium_skin_tone"] = "🛌🏽", + ["person_in_bed_tone4"] = "🛌🏾", + ["person_in_bed_medium_dark_skin_tone"] = "🛌🏾", + ["person_in_bed_tone5"] = "🛌🏿", + ["person_in_bed_dark_skin_tone"] = "🛌🏿", + ["teddy_bear"] = "🧸", + ["frame_photo"] = "🖼️", + ["frame_with_picture"] = "🖼️", + ["shopping_bags"] = "🛍️", + ["shopping_cart"] = "🛒", + ["shopping_trolley"] = "🛒", + ["gift"] = "🎁", + ["balloon"] = "🎈", + ["flags"] = "🎏", + ["ribbon"] = "🎀", + ["confetti_ball"] = "🎊", + ["tada"] = "🎉", + ["piñata"] = "🪅", + ["nesting_dolls"] = "🪆", + ["dolls"] = "🎎", + ["izakaya_lantern"] = "🏮", + ["wind_chime"] = "🎐", + ["red_envelope"] = "🧧", + ["envelope"] = "✉️", + ["envelope_with_arrow"] = "📩", + ["incoming_envelope"] = "📨", + ["e_mail"] = "📧", + ["email"] = "📧", + ["love_letter"] = "💌", + ["inbox_tray"] = "📥", + ["outbox_tray"] = "📤", + ["package"] = "📦", + ["label"] = "🏷️", + ["mailbox_closed"] = "📪", + ["mailbox"] = "📫", + ["mailbox_with_mail"] = "📬", + ["mailbox_with_no_mail"] = "📭", + ["postbox"] = "📮", + ["postal_horn"] = "📯", + ["placard"] = "🪧", + ["scroll"] = "📜", + ["page_with_curl"] = "📃", + ["page_facing_up"] = "📄", + ["bookmark_tabs"] = "📑", + ["receipt"] = "🧾", + ["bar_chart"] = "📊", + ["chart_with_upwards_trend"] = "📈", + ["chart_with_downwards_trend"] = "📉", + ["notepad_spiral"] = "🗒️", + ["spiral_note_pad"] = "🗒️", + ["calendar_spiral"] = "🗓️", + ["spiral_calendar_pad"] = "🗓️", + ["calendar"] = "📆", + ["date"] = "📅", + ["wastebasket"] = "🗑️", + ["card_index"] = "📇", + ["card_box"] = "🗃️", + ["card_file_box"] = "🗃️", + ["ballot_box"] = "🗳️", + ["ballot_box_with_ballot"] = "🗳️", + ["file_cabinet"] = "🗄️", + ["clipboard"] = "📋", + ["file_folder"] = "📁", + ["open_file_folder"] = "📂", + ["dividers"] = "🗂️", + ["card_index_dividers"] = "🗂️", + ["newspaper2"] = "🗞️", + ["rolled_up_newspaper"] = "🗞️", + ["newspaper"] = "📰", + ["notebook"] = "📓", + ["notebook_with_decorative_cover"] = "📔", + ["ledger"] = "📒", + ["closed_book"] = "📕", + ["green_book"] = "📗", + ["blue_book"] = "📘", + ["orange_book"] = "📙", + ["books"] = "📚", + ["book"] = "📖", + ["bookmark"] = "🔖", + ["safety_pin"] = "🧷", + ["link"] = "🔗", + ["paperclip"] = "📎", + ["paperclips"] = "🖇️", + ["linked_paperclips"] = "🖇️", + ["triangular_ruler"] = "📐", + ["straight_ruler"] = "📏", + ["abacus"] = "🧮", + ["pushpin"] = "📌", + ["round_pushpin"] = "📍", + ["scissors"] = "✂️", + ["pen_ballpoint"] = "🖊️", + ["lower_left_ballpoint_pen"] = "🖊️", + ["pen_fountain"] = "🖋️", + ["lower_left_fountain_pen"] = "🖋️", + ["black_nib"] = "✒️", + ["paintbrush"] = "🖌️", + ["lower_left_paintbrush"] = "🖌️", + ["crayon"] = "🖍️", + ["lower_left_crayon"] = "🖍️", + ["pencil"] = "📝", + ["memo"] = "📝", + ["pencil2"] = "✏️", + ["mag"] = "🔍", + ["mag_right"] = "🔎", + ["lock_with_ink_pen"] = "🔏", + ["closed_lock_with_key"] = "🔐", + ["lock"] = "🔒", + ["unlock"] = "🔓", + ["heart"] = "❤️", + ["orange_heart"] = "🧡", + ["yellow_heart"] = "💛", + ["green_heart"] = "💚", + ["blue_heart"] = "💙", + ["purple_heart"] = "💜", + ["black_heart"] = "🖤", + ["brown_heart"] = "🤎", + ["white_heart"] = "🤍", + ["broken_heart"] = "💔", + ["heart_exclamation"] = "❣️", + ["heavy_heart_exclamation_mark_ornament"] = "❣️", + ["two_hearts"] = "💕", + ["revolving_hearts"] = "💞", + ["heartbeat"] = "💓", + ["heartpulse"] = "💗", + ["sparkling_heart"] = "💖", + ["cupid"] = "💘", + ["gift_heart"] = "💝", + ["mending_heart"] = "❤️‍🩹", + ["heart_on_fire"] = "❤️‍🔥", + ["heart_decoration"] = "💟", + ["peace"] = "☮️", + ["peace_symbol"] = "☮️", + ["cross"] = "✝️", + ["latin_cross"] = "✝️", + ["star_and_crescent"] = "☪️", + ["om_symbol"] = "🕉️", + ["wheel_of_dharma"] = "☸️", + ["star_of_david"] = "✡️", + ["six_pointed_star"] = "🔯", + ["menorah"] = "🕎", + ["yin_yang"] = "☯️", + ["orthodox_cross"] = "☦️", + ["place_of_worship"] = "🛐", + ["worship_symbol"] = "🛐", + ["ophiuchus"] = "⛎", + ["aries"] = "♈", + ["taurus"] = "♉", + ["gemini"] = "♊", + ["cancer"] = "♋", + ["leo"] = "♌", + ["virgo"] = "♍", + ["libra"] = "♎", + ["scorpius"] = "♏", + ["sagittarius"] = "♐", + ["capricorn"] = "♑", + ["aquarius"] = "♒", + ["pisces"] = "♓", + ["id"] = "🆔", + ["atom"] = "⚛️", + ["atom_symbol"] = "⚛️", + ["accept"] = "🉑", + ["radioactive"] = "☢️", + ["radioactive_sign"] = "☢️", + ["biohazard"] = "☣️", + ["biohazard_sign"] = "☣️", + ["mobile_phone_off"] = "📴", + ["vibration_mode"] = "📳", + ["u6709"] = "🈶", + ["u7121"] = "🈚", + ["u7533"] = "🈸", + ["u55b6"] = "🈺", + ["u6708"] = "🈷️", + ["eight_pointed_black_star"] = "✴️", + ["vs"] = "🆚", + ["white_flower"] = "💮", + ["ideograph_advantage"] = "🉐", + ["secret"] = "㊙️", + ["congratulations"] = "㊗️", + ["u5408"] = "🈴", + ["u6e80"] = "🈵", + ["u5272"] = "🈹", + ["u7981"] = "🈲", + ["a"] = "🅰️", + ["b"] = "🅱️", + ["ab"] = "🆎", + ["cl"] = "🆑", + ["o2"] = "🅾️", + ["sos"] = "🆘", + ["x"] = "❌", + ["o"] = "⭕", + ["octagonal_sign"] = "🛑", + ["stop_sign"] = "🛑", + ["no_entry"] = "⛔", + ["name_badge"] = "📛", + ["no_entry_sign"] = "🚫", + ["100"] = "💯", + ["anger"] = "💢", + ["hotsprings"] = "♨️", + ["no_pedestrians"] = "🚷", + ["do_not_litter"] = "🚯", + ["no_bicycles"] = "🚳", + ["non_potable_water"] = "🚱", + ["underage"] = "🔞", + ["no_mobile_phones"] = "📵", + ["no_smoking"] = "🚭", + ["exclamation"] = "❗", + ["grey_exclamation"] = "❕", + ["question"] = "❓", + ["grey_question"] = "❔", + ["bangbang"] = "‼️", + ["interrobang"] = "⁉️", + ["low_brightness"] = "🔅", + ["high_brightness"] = "🔆", + ["part_alternation_mark"] = "〽️", + ["warning"] = "⚠️", + ["children_crossing"] = "🚸", + ["trident"] = "🔱", + ["fleur_de_lis"] = "⚜️", + ["beginner"] = "🔰", + ["recycle"] = "♻️", + ["white_check_mark"] = "✅", + ["u6307"] = "🈯", + ["chart"] = "💹", + ["sparkle"] = "❇️", + ["eight_spoked_asterisk"] = "✳️", + ["negative_squared_cross_mark"] = "❎", + ["globe_with_meridians"] = "🌐", + ["diamond_shape_with_a_dot_inside"] = "💠", + ["m"] = "Ⓜ️", + ["cyclone"] = "🌀", + ["zzz"] = "💤", + ["atm"] = "🏧", + ["wc"] = "🚾", + ["wheelchair"] = "♿", + ["parking"] = "🅿️", + ["u7a7a"] = "🈳", + ["sa"] = "🈂️", + ["passport_control"] = "🛂", + ["customs"] = "🛃", + ["baggage_claim"] = "🛄", + ["left_luggage"] = "🛅", + ["elevator"] = "🛗", + ["mens"] = "🚹", + ["womens"] = "🚺", + ["baby_symbol"] = "🚼", + ["restroom"] = "🚻", + ["put_litter_in_its_place"] = "🚮", + ["cinema"] = "🎦", + ["signal_strength"] = "📶", + ["koko"] = "🈁", + ["symbols"] = "🔣", + ["information_source"] = "ℹ️", + ["abc"] = "🔤", + ["abcd"] = "🔡", + ["capital_abcd"] = "🔠", + ["ng"] = "🆖", + ["ok"] = "🆗", + ["up"] = "🆙", + ["cool"] = "🆒", + ["new"] = "🆕", + ["free"] = "🆓", + ["zero"] = "0️⃣", + ["one"] = "1️⃣", + ["two"] = "2️⃣", + ["three"] = "3️⃣", + ["four"] = "4️⃣", + ["five"] = "5️⃣", + ["six"] = "6️⃣", + ["seven"] = "7️⃣", + ["eight"] = "8️⃣", + ["nine"] = "9️⃣", + ["keycap_ten"] = "🔟", + ["1234"] = "🔢", + ["hash"] = "#️⃣", + ["asterisk"] = "*️⃣", + ["keycap_asterisk"] = "*️⃣", + ["eject"] = "⏏️", + ["eject_symbol"] = "⏏️", + ["arrow_forward"] = "▶️", + ["pause_button"] = "⏸️", + ["double_vertical_bar"] = "⏸️", + ["play_pause"] = "⏯️", + ["stop_button"] = "⏹️", + ["record_button"] = "⏺️", + ["track_next"] = "⏭️", + ["next_track"] = "⏭️", + ["track_previous"] = "⏮️", + ["previous_track"] = "⏮️", + ["fast_forward"] = "⏩", + ["rewind"] = "⏪", + ["arrow_double_up"] = "⏫", + ["arrow_double_down"] = "⏬", + ["arrow_backward"] = "◀️", + ["arrow_up_small"] = "🔼", + ["arrow_down_small"] = "🔽", + ["arrow_right"] = "➡️", + ["arrow_left"] = "⬅️", + ["arrow_up"] = "⬆️", + ["arrow_down"] = "⬇️", + ["arrow_upper_right"] = "↗️", + ["arrow_lower_right"] = "↘️", + ["arrow_lower_left"] = "↙️", + ["arrow_upper_left"] = "↖️", + ["arrow_up_down"] = "↕️", + ["left_right_arrow"] = "↔️", + ["arrow_right_hook"] = "↪️", + ["leftwards_arrow_with_hook"] = "↩️", + ["arrow_heading_up"] = "⤴️", + ["arrow_heading_down"] = "⤵️", + ["twisted_rightwards_arrows"] = "🔀", + ["repeat"] = "🔁", + ["repeat_one"] = "🔂", + ["arrows_counterclockwise"] = "🔄", + ["arrows_clockwise"] = "🔃", + ["musical_note"] = "🎵", + ["notes"] = "🎶", + ["heavy_plus_sign"] = "➕", + ["heavy_minus_sign"] = "➖", + ["heavy_division_sign"] = "➗", + ["heavy_multiplication_x"] = "✖️", + ["infinity"] = "♾️", + ["heavy_dollar_sign"] = "💲", + ["currency_exchange"] = "💱", + ["tm"] = "™️", + ["copyright"] = "©️", + ["registered"] = "®️", + ["wavy_dash"] = "〰️", + ["curly_loop"] = "➰", + ["loop"] = "➿", + ["end"] = "🔚", + ["back"] = "🔙", + ["on"] = "🔛", + ["top"] = "🔝", + ["soon"] = "🔜", + ["heavy_check_mark"] = "✔️", + ["ballot_box_with_check"] = "☑️", + ["radio_button"] = "🔘", + ["white_circle"] = "⚪", + ["black_circle"] = "⚫", + ["red_circle"] = "🔴", + ["blue_circle"] = "🔵", + ["brown_circle"] = "🟤", + ["purple_circle"] = "🟣", + ["green_circle"] = "🟢", + ["yellow_circle"] = "🟡", + ["orange_circle"] = "🟠", + ["small_red_triangle"] = "🔺", + ["small_red_triangle_down"] = "🔻", + ["small_orange_diamond"] = "🔸", + ["small_blue_diamond"] = "🔹", + ["large_orange_diamond"] = "🔶", + ["large_blue_diamond"] = "🔷", + ["white_square_button"] = "🔳", + ["black_square_button"] = "🔲", + ["black_small_square"] = "▪️", + ["white_small_square"] = "▫️", + ["black_medium_small_square"] = "◾", + ["white_medium_small_square"] = "◽", + ["black_medium_square"] = "◼️", + ["white_medium_square"] = "◻️", + ["black_large_square"] = "⬛", + ["white_large_square"] = "⬜", + ["orange_square"] = "🟧", + ["blue_square"] = "🟦", + ["red_square"] = "🟥", + ["brown_square"] = "🟫", + ["purple_square"] = "🟪", + ["green_square"] = "🟩", + ["yellow_square"] = "🟨", + ["speaker"] = "🔈", + ["mute"] = "🔇", + ["sound"] = "🔉", + ["loud_sound"] = "🔊", + ["bell"] = "🔔", + ["no_bell"] = "🔕", + ["mega"] = "📣", + ["loudspeaker"] = "📢", + ["speech_left"] = "🗨️", + ["left_speech_bubble"] = "🗨️", + ["eye_in_speech_bubble"] = "👁‍🗨", + ["speech_balloon"] = "💬", + ["thought_balloon"] = "💭", + ["anger_right"] = "🗯️", + ["right_anger_bubble"] = "🗯️", + ["spades"] = "♠️", + ["clubs"] = "♣️", + ["hearts"] = "♥️", + ["diamonds"] = "♦️", + ["black_joker"] = "🃏", + ["flower_playing_cards"] = "🎴", + ["mahjong"] = "🀄", + ["clock1"] = "🕐", + ["clock2"] = "🕑", + ["clock3"] = "🕒", + ["clock4"] = "🕓", + ["clock5"] = "🕔", + ["clock6"] = "🕕", + ["clock7"] = "🕖", + ["clock8"] = "🕗", + ["clock9"] = "🕘", + ["clock10"] = "🕙", + ["clock11"] = "🕚", + ["clock12"] = "🕛", + ["clock130"] = "🕜", + ["clock230"] = "🕝", + ["clock330"] = "🕞", + ["clock430"] = "🕟", + ["clock530"] = "🕠", + ["clock630"] = "🕡", + ["clock730"] = "🕢", + ["clock830"] = "🕣", + ["clock930"] = "🕤", + ["clock1030"] = "🕥", + ["clock1130"] = "🕦", + ["clock1230"] = "🕧", + ["female_sign"] = "♀️", + ["male_sign"] = "♂️", + ["transgender_symbol"] = "⚧", + ["medical_symbol"] = "⚕️", + ["regional_indicator_z"] = "🇿", + ["regional_indicator_y"] = "🇾", + ["regional_indicator_x"] = "🇽", + ["regional_indicator_w"] = "🇼", + ["regional_indicator_v"] = "🇻", + ["regional_indicator_u"] = "🇺", + ["regional_indicator_t"] = "🇹", + ["regional_indicator_s"] = "🇸", + ["regional_indicator_r"] = "🇷", + ["regional_indicator_q"] = "🇶", + ["regional_indicator_p"] = "🇵", + ["regional_indicator_o"] = "🇴", + ["regional_indicator_n"] = "🇳", + ["regional_indicator_m"] = "🇲", + ["regional_indicator_l"] = "🇱", + ["regional_indicator_k"] = "🇰", + ["regional_indicator_j"] = "🇯", + ["regional_indicator_i"] = "🇮", + ["regional_indicator_h"] = "🇭", + ["regional_indicator_g"] = "🇬", + ["regional_indicator_f"] = "🇫", + ["regional_indicator_e"] = "🇪", + ["regional_indicator_d"] = "🇩", + ["regional_indicator_c"] = "🇨", + ["regional_indicator_b"] = "🇧", + ["regional_indicator_a"] = "🇦", + ["flag_white"] = "🏳️", + ["flag_black"] = "🏴", + ["checkered_flag"] = "🏁", + ["triangular_flag_on_post"] = "🚩", + ["rainbow_flag"] = "🏳️‍🌈", + ["gay_pride_flag"] = "🏳️‍🌈", + ["transgender_flag"] = "🏳️‍⚧️", + ["pirate_flag"] = "🏴‍☠️", + ["flag_af"] = "🇦🇫", + ["flag_ax"] = "🇦🇽", + ["flag_al"] = "🇦🇱", + ["flag_dz"] = "🇩🇿", + ["flag_as"] = "🇦🇸", + ["flag_ad"] = "🇦🇩", + ["flag_ao"] = "🇦🇴", + ["flag_ai"] = "🇦🇮", + ["flag_aq"] = "🇦🇶", + ["flag_ag"] = "🇦🇬", + ["flag_ar"] = "🇦🇷", + ["flag_am"] = "🇦🇲", + ["flag_aw"] = "🇦🇼", + ["flag_au"] = "🇦🇺", + ["flag_at"] = "🇦🇹", + ["flag_az"] = "🇦🇿", + ["flag_bs"] = "🇧🇸", + ["flag_bh"] = "🇧🇭", + ["flag_bd"] = "🇧🇩", + ["flag_bb"] = "🇧🇧", + ["flag_by"] = "🇧🇾", + ["flag_be"] = "🇧🇪", + ["flag_bz"] = "🇧🇿", + ["flag_bj"] = "🇧🇯", + ["flag_bm"] = "🇧🇲", + ["flag_bt"] = "🇧🇹", + ["flag_bo"] = "🇧🇴", + ["flag_ba"] = "🇧🇦", + ["flag_bw"] = "🇧🇼", + ["flag_br"] = "🇧🇷", + ["flag_io"] = "🇮🇴", + ["flag_vg"] = "🇻🇬", + ["flag_bn"] = "🇧🇳", + ["flag_bg"] = "🇧🇬", + ["flag_bf"] = "🇧🇫", + ["flag_bi"] = "🇧🇮", + ["flag_kh"] = "🇰🇭", + ["flag_cm"] = "🇨🇲", + ["flag_ca"] = "🇨🇦", + ["flag_ic"] = "🇮🇨", + ["flag_cv"] = "🇨🇻", + ["flag_bq"] = "🇧🇶", + ["flag_ky"] = "🇰🇾", + ["flag_cf"] = "🇨🇫", + ["flag_td"] = "🇹🇩", + ["flag_cl"] = "🇨🇱", + ["flag_cn"] = "🇨🇳", + ["flag_cx"] = "🇨🇽", + ["flag_cc"] = "🇨🇨", + ["flag_co"] = "🇨🇴", + ["flag_km"] = "🇰🇲", + ["flag_cg"] = "🇨🇬", + ["flag_cd"] = "🇨🇩", + ["flag_ck"] = "🇨🇰", + ["flag_cr"] = "🇨🇷", + ["flag_ci"] = "🇨🇮", + ["flag_hr"] = "🇭🇷", + ["flag_cu"] = "🇨🇺", + ["flag_cw"] = "🇨🇼", + ["flag_cy"] = "🇨🇾", + ["flag_cz"] = "🇨🇿", + ["flag_dk"] = "🇩🇰", + ["flag_dj"] = "🇩🇯", + ["flag_dm"] = "🇩🇲", + ["flag_do"] = "🇩🇴", + ["flag_ec"] = "🇪🇨", + ["flag_eg"] = "🇪🇬", + ["flag_sv"] = "🇸🇻", + ["flag_gq"] = "🇬🇶", + ["flag_er"] = "🇪🇷", + ["flag_ee"] = "🇪🇪", + ["flag_et"] = "🇪🇹", + ["flag_eu"] = "🇪🇺", + ["flag_fk"] = "🇫🇰", + ["flag_fo"] = "🇫🇴", + ["flag_fj"] = "🇫🇯", + ["flag_fi"] = "🇫🇮", + ["flag_fr"] = "🇫🇷", + ["flag_gf"] = "🇬🇫", + ["flag_pf"] = "🇵🇫", + ["flag_tf"] = "🇹🇫", + ["flag_ga"] = "🇬🇦", + ["flag_gm"] = "🇬🇲", + ["flag_ge"] = "🇬🇪", + ["flag_de"] = "🇩🇪", + ["flag_gh"] = "🇬🇭", + ["flag_gi"] = "🇬🇮", + ["flag_gr"] = "🇬🇷", + ["flag_gl"] = "🇬🇱", + ["flag_gd"] = "🇬🇩", + ["flag_gp"] = "🇬🇵", + ["flag_gu"] = "🇬🇺", + ["flag_gt"] = "🇬🇹", + ["flag_gg"] = "🇬🇬", + ["flag_gn"] = "🇬🇳", + ["flag_gw"] = "🇬🇼", + ["flag_gy"] = "🇬🇾", + ["flag_ht"] = "🇭🇹", + ["flag_hn"] = "🇭🇳", + ["flag_hk"] = "🇭🇰", + ["flag_hu"] = "🇭🇺", + ["flag_is"] = "🇮🇸", + ["flag_in"] = "🇮🇳", + ["flag_id"] = "🇮🇩", + ["flag_ir"] = "🇮🇷", + ["flag_iq"] = "🇮🇶", + ["flag_ie"] = "🇮🇪", + ["flag_im"] = "🇮🇲", + ["flag_il"] = "🇮🇱", + ["flag_it"] = "🇮🇹", + ["flag_jm"] = "🇯🇲", + ["flag_jp"] = "🇯🇵", + ["crossed_flags"] = "🎌", + ["flag_je"] = "🇯🇪", + ["flag_jo"] = "🇯🇴", + ["flag_kz"] = "🇰🇿", + ["flag_ke"] = "🇰🇪", + ["flag_ki"] = "🇰🇮", + ["flag_xk"] = "🇽🇰", + ["flag_kw"] = "🇰🇼", + ["flag_kg"] = "🇰🇬", + ["flag_la"] = "🇱🇦", + ["flag_lv"] = "🇱🇻", + ["flag_lb"] = "🇱🇧", + ["flag_ls"] = "🇱🇸", + ["flag_lr"] = "🇱🇷", + ["flag_ly"] = "🇱🇾", + ["flag_li"] = "🇱🇮", + ["flag_lt"] = "🇱🇹", + ["flag_lu"] = "🇱🇺", + ["flag_mo"] = "🇲🇴", + ["flag_mk"] = "🇲🇰", + ["flag_mg"] = "🇲🇬", + ["flag_mw"] = "🇲🇼", + ["flag_my"] = "🇲🇾", + ["flag_mv"] = "🇲🇻", + ["flag_ml"] = "🇲🇱", + ["flag_mt"] = "🇲🇹", + ["flag_mh"] = "🇲🇭", + ["flag_mq"] = "🇲🇶", + ["flag_mr"] = "🇲🇷", + ["flag_mu"] = "🇲🇺", + ["flag_yt"] = "🇾🇹", + ["flag_mx"] = "🇲🇽", + ["flag_fm"] = "🇫🇲", + ["flag_md"] = "🇲🇩", + ["flag_mc"] = "🇲🇨", + ["flag_mn"] = "🇲🇳", + ["flag_me"] = "🇲🇪", + ["flag_ms"] = "🇲🇸", + ["flag_ma"] = "🇲🇦", + ["flag_mz"] = "🇲🇿", + ["flag_mm"] = "🇲🇲", + ["flag_na"] = "🇳🇦", + ["flag_nr"] = "🇳🇷", + ["flag_np"] = "🇳🇵", + ["flag_nl"] = "🇳🇱", + ["flag_nc"] = "🇳🇨", + ["flag_nz"] = "🇳🇿", + ["flag_ni"] = "🇳🇮", + ["flag_ne"] = "🇳🇪", + ["flag_ng"] = "🇳🇬", + ["flag_nu"] = "🇳🇺", + ["flag_nf"] = "🇳🇫", + ["flag_kp"] = "🇰🇵", + ["flag_mp"] = "🇲🇵", + ["flag_no"] = "🇳🇴", + ["flag_om"] = "🇴🇲", + ["flag_pk"] = "🇵🇰", + ["flag_pw"] = "🇵🇼", + ["flag_ps"] = "🇵🇸", + ["flag_pa"] = "🇵🇦", + ["flag_pg"] = "🇵🇬", + ["flag_py"] = "🇵🇾", + ["flag_pe"] = "🇵🇪", + ["flag_ph"] = "🇵🇭", + ["flag_pn"] = "🇵🇳", + ["flag_pl"] = "🇵🇱", + ["flag_pt"] = "🇵🇹", + ["flag_pr"] = "🇵🇷", + ["flag_qa"] = "🇶🇦", + ["flag_re"] = "🇷🇪", + ["flag_ro"] = "🇷🇴", + ["flag_ru"] = "🇷🇺", + ["flag_rw"] = "🇷🇼", + ["flag_ws"] = "🇼🇸", + ["flag_sm"] = "🇸🇲", + ["flag_st"] = "🇸🇹", + ["flag_sa"] = "🇸🇦", + ["flag_sn"] = "🇸🇳", + ["flag_rs"] = "🇷🇸", + ["flag_sc"] = "🇸🇨", + ["flag_sl"] = "🇸🇱", + ["flag_sg"] = "🇸🇬", + ["flag_sx"] = "🇸🇽", + ["flag_sk"] = "🇸🇰", + ["flag_si"] = "🇸🇮", + ["flag_gs"] = "🇬🇸", + ["flag_sb"] = "🇸🇧", + ["flag_so"] = "🇸🇴", + ["flag_za"] = "🇿🇦", + ["flag_kr"] = "🇰🇷", + ["flag_ss"] = "🇸🇸", + ["flag_es"] = "🇪🇸", + ["flag_lk"] = "🇱🇰", + ["flag_bl"] = "🇧🇱", + ["flag_sh"] = "🇸🇭", + ["flag_kn"] = "🇰🇳", + ["flag_lc"] = "🇱🇨", + ["flag_pm"] = "🇵🇲", + ["flag_vc"] = "🇻🇨", + ["flag_sd"] = "🇸🇩", + ["flag_sr"] = "🇸🇷", + ["flag_sz"] = "🇸🇿", + ["flag_se"] = "🇸🇪", + ["flag_ch"] = "🇨🇭", + ["flag_sy"] = "🇸🇾", + ["flag_tw"] = "🇹🇼", + ["flag_tj"] = "🇹🇯", + ["flag_tz"] = "🇹🇿", + ["flag_th"] = "🇹🇭", + ["flag_tl"] = "🇹🇱", + ["flag_tg"] = "🇹🇬", + ["flag_tk"] = "🇹🇰", + ["flag_to"] = "🇹🇴", + ["flag_tt"] = "🇹🇹", + ["flag_tn"] = "🇹🇳", + ["flag_tr"] = "🇹🇷", + ["flag_tm"] = "🇹🇲", + ["flag_tc"] = "🇹🇨", + ["flag_vi"] = "🇻🇮", + ["flag_tv"] = "🇹🇻", + ["flag_ug"] = "🇺🇬", + ["flag_ua"] = "🇺🇦", + ["flag_ae"] = "🇦🇪", + ["flag_gb"] = "🇬🇧", + ["england"] = "🏴󠁧󠁢󠁥󠁮󠁧󠁿", + ["scotland"] = "🏴󠁧󠁢󠁳󠁣󠁴󠁿", + ["wales"] = "🏴󠁧󠁢󠁷󠁬󠁳󠁿", + ["flag_us"] = "🇺🇸", + ["flag_uy"] = "🇺🇾", + ["flag_uz"] = "🇺🇿", + ["flag_vu"] = "🇻🇺", + ["flag_va"] = "🇻🇦", + ["flag_ve"] = "🇻🇪", + ["flag_vn"] = "🇻🇳", + ["flag_wf"] = "🇼🇫", + ["flag_eh"] = "🇪🇭", + ["flag_ye"] = "🇾🇪", + ["flag_zm"] = "🇿🇲", + ["flag_zw"] = "🇿🇼", + ["flag_ac"] = "🇦🇨", + ["flag_bv"] = "🇧🇻", + ["flag_cp"] = "🇨🇵", + ["flag_ea"] = "🇪🇦", + ["flag_dg"] = "🇩🇬", + ["flag_hm"] = "🇭🇲", + ["flag_mf"] = "🇲🇫", + ["flag_sj"] = "🇸🇯", + ["flag_ta"] = "🇹🇦", + ["flag_um"] = "🇺🇲", + ["united_nations"] = "🇺🇳", + }; + + public static IReadOnlyCollection GetAllNames() => _toCodes.Keys; public static string? TryGetCode(string name) => _toCodes.GetValueOrDefault(name); diff --git a/DiscordChatExporter.Core/Discord/Data/Guild.cs b/DiscordChatExporter.Core/Discord/Data/Guild.cs index 71a1236e..e9bdf9c5 100644 --- a/DiscordChatExporter.Core/Discord/Data/Guild.cs +++ b/DiscordChatExporter.Core/Discord/Data/Guild.cs @@ -1,14 +1,14 @@ -using System.Text.Json; +using System.Text.Json; using DiscordChatExporter.Core.Discord.Data.Common; -using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Reading; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Discord.Data; // https://discord.com/developers/docs/resources/guild#guild-object public partial record Guild(Snowflake Id, string Name, string IconUrl) : IHasId { - public bool IsDirect => Id == DirectMessages.Id; + public bool IsDirect { get; } = Id == Snowflake.Zero; } public partial record Guild @@ -25,7 +25,8 @@ public partial record Guild var iconUrl = json.GetPropertyOrNull("icon") ?.GetNonWhiteSpaceStringOrNull() - ?.Pipe(h => ImageCdn.GetGuildIconUrl(id, h)) ?? ImageCdn.GetFallbackUserAvatarUrl(); + ?.Pipe(h => ImageCdn.GetGuildIconUrl(id, h)) + ?? ImageCdn.GetFallbackUserAvatarUrl(); return new Guild(id, name, iconUrl); } diff --git a/DiscordChatExporter.Core/Discord/Data/Interaction.cs b/DiscordChatExporter.Core/Discord/Data/Interaction.cs index 3a020d40..da611e46 100644 --- a/DiscordChatExporter.Core/Discord/Data/Interaction.cs +++ b/DiscordChatExporter.Core/Discord/Data/Interaction.cs @@ -1,6 +1,6 @@ -using System.Text.Json; -using DiscordChatExporter.Core.Utils.Extensions; +using System.Text.Json; using JsonExtensions.Reading; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Discord.Data; diff --git a/DiscordChatExporter.Core/Discord/Data/Invite.cs b/DiscordChatExporter.Core/Discord/Data/Invite.cs index 17d077f4..e7093d60 100644 --- a/DiscordChatExporter.Core/Discord/Data/Invite.cs +++ b/DiscordChatExporter.Core/Discord/Data/Invite.cs @@ -1,7 +1,7 @@ -using System.Text.Json; +using System.Text.Json; using System.Text.RegularExpressions; -using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Reading; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Discord.Data; diff --git a/DiscordChatExporter.Core/Discord/Data/Member.cs b/DiscordChatExporter.Core/Discord/Data/Member.cs index ce92e569..6733cb13 100644 --- a/DiscordChatExporter.Core/Discord/Data/Member.cs +++ b/DiscordChatExporter.Core/Discord/Data/Member.cs @@ -1,10 +1,9 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; using DiscordChatExporter.Core.Discord.Data.Common; -using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Reading; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Discord.Data; @@ -16,13 +15,12 @@ public partial record Member( IReadOnlyList RoleIds ) : IHasId { - public Snowflake Id => User.Id; + public Snowflake Id { get; } = User.Id; } public partial record Member { - public static Member CreateFallback(User user) => - new(user, null, null, Array.Empty()); + public static Member CreateFallback(User user) => new(user, null, null, []); public static Member Parse(JsonElement json, Snowflake? guildId = null) { @@ -34,7 +32,8 @@ public partial record Member ?.EnumerateArray() .Select(j => j.GetNonWhiteSpaceString()) .Select(Snowflake.Parse) - .ToArray() ?? Array.Empty(); + .ToArray() + ?? []; var avatarUrl = guildId is not null ? json.GetPropertyOrNull("avatar") diff --git a/DiscordChatExporter.Core/Discord/Data/Message.cs b/DiscordChatExporter.Core/Discord/Data/Message.cs index f7616473..29465ab5 100644 --- a/DiscordChatExporter.Core/Discord/Data/Message.cs +++ b/DiscordChatExporter.Core/Discord/Data/Message.cs @@ -1,11 +1,11 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; using DiscordChatExporter.Core.Discord.Data.Common; using DiscordChatExporter.Core.Discord.Data.Embeds; -using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Reading; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Discord.Data; @@ -27,23 +27,26 @@ public partial record Message( IReadOnlyList MentionedUsers, MessageReference? Reference, Message? ReferencedMessage, + MessageSnapshot? ForwardedMessage, Interaction? Interaction ) : IHasId { - public bool IsSystemNotification => - Kind is >= MessageKind.RecipientAdd and <= MessageKind.ThreadCreated; - - public bool IsReply => Kind == MessageKind.Reply; - - // App interactions are rendered as replies in the Discord client, but they are not actually replies - public bool IsReplyLike => IsReply || Interaction is not null; - - public bool IsEmpty => + public bool IsEmpty { get; } = string.IsNullOrWhiteSpace(Content) && !Attachments.Any() && !Embeds.Any() && !Stickers.Any(); + public bool IsSystemNotification { get; } = + Kind is >= MessageKind.RecipientAdd and <= MessageKind.ThreadCreated; + + public bool IsReply { get; } = Kind == MessageKind.Reply; + + // App interactions are rendered as replies in the Discord client, but they are not actually replies + public bool IsReplyLike => IsReply || Interaction is not null; + + public bool IsForwarded { get; } = Reference?.Kind == MessageReferenceKind.Forward; + public IEnumerable GetReferencedUsers() { yield return Author; @@ -83,16 +86,15 @@ public partial record Message // Find embeds with the same URL that only contain a single image and nothing else var trailingEmbeds = embeds .Skip(i + 1) - .TakeWhile( - e => - e.Url == embed.Url - && e.Timestamp is null - && e.Author is null - && e.Color is null - && string.IsNullOrWhiteSpace(e.Description) - && !e.Fields.Any() - && e.Images.Count == 1 - && e.Footer is null + .TakeWhile(e => + e.Url == embed.Url + && e.Timestamp is null + && e.Author is null + && e.Color is null + && string.IsNullOrWhiteSpace(e.Description) + && !e.Fields.Any() + && e.Images.Count == 1 + && e.Footer is null ) .ToArray(); @@ -144,32 +146,45 @@ public partial record Message json.GetPropertyOrNull("attachments") ?.EnumerateArrayOrNull() ?.Select(Attachment.Parse) - .ToArray() ?? Array.Empty(); + .ToArray() + ?? []; var embeds = NormalizeEmbeds( json.GetPropertyOrNull("embeds")?.EnumerateArrayOrNull()?.Select(Embed.Parse).ToArray() - ?? Array.Empty() + ?? [] ); var stickers = json.GetPropertyOrNull("sticker_items") ?.EnumerateArrayOrNull() ?.Select(Sticker.Parse) - .ToArray() ?? Array.Empty(); + .ToArray() + ?? []; var reactions = json.GetPropertyOrNull("reactions") ?.EnumerateArrayOrNull() ?.Select(Reaction.Parse) - .ToArray() ?? Array.Empty(); + .ToArray() + ?? []; var mentionedUsers = json.GetPropertyOrNull("mentions")?.EnumerateArrayOrNull()?.Select(User.Parse).ToArray() - ?? Array.Empty(); + ?? []; var messageReference = json.GetPropertyOrNull("message_reference") ?.Pipe(MessageReference.Parse); + var referencedMessage = json.GetPropertyOrNull("referenced_message")?.Pipe(Parse); + + // Currently Discord only supports 1 snapshot per forward + var forwardedMessage = json.GetPropertyOrNull("message_snapshots") + ?.EnumerateArrayOrNull() + ?.Select(j => j.GetPropertyOrNull("message")) + .WhereNotNull() + .Select(MessageSnapshot.Parse) + .FirstOrDefault(); + var interaction = json.GetPropertyOrNull("interaction")?.Pipe(Interaction.Parse); return new Message( @@ -189,6 +204,7 @@ public partial record Message mentionedUsers, messageReference, referencedMessage, + forwardedMessage, interaction ); } diff --git a/DiscordChatExporter.Core/Discord/Data/MessageFlags.cs b/DiscordChatExporter.Core/Discord/Data/MessageFlags.cs index cb12eace..a0d86f95 100644 --- a/DiscordChatExporter.Core/Discord/Data/MessageFlags.cs +++ b/DiscordChatExporter.Core/Discord/Data/MessageFlags.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace DiscordChatExporter.Core.Discord.Data; @@ -14,5 +14,5 @@ public enum MessageFlags Urgent = 16, HasThread = 32, Ephemeral = 64, - Loading = 128 + Loading = 128, } diff --git a/DiscordChatExporter.Core/Discord/Data/MessageKind.cs b/DiscordChatExporter.Core/Discord/Data/MessageKind.cs index 411238b5..c2aba207 100644 --- a/DiscordChatExporter.Core/Discord/Data/MessageKind.cs +++ b/DiscordChatExporter.Core/Discord/Data/MessageKind.cs @@ -1,4 +1,4 @@ -namespace DiscordChatExporter.Core.Discord.Data; +namespace DiscordChatExporter.Core.Discord.Data; // https://discord.com/developers/docs/resources/channel#message-object-message-types public enum MessageKind @@ -12,5 +12,5 @@ public enum MessageKind ChannelPinnedMessage = 6, GuildMemberJoin = 7, ThreadCreated = 18, - Reply = 19 + Reply = 19, } diff --git a/DiscordChatExporter.Core/Discord/Data/MessageReference.cs b/DiscordChatExporter.Core/Discord/Data/MessageReference.cs index 6028e942..b6f2bf56 100644 --- a/DiscordChatExporter.Core/Discord/Data/MessageReference.cs +++ b/DiscordChatExporter.Core/Discord/Data/MessageReference.cs @@ -1,14 +1,23 @@ using System.Text.Json; -using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Reading; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Discord.Data; // https://discord.com/developers/docs/resources/channel#message-object-message-reference-structure -public record MessageReference(Snowflake? MessageId, Snowflake? ChannelId, Snowflake? GuildId) +public record MessageReference( + MessageReferenceKind Kind, + Snowflake? MessageId, + Snowflake? ChannelId, + Snowflake? GuildId +) { public static MessageReference Parse(JsonElement json) { + var kind = + json.GetPropertyOrNull("type")?.GetInt32OrNull()?.Pipe(t => (MessageReferenceKind)t) + ?? MessageReferenceKind.Default; + var messageId = json.GetPropertyOrNull("message_id") ?.GetNonWhiteSpaceStringOrNull() ?.Pipe(Snowflake.Parse); @@ -21,6 +30,6 @@ public record MessageReference(Snowflake? MessageId, Snowflake? ChannelId, Snowf ?.GetNonWhiteSpaceStringOrNull() ?.Pipe(Snowflake.Parse); - return new MessageReference(messageId, channelId, guildId); + return new MessageReference(kind, messageId, channelId, guildId); } } diff --git a/DiscordChatExporter.Core/Discord/Data/MessageReferenceKind.cs b/DiscordChatExporter.Core/Discord/Data/MessageReferenceKind.cs new file mode 100644 index 00000000..6576a283 --- /dev/null +++ b/DiscordChatExporter.Core/Discord/Data/MessageReferenceKind.cs @@ -0,0 +1,8 @@ +namespace DiscordChatExporter.Core.Discord.Data; + +// https://discord.com/developers/docs/resources/channel#message-reference-types +public enum MessageReferenceKind +{ + Default = 0, + Forward = 1, +} diff --git a/DiscordChatExporter.Core/Discord/Data/MessageSnapshot.cs b/DiscordChatExporter.Core/Discord/Data/MessageSnapshot.cs new file mode 100644 index 00000000..1df2ac47 --- /dev/null +++ b/DiscordChatExporter.Core/Discord/Data/MessageSnapshot.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using DiscordChatExporter.Core.Discord.Data.Embeds; +using JsonExtensions.Reading; + +namespace DiscordChatExporter.Core.Discord.Data; + +// https://docs.discord.com/developers/resources/message#message-snapshot-object +public record MessageSnapshot( + DateTimeOffset Timestamp, + DateTimeOffset? EditedTimestamp, + string Content, + IReadOnlyList Attachments, + IReadOnlyList Embeds, + IReadOnlyList Stickers +) +{ + public static MessageSnapshot Parse(JsonElement json) + { + var timestamp = + json.GetPropertyOrNull("timestamp")?.GetDateTimeOffsetOrNull() + ?? DateTimeOffset.MinValue; + + var editedTimestamp = json.GetPropertyOrNull("edited_timestamp")?.GetDateTimeOffsetOrNull(); + + var content = json.GetPropertyOrNull("content")?.GetStringOrNull() ?? ""; + + var attachments = + json.GetPropertyOrNull("attachments") + ?.EnumerateArrayOrNull() + ?.Select(Attachment.Parse) + .ToArray() + ?? []; + + var embeds = + json.GetPropertyOrNull("embeds")?.EnumerateArrayOrNull()?.Select(Embed.Parse).ToArray() + ?? []; + + var stickers = + json.GetPropertyOrNull("sticker_items") + ?.EnumerateArrayOrNull() + ?.Select(Sticker.Parse) + .ToArray() + ?? []; + + return new MessageSnapshot( + timestamp, + editedTimestamp, + content, + attachments, + embeds, + stickers + ); + } +} diff --git a/DiscordChatExporter.Core/Discord/Data/Reaction.cs b/DiscordChatExporter.Core/Discord/Data/Reaction.cs index 21e9c712..aab83ab9 100644 --- a/DiscordChatExporter.Core/Discord/Data/Reaction.cs +++ b/DiscordChatExporter.Core/Discord/Data/Reaction.cs @@ -1,5 +1,5 @@ -using System.Text.Json; -using DiscordChatExporter.Core.Utils.Extensions; +using System.Text.Json; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Discord.Data; diff --git a/DiscordChatExporter.Core/Discord/Data/Role.cs b/DiscordChatExporter.Core/Discord/Data/Role.cs index 86616eff..009b223a 100644 --- a/DiscordChatExporter.Core/Discord/Data/Role.cs +++ b/DiscordChatExporter.Core/Discord/Data/Role.cs @@ -1,8 +1,8 @@ -using System.Drawing; +using System.Drawing; using System.Text.Json; using DiscordChatExporter.Core.Discord.Data.Common; -using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Reading; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Discord.Data; @@ -18,7 +18,7 @@ public record Role(Snowflake Id, string Name, int Position, Color? Color) : IHas var color = json.GetPropertyOrNull("color") ?.GetInt32OrNull() ?.Pipe(System.Drawing.Color.FromArgb) - .ResetAlpha() + .WithFullAlpha() .NullIf(c => c.ToRgb() <= 0); return new Role(id, name, position, color); diff --git a/DiscordChatExporter.Core/Discord/Data/Sticker.cs b/DiscordChatExporter.Core/Discord/Data/Sticker.cs index 63462d84..1fb32cad 100644 --- a/DiscordChatExporter.Core/Discord/Data/Sticker.cs +++ b/DiscordChatExporter.Core/Discord/Data/Sticker.cs @@ -1,15 +1,15 @@ -using System; +using System; using System.Text.Json; using DiscordChatExporter.Core.Discord.Data.Common; -using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Reading; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Discord.Data; // https://discord.com/developers/docs/resources/sticker#sticker-resource public partial record Sticker(Snowflake Id, string Name, StickerFormat Format, string SourceUrl) { - public bool IsImage => Format != StickerFormat.Lottie; + public bool IsImage { get; } = Format != StickerFormat.Lottie; } public partial record Sticker @@ -28,7 +28,7 @@ public partial record Sticker StickerFormat.Apng => "png", StickerFormat.Lottie => "json", StickerFormat.Gif => "gif", - _ => throw new InvalidOperationException($"Unknown sticker format '{format}'.") + _ => throw new InvalidOperationException($"Unknown sticker format '{format}'."), } ); diff --git a/DiscordChatExporter.Core/Discord/Data/StickerFormat.cs b/DiscordChatExporter.Core/Discord/Data/StickerFormat.cs index a1a78d48..54dd9ace 100644 --- a/DiscordChatExporter.Core/Discord/Data/StickerFormat.cs +++ b/DiscordChatExporter.Core/Discord/Data/StickerFormat.cs @@ -1,9 +1,9 @@ -namespace DiscordChatExporter.Core.Discord.Data; +namespace DiscordChatExporter.Core.Discord.Data; public enum StickerFormat { Png = 1, Apng = 2, Lottie = 3, - Gif = 4 + Gif = 4, } diff --git a/DiscordChatExporter.Core/Discord/Data/User.cs b/DiscordChatExporter.Core/Discord/Data/User.cs index 33012292..3a33a0c9 100644 --- a/DiscordChatExporter.Core/Discord/Data/User.cs +++ b/DiscordChatExporter.Core/Discord/Data/User.cs @@ -1,7 +1,7 @@ -using System.Text.Json; +using System.Text.Json; using DiscordChatExporter.Core.Discord.Data.Common; -using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Reading; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Discord.Data; @@ -18,7 +18,7 @@ public partial record User( string AvatarUrl ) : IHasId { - public string DiscriminatorFormatted => + public string DiscriminatorFormatted { get; } = Discriminator is not null ? $"{Discriminator:0000}" : "0000"; // This effectively represents the user's true identity. diff --git a/DiscordChatExporter.Core/Discord/DiscordClient.cs b/DiscordChatExporter.Core/Discord/DiscordClient.cs index a3f14cb1..f435e282 100644 --- a/DiscordChatExporter.Core/Discord/DiscordClient.cs +++ b/DiscordChatExporter.Core/Discord/DiscordClient.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -11,14 +11,17 @@ using System.Threading.Tasks; using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Exceptions; using DiscordChatExporter.Core.Utils; -using DiscordChatExporter.Core.Utils.Extensions; using Gress; using JsonExtensions.Http; using JsonExtensions.Reading; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Discord; -public class DiscordClient(string token) +public class DiscordClient( + string token, + RateLimitPreference rateLimitPreference = RateLimitPreference.RespectAll +) { private readonly Uri _baseUri = new("https://discord.com/api/v10/", UriKind.Absolute); private TokenKind? _resolvedTokenKind; @@ -47,33 +50,41 @@ public class DiscordClient(string token) innerCancellationToken ); - // If this was the last request available before hitting the rate limit, - // wait out the reset time so that future requests can succeed. - // This may add an unnecessary delay in case the user doesn't intend to - // make any more requests, but implementing a smarter solution would - // require properly keeping track of Discord's global/per-route/per-resource - // rate limits and that's just way too much effort. - // https://discord.com/developers/docs/topics/rate-limits - var remainingRequestCount = response - .Headers.TryGetValue("X-RateLimit-Remaining") - ?.Pipe(s => int.Parse(s, CultureInfo.InvariantCulture)); - - var resetAfterDelay = response - .Headers.TryGetValue("X-RateLimit-Reset-After") - ?.Pipe(s => double.Parse(s, CultureInfo.InvariantCulture)) - .Pipe(TimeSpan.FromSeconds); - - if (remainingRequestCount <= 0 && resetAfterDelay is not null) + // Discord has advisory rate limits (communicated via response headers), but they are typically + // way stricter than the actual rate limits enforced by the server. + // The user may choose to ignore the advisory rate limits and only retry on hard rate limits, + // if they want to prioritize speed over compliance (and safety of their account/bot). + // https://github.com/Tyrrrz/DiscordChatExporter/issues/1021 + if (rateLimitPreference.IsRespectedFor(tokenKind)) { - var delay = - // Adding a small buffer to the reset time reduces the chance of getting - // rate limited again, because it allows for more requests to be released. - (resetAfterDelay.Value + TimeSpan.FromSeconds(1)) - // Sometimes Discord returns an absurdly high value for the reset time, which - // is not actually enforced by the server. So we cap it at a reasonable value. - .Clamp(TimeSpan.Zero, TimeSpan.FromSeconds(60)); + var remainingRequestCount = response + .Headers.TryGetValue("X-RateLimit-Remaining") + ?.Pipe(s => int.ParseOrNull(s, CultureInfo.InvariantCulture)); - await Task.Delay(delay, innerCancellationToken); + var resetAfterDelay = response + .Headers.TryGetValue("X-RateLimit-Reset-After") + ?.Pipe(s => double.ParseOrNull(s, CultureInfo.InvariantCulture)) + ?.Pipe(TimeSpan.FromSeconds); + + // If this was the last request available before hitting the rate limit, + // wait out the reset time so that future requests can succeed. + // This may add an unnecessary delay in case the user doesn't intend to + // make any more requests, but implementing a smarter solution would + // require properly keeping track of Discord's global/per-route/per-resource + // rate limits and that's just way too much effort. + // https://discord.com/developers/docs/topics/rate-limits + if (remainingRequestCount <= 0 && resetAfterDelay is not null) + { + var delay = + // Adding a small buffer to the reset time reduces the chance of getting + // rate limited again, because it allows for more requests to be released. + (resetAfterDelay.Value + TimeSpan.FromSeconds(1)) + // Sometimes Discord returns an absurdly high value for the reset time, which + // is not actually enforced by the server. So we cap it at a reasonable value. + .Clamp(TimeSpan.Zero, TimeSpan.FromSeconds(60)); + + await Task.Delay(delay, innerCancellationToken); + } } return response; @@ -133,30 +144,31 @@ public class DiscordClient(string token) { throw response.StatusCode switch { - HttpStatusCode.Unauthorized - => throw new DiscordChatExporterException( - "Authentication token is invalid.", - true - ), + HttpStatusCode.Unauthorized => throw new DiscordChatExporterException( + "Authentication token is invalid.", + true + ), - HttpStatusCode.Forbidden - => throw new DiscordChatExporterException( - $"Request to '{url}' failed: forbidden." - ), + HttpStatusCode.Forbidden => throw new DiscordChatExporterException( + $"Request to '{url}' failed: forbidden." + ), - HttpStatusCode.NotFound - => throw new DiscordChatExporterException( - $"Request to '{url}' failed: not found." - ), + HttpStatusCode.NotFound => throw new DiscordChatExporterException( + $"Request to '{url}' failed: not found." + ), - _ - => throw new DiscordChatExporterException( - $""" - Request to '{url}' failed: {response.StatusCode.ToString().ToSpaceSeparatedWords().ToLowerInvariant()}. - Response content: {await response.Content.ReadAsStringAsync(cancellationToken)} - """, - true - ) + _ => throw new DiscordChatExporterException( + $""" + Request to '{url}' failed: {response + .StatusCode.ToString() + .SeparateWords(' ') + .ToLowerInvariant()}. + Response content: {await response.Content.ReadAsStringAsync( + cancellationToken + )} + """, + true + ), }; } @@ -182,6 +194,23 @@ public class DiscordClient(string token) return Application.Parse(response); } + private async ValueTask EnsureMessageContentIntentAsync( + CancellationToken cancellationToken = default + ) + { + if (await ResolveTokenKindAsync(cancellationToken) != TokenKind.Bot) + return; + + var application = await GetApplicationAsync(cancellationToken); + if (application.IsMessageContentIntentEnabled) + return; + + throw new DiscordChatExporterException( + "Provided bot account is missing the MESSAGE_CONTENT privileged intent.", + true + ); + } + public async ValueTask TryGetUserAsync( Snowflake userId, CancellationToken cancellationToken = default @@ -293,184 +322,19 @@ public class DiscordClient(string token) if (guildId == Guild.DirectMessages.Id) yield break; - var channels = (await GetGuildChannelsAsync(guildId, cancellationToken)) - // Categories cannot have threads - .Where(c => !c.IsCategory) - // Voice channels cannot have threads - .Where(c => !c.IsVoice) - // Empty channels cannot have threads - .Where(c => !c.IsEmpty) - // If the 'before' boundary is specified, skip channels that don't have messages - // for that range, because thread-start event should always be accompanied by a message. - // Note that we don't perform a similar check for the 'after' boundary, because - // threads may have messages in range, even if the parent channel doesn't. - .Where(c => before is null || c.MayHaveMessagesBefore(before.Value)) - .ToArray(); + var channels = await GetGuildChannelsAsync(guildId, cancellationToken); - // User accounts can only fetch threads using the search endpoint - if (await ResolveTokenKindAsync(cancellationToken) == TokenKind.User) + foreach ( + var channel in await GetChannelThreadsAsync( + channels, + includeArchived, + before, + after, + cancellationToken + ) + ) { - // Active threads - foreach (var channel in channels) - { - var currentOffset = 0; - while (true) - { - var url = new UrlBuilder() - .SetPath($"channels/{channel.Id}/threads/search") - .SetQueryParameter("sort_by", "last_message_time") - .SetQueryParameter("sort_order", "desc") - .SetQueryParameter("archived", "false") - .SetQueryParameter("offset", currentOffset.ToString()) - .Build(); - - // Can be null on channels that the user cannot access or channels without threads - var response = await TryGetJsonResponseAsync(url, cancellationToken); - if (response is null) - break; - - var breakOuter = false; - - foreach ( - var threadJson in response.Value.GetProperty("threads").EnumerateArray() - ) - { - var thread = Channel.Parse(threadJson, channel); - - // If the 'after' boundary is specified, we can break early, - // because threads are sorted by last message time. - if (after is not null && !thread.MayHaveMessagesAfter(after.Value)) - { - breakOuter = true; - break; - } - - yield return thread; - currentOffset++; - } - - if (breakOuter) - break; - - if (!response.Value.GetProperty("has_more").GetBoolean()) - break; - } - } - - // Archived threads - if (includeArchived) - { - foreach (var channel in channels) - { - var currentOffset = 0; - while (true) - { - var url = new UrlBuilder() - .SetPath($"channels/{channel.Id}/threads/search") - .SetQueryParameter("sort_by", "last_message_time") - .SetQueryParameter("sort_order", "desc") - .SetQueryParameter("archived", "true") - .SetQueryParameter("offset", currentOffset.ToString()) - .Build(); - - // Can be null on channels that the user cannot access or channels without threads - var response = await TryGetJsonResponseAsync(url, cancellationToken); - if (response is null) - break; - - var breakOuter = false; - - foreach ( - var threadJson in response.Value.GetProperty("threads").EnumerateArray() - ) - { - var thread = Channel.Parse(threadJson, channel); - - // If the 'after' boundary is specified, we can break early, - // because threads are sorted by last message time. - if (after is not null && !thread.MayHaveMessagesAfter(after.Value)) - { - breakOuter = true; - break; - } - - yield return thread; - currentOffset++; - } - - if (breakOuter) - break; - - if (!response.Value.GetProperty("has_more").GetBoolean()) - break; - } - } - } - } - // Bot accounts can only fetch threads using the threads endpoint - else - { - // Active threads - { - var parentsById = channels.ToDictionary(c => c.Id); - - var response = await GetJsonResponseAsync( - $"guilds/{guildId}/threads/active", - cancellationToken - ); - - foreach (var threadJson in response.GetProperty("threads").EnumerateArray()) - { - var parent = threadJson - .GetPropertyOrNull("parent_id") - ?.GetNonWhiteSpaceStringOrNull() - ?.Pipe(Snowflake.Parse) - .Pipe(parentsById.GetValueOrDefault); - - yield return Channel.Parse(threadJson, parent); - } - } - - // Archived threads - if (includeArchived) - { - foreach (var channel in channels) - { - // Public archived threads - { - // Can be null on certain channels - var response = await TryGetJsonResponseAsync( - $"channels/{channel.Id}/threads/archived/public", - cancellationToken - ); - - if (response is null) - continue; - - foreach ( - var threadJson in response.Value.GetProperty("threads").EnumerateArray() - ) - yield return Channel.Parse(threadJson, channel); - } - - // Private archived threads - { - // Can be null on certain channels - var response = await TryGetJsonResponseAsync( - $"channels/{channel.Id}/threads/archived/private", - cancellationToken - ); - - if (response is null) - continue; - - foreach ( - var threadJson in response.Value.GetProperty("threads").EnumerateArray() - ) - yield return Channel.Parse(threadJson, channel); - } - } - } + yield return channel; } } @@ -524,21 +388,229 @@ public class DiscordClient(string token) ?.GetNonWhiteSpaceStringOrNull() ?.Pipe(Snowflake.Parse); - try - { - var parent = parentId is not null - ? await GetChannelAsync(parentId.Value, cancellationToken) - : null; - - return Channel.Parse(response, parent); - } // It's possible for the parent channel to be inaccessible, despite the // child channel being accessible. // https://github.com/Tyrrrz/DiscordChatExporter/issues/1108 - catch (DiscordChatExporterException) + var parent = parentId is not null + ? await TryGetChannelAsync(parentId.Value, cancellationToken) + : null; + + return Channel.Parse(response, parent); + } + + public async ValueTask TryGetChannelAsync( + Snowflake channelId, + CancellationToken cancellationToken = default + ) + { + var response = await TryGetJsonResponseAsync($"channels/{channelId}", cancellationToken); + if (response is null) + return null; + + var parentId = response + .Value.GetPropertyOrNull("parent_id") + ?.GetNonWhiteSpaceStringOrNull() + ?.Pipe(Snowflake.Parse); + + Channel? parent = null; + if (parentId is not null) { - return Channel.Parse(response); + // It's possible for the parent channel to be inaccessible, despite the + // child channel being accessible. + // https://github.com/Tyrrrz/DiscordChatExporter/issues/1108 + parent = await TryGetChannelAsync(parentId.Value, cancellationToken); } + + return Channel.Parse(response.Value, parent); + } + + public async IAsyncEnumerable GetChannelThreadsAsync( + IReadOnlyList channels, + bool includeArchived = false, + Snowflake? before = null, + Snowflake? after = null, + [EnumeratorCancellation] CancellationToken cancellationToken = default + ) + { + var filteredChannels = channels + // Categories cannot have threads + .Where(c => !c.IsCategory) + // Voice channels cannot have threads + .Where(c => !c.IsVoice) + // Empty channels cannot have threads + .Where(c => !c.IsEmpty) + // If the 'before' boundary is specified, skip channels that don't have messages + // for that range, because thread-start event should always be accompanied by a message. + // Note that we don't perform a similar check for the 'after' boundary, because + // threads may have messages in range, even if the parent channel doesn't. + .Where(c => before is null || c.MayHaveMessagesBefore(before.Value)) + .ToArray(); + + // Track yielded thread IDs to avoid duplicates that can occur when a thread transitions + // from active to archived between the two separate API calls used to fetch threads. + // https://github.com/Tyrrrz/DiscordChatExporter/issues/1433 + var seenThreadIds = new HashSet(); + + // User accounts can only fetch threads using the search endpoint + if (await ResolveTokenKindAsync(cancellationToken) == TokenKind.User) + { + foreach (var channel in filteredChannels) + { + // Either include both active and archived threads, or only active threads + foreach ( + var isArchived in includeArchived ? new[] { false, true } : new[] { false } + ) + { + // Offset is just the index of the last thread in the previous batch + var currentOffset = 0; + while (true) + { + var url = new UrlBuilder() + .SetPath($"channels/{channel.Id}/threads/search") + .SetQueryParameter("sort_by", "last_message_time") + .SetQueryParameter("sort_order", "desc") + .SetQueryParameter("archived", isArchived.ToString().ToLowerInvariant()) + .SetQueryParameter("offset", currentOffset.ToString()) + .Build(); + + // Can be null on channels that the user cannot access or channels without threads + var response = await TryGetJsonResponseAsync(url, cancellationToken); + if (response is null) + break; + + var breakOuter = false; + + foreach ( + var threadJson in response.Value.GetProperty("threads").EnumerateArray() + ) + { + var thread = Channel.Parse(threadJson, channel); + + // If the 'after' boundary is specified, we can break early, + // because threads are sorted by last message timestamp. + if (after is not null && !thread.MayHaveMessagesAfter(after.Value)) + { + breakOuter = true; + break; + } + + if (seenThreadIds.Add(thread.Id)) + yield return thread; + + currentOffset++; + } + + if (breakOuter) + break; + + if (!response.Value.GetProperty("has_more").GetBoolean()) + break; + } + } + } + } + // Bot accounts can only fetch threads using the threads endpoint + else + { + var guilds = new HashSet(); + foreach (var channel in filteredChannels) + guilds.Add(channel.GuildId); + + // Active threads + foreach (var guildId in guilds) + { + var parentsById = filteredChannels.ToDictionary(c => c.Id); + + var response = await GetJsonResponseAsync( + $"guilds/{guildId}/threads/active", + cancellationToken + ); + + foreach (var threadJson in response.GetProperty("threads").EnumerateArray()) + { + var parent = threadJson + .GetPropertyOrNull("parent_id") + ?.GetNonWhiteSpaceStringOrNull() + ?.Pipe(Snowflake.Parse) + .Pipe(parentsById.GetValueOrDefault); + + if (filteredChannels.Contains(parent)) + { + var thread = Channel.Parse(threadJson, parent); + + if (seenThreadIds.Add(thread.Id)) + yield return thread; + } + } + } + + // Archived threads + if (includeArchived) + { + foreach (var channel in filteredChannels) + { + foreach (var archiveType in new[] { "public", "private" }) + { + // This endpoint parameter expects an ISO8601 timestamp, not a snowflake + var currentBefore = before + ?.ToDate() + .ToString("O", CultureInfo.InvariantCulture); + + while (true) + { + // Threads are sorted by archive timestamp, not by last message timestamp + var url = new UrlBuilder() + .SetPath($"channels/{channel.Id}/threads/archived/{archiveType}") + .SetQueryParameter("before", currentBefore) + .Build(); + + // Can be null on certain channels + var response = await TryGetJsonResponseAsync(url, cancellationToken); + if (response is null) + break; + + foreach ( + var threadJson in response + .Value.GetProperty("threads") + .EnumerateArray() + ) + { + var thread = Channel.Parse(threadJson, channel); + + currentBefore = threadJson + .GetProperty("thread_metadata") + .GetProperty("archive_timestamp") + .GetString(); + + if (seenThreadIds.Add(thread.Id)) + yield return thread; + } + + if (!response.Value.GetProperty("has_more").GetBoolean()) + break; + } + } + } + } + } + } + + private async ValueTask TryGetFirstMessageAsync( + Snowflake channelId, + Snowflake? after = null, + CancellationToken cancellationToken = default + ) + { + var url = new UrlBuilder() + .SetPath($"channels/{channelId}/messages") + .SetQueryParameter("limit", "1") + .SetQueryParameter("after", (after ?? Snowflake.Zero).ToString()) + .Build(); + + var response = await GetJsonResponseAsync(url, cancellationToken); + var message = response.EnumerateArray().Select(Message.Parse).FirstOrDefault(); + + return message; } private async ValueTask TryGetLastMessageAsync( @@ -599,22 +671,10 @@ public class DiscordClient(string token) yield break; // If all messages are empty, make sure that it's not because the bot account doesn't - // have the Message Content Intent enabled. + // have the MESSAGE_CONTENT intent enabled. // https://github.com/Tyrrrz/DiscordChatExporter/issues/1106#issuecomment-1741548959 - if ( - messages.All(m => m.IsEmpty) - && await ResolveTokenKindAsync(cancellationToken) == TokenKind.Bot - ) - { - var application = await GetApplicationAsync(cancellationToken); - if (!application.IsMessageContentIntentEnabled) - { - throw new DiscordChatExporterException( - "Provided bot account does not have the Message Content Intent enabled.", - true - ); - } - } + if (messages.All(m => m.IsEmpty)) + await EnsureMessageContentIntentAsync(cancellationToken); foreach (var message in messages) { @@ -647,6 +707,75 @@ public class DiscordClient(string token) } } + public async IAsyncEnumerable GetMessagesInReverseAsync( + Snowflake channelId, + Snowflake? after = null, + Snowflake? before = null, + IProgress? progress = null, + [EnumeratorCancellation] CancellationToken cancellationToken = default + ) + { + // Get the first message in the specified range, so we can later calculate the + // progress based on the difference between message timestamps. + // Snapshotting is not necessary here because new messages can't appear in the past. + var firstMessage = await TryGetFirstMessageAsync(channelId, after, cancellationToken); + if (firstMessage is null || firstMessage.Timestamp > before?.ToDate()) + yield break; + + // Keep track of the last message in range in order to calculate the progress + var lastMessage = default(Message); + + var currentBefore = before; + while (true) + { + var url = new UrlBuilder() + .SetPath($"channels/{channelId}/messages") + .SetQueryParameter("limit", "100") + .SetQueryParameter("before", currentBefore?.ToString()) + .Build(); + + var response = await GetJsonResponseAsync(url, cancellationToken); + + var messages = response.EnumerateArray().Select(Message.Parse).ToArray(); + + // Break if there are no messages (can happen if messages are deleted during execution) + if (!messages.Any()) + yield break; + + // If all messages are empty, make sure that it's not because the bot account doesn't + // have the MESSAGE_CONTENT intent enabled. + // https://github.com/Tyrrrz/DiscordChatExporter/issues/1106#issuecomment-1741548959 + if (messages.All(m => m.IsEmpty)) + await EnsureMessageContentIntentAsync(cancellationToken); + + foreach (var message in messages) + { + lastMessage ??= message; + + // Report progress based on timestamps + if (progress is not null) + { + var exportedDuration = (lastMessage.Timestamp - message.Timestamp).Duration(); + var totalDuration = (lastMessage.Timestamp - firstMessage.Timestamp).Duration(); + + progress.Report( + Percentage.FromFraction( + // Avoid division by zero if all messages have the exact same timestamp + // (which happens when there's only one message in the channel) + totalDuration > TimeSpan.Zero + ? exportedDuration / totalDuration + : 1 + ) + ); + } + + yield return message; + } + + currentBefore = messages.Last().Id; + } + } + public async IAsyncEnumerable GetMessageReactionsAsync( Snowflake channelId, Snowflake messageId, @@ -671,10 +800,14 @@ public class DiscordClient(string token) .SetQueryParameter("after", currentAfter.ToString()) .Build(); - var response = await GetJsonResponseAsync(url, cancellationToken); + // Can be null on reactions with an emoji that has been deleted (?) + // https://github.com/Tyrrrz/DiscordChatExporter/issues/1226 + var response = await TryGetJsonResponseAsync(url, cancellationToken); + if (response is null) + yield break; var count = 0; - foreach (var userJson in response.EnumerateArray()) + foreach (var userJson in response.Value.EnumerateArray()) { var user = User.Parse(userJson); yield return user; @@ -683,9 +816,7 @@ public class DiscordClient(string token) count++; } - // Each batch can contain up to 100 users. - // If we got fewer, then it's definitely the last batch. - if (count < 100) + if (count <= 0) yield break; } } diff --git a/DiscordChatExporter.Core/Discord/Dump/DataDump.cs b/DiscordChatExporter.Core/Discord/Dump/DataDump.cs index be3deb1c..731a458d 100644 --- a/DiscordChatExporter.Core/Discord/Dump/DataDump.cs +++ b/DiscordChatExporter.Core/Discord/Dump/DataDump.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Collections.Generic; using System.IO.Compression; +using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -40,17 +41,19 @@ public partial class DataDump CancellationToken cancellationToken = default ) { - using var archive = ZipFile.OpenRead(zipFilePath); + await using var archive = await ZipFile.OpenReadAsync(zipFilePath, cancellationToken); - var entry = archive.GetEntry("messages/index.json"); - if (entry is null) - { - throw new InvalidOperationException( + // Use case-insensitive search to accommodate for different data dump versions + // https://github.com/Tyrrrz/DiscordChatExporter/issues/1459 + var entry = + archive.Entries.FirstOrDefault(e => + e.FullName.Equals("messages/index.json", StringComparison.OrdinalIgnoreCase) + ) + ?? throw new InvalidOperationException( "Failed to locate the channel index inside the data package." ); - } - await using var stream = entry.Open(); + await using var stream = await entry.OpenAsync(cancellationToken); using var document = await JsonDocument.ParseAsync(stream, default, cancellationToken); return Parse(document.RootElement); diff --git a/DiscordChatExporter.Core/Discord/Dump/DataDumpChannel.cs b/DiscordChatExporter.Core/Discord/Dump/DataDumpChannel.cs index 01bdf2d1..3d97489b 100644 --- a/DiscordChatExporter.Core/Discord/Dump/DataDumpChannel.cs +++ b/DiscordChatExporter.Core/Discord/Dump/DataDumpChannel.cs @@ -1,3 +1,3 @@ -namespace DiscordChatExporter.Core.Discord.Dump; +namespace DiscordChatExporter.Core.Discord.Dump; public record DataDumpChannel(Snowflake Id, string Name); diff --git a/DiscordChatExporter.Core/Discord/RateLimitPreference.cs b/DiscordChatExporter.Core/Discord/RateLimitPreference.cs new file mode 100644 index 00000000..76c642ea --- /dev/null +++ b/DiscordChatExporter.Core/Discord/RateLimitPreference.cs @@ -0,0 +1,38 @@ +using System; + +namespace DiscordChatExporter.Core.Discord; + +[Flags] +public enum RateLimitPreference +{ + IgnoreAll = 0, + RespectForUserTokens = 0b1, + RespectForBotTokens = 0b10, + RespectAll = RespectForUserTokens | RespectForBotTokens, +} + +public static class RateLimitPreferenceExtensions +{ + extension(RateLimitPreference rateLimitPreference) + { + internal bool IsRespectedFor(TokenKind tokenKind) => + tokenKind switch + { + TokenKind.User => (rateLimitPreference & RateLimitPreference.RespectForUserTokens) + != 0, + TokenKind.Bot => (rateLimitPreference & RateLimitPreference.RespectForBotTokens) + != 0, + _ => throw new ArgumentOutOfRangeException(nameof(tokenKind)), + }; + + public string GetDisplayName() => + rateLimitPreference switch + { + RateLimitPreference.IgnoreAll => "Always ignore", + RateLimitPreference.RespectForUserTokens => "Respect for user tokens", + RateLimitPreference.RespectForBotTokens => "Respect for bot tokens", + RateLimitPreference.RespectAll => "Always respect", + _ => throw new ArgumentOutOfRangeException(nameof(rateLimitPreference)), + }; + } +} diff --git a/DiscordChatExporter.Core/Discord/Snowflake.cs b/DiscordChatExporter.Core/Discord/Snowflake.cs index e5321047..9606aaee 100644 --- a/DiscordChatExporter.Core/Discord/Snowflake.cs +++ b/DiscordChatExporter.Core/Discord/Snowflake.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; diff --git a/DiscordChatExporter.Core/Discord/TokenKind.cs b/DiscordChatExporter.Core/Discord/TokenKind.cs index 09117202..9718698c 100644 --- a/DiscordChatExporter.Core/Discord/TokenKind.cs +++ b/DiscordChatExporter.Core/Discord/TokenKind.cs @@ -1,7 +1,7 @@ -namespace DiscordChatExporter.Core.Discord; +namespace DiscordChatExporter.Core.Discord; public enum TokenKind { User, - Bot + Bot, } diff --git a/DiscordChatExporter.Core/DiscordChatExporter.Core.csproj b/DiscordChatExporter.Core/DiscordChatExporter.Core.csproj index 709517fa..19498bbb 100644 --- a/DiscordChatExporter.Core/DiscordChatExporter.Core.csproj +++ b/DiscordChatExporter.Core/DiscordChatExporter.Core.csproj @@ -1,15 +1,15 @@ - - - - - - - - - - + + + + + + + + + + + - - \ No newline at end of file + diff --git a/DiscordChatExporter.Core/Exceptions/ChannelEmptyException.cs b/DiscordChatExporter.Core/Exceptions/ChannelEmptyException.cs new file mode 100644 index 00000000..42e6c787 --- /dev/null +++ b/DiscordChatExporter.Core/Exceptions/ChannelEmptyException.cs @@ -0,0 +1,3 @@ +namespace DiscordChatExporter.Core.Exceptions; + +public class ChannelEmptyException(string message) : DiscordChatExporterException(message); diff --git a/DiscordChatExporter.Core/Exceptions/DiscordChatExporterException.cs b/DiscordChatExporter.Core/Exceptions/DiscordChatExporterException.cs index 67b081ac..c0ff66cd 100644 --- a/DiscordChatExporter.Core/Exceptions/DiscordChatExporterException.cs +++ b/DiscordChatExporter.Core/Exceptions/DiscordChatExporterException.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace DiscordChatExporter.Core.Exceptions; diff --git a/DiscordChatExporter.Core/Exporting/ChannelExporter.cs b/DiscordChatExporter.Core/Exporting/ChannelExporter.cs index dc60c1dc..83fc08eb 100644 --- a/DiscordChatExporter.Core/Exporting/ChannelExporter.cs +++ b/DiscordChatExporter.Core/Exporting/ChannelExporter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using DiscordChatExporter.Core.Discord; @@ -20,61 +20,67 @@ public class ChannelExporter(DiscordClient discord) if (request.Channel.Kind == ChannelKind.GuildForum) { throw new DiscordChatExporterException( - $"Channel '{request.Channel.Name}' (#{request.Channel.Id}) " - + $"of guild '{request.Guild.Name}' (#{request.Guild.Id}) " + $"Channel '{request.Channel.Name}' " + + $"of guild '{request.Guild.Name}' " + $"is a forum and cannot be exported directly. " + "You need to pull its threads and export them individually." ); } - // Check if the channel is empty - if (request.Channel.IsEmpty) - { - throw new DiscordChatExporterException( - $"Channel '{request.Channel.Name}' (#{request.Channel.Id}) " - + $"of guild '{request.Guild.Name}' (#{request.Guild.Id}) " - + $"does not contain any messages." - ); - } - - // Check if the 'after' boundary is valid - if (request.After is not null && !request.Channel.MayHaveMessagesAfter(request.After.Value)) - { - throw new DiscordChatExporterException( - $"Channel '{request.Channel.Name}' (#{request.Channel.Id}) " - + $"of guild '{request.Guild.Name}' (#{request.Guild.Id}) " - + $"does not contain any messages within the specified period." - ); - } - - // Check if the 'before' boundary is valid - if ( - request.Before is not null - && !request.Channel.MayHaveMessagesBefore(request.Before.Value) - ) - { - throw new DiscordChatExporterException( - $"Channel '{request.Channel.Name}' (#{request.Channel.Id}) " - + $"of guild '{request.Guild.Name}' (#{request.Guild.Id}) " - + $"does not contain any messages within the specified period." - ); - } - // Build context var context = new ExportContext(discord, request); await context.PopulateChannelsAndRolesAsync(cancellationToken); - // Export messages + // Initialize the exporter before further checks to ensure the file is created even if + // an exception is thrown after this point. await using var messageExporter = new MessageExporter(context); - await foreach ( - var message in discord.GetMessagesAsync( + + // Check if the channel is empty + if (request.Channel.IsEmpty) + { + throw new ChannelEmptyException( + $"Channel '{request.Channel.Name}' " + + $"of guild '{request.Guild.Name}' " + + $"does not contain any messages; an empty file will be created." + ); + } + + // Check if the 'before' and 'after' boundaries are valid + if ( + ( + request.Before is not null + && !request.Channel.MayHaveMessagesBefore(request.Before.Value) + ) + || ( + request.After is not null + && !request.Channel.MayHaveMessagesAfter(request.After.Value) + ) + ) + { + throw new ChannelEmptyException( + $"Channel '{request.Channel.Name}' " + + $"of guild '{request.Guild.Name}' " + + $"does not contain any messages within the specified period; an empty file will be created." + ); + } + + var messages = !request.IsReverseMessageOrder + ? discord.GetMessagesAsync( request.Channel.Id, request.After, request.Before, progress, cancellationToken ) - ) + : discord.GetMessagesInReverseAsync( + request.Channel.Id, + request.After, + request.Before, + progress, + cancellationToken + ); + + await foreach (var message in messages) { try { @@ -98,15 +104,5 @@ public class ChannelExporter(DiscordClient discord) ); } } - - // Throw if no messages were exported - if (messageExporter.MessagesExported <= 0) - { - throw new DiscordChatExporterException( - $"Channel '{request.Channel.Name}' (#{request.Channel.Id}) " - + $"of guild '{request.Guild.Name}' (#{request.Guild.Id}) " - + $"does not contain any matching messages within the specified period." - ); - } } } diff --git a/DiscordChatExporter.Core/Exporting/CsvMessageWriter.cs b/DiscordChatExporter.Core/Exporting/CsvMessageWriter.cs index f6911ce3..49ee5224 100644 --- a/DiscordChatExporter.Core/Exporting/CsvMessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/CsvMessageWriter.cs @@ -1,11 +1,11 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; using DiscordChatExporter.Core.Discord.Data; -using DiscordChatExporter.Core.Utils.Extensions; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Exporting; diff --git a/DiscordChatExporter.Core/Exporting/ExportAssetDownloader.cs b/DiscordChatExporter.Core/Exporting/ExportAssetDownloader.cs index fe957ed2..071616ec 100644 --- a/DiscordChatExporter.Core/Exporting/ExportAssetDownloader.cs +++ b/DiscordChatExporter.Core/Exporting/ExportAssetDownloader.cs @@ -1,27 +1,21 @@ -using System; +using System; using System.Collections.Generic; -using System.Globalization; using System.IO; +using System.Net.Http; using System.Security.Cryptography; using System.Text; -using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Web; using AsyncKeyedLock; using DiscordChatExporter.Core.Utils; -using DiscordChatExporter.Core.Utils.Extensions; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Exporting; internal partial class ExportAssetDownloader(string workingDirPath, bool reuse) { - private static readonly AsyncKeyedLocker Locker = - new(o => - { - o.PoolSize = 20; - o.PoolInitialFill = 1; - }); + private static readonly AsyncKeyedLocker Locker = new(); // File paths of the previously downloaded assets private readonly Dictionary _previousPathsByUrl = new(StringComparer.Ordinal); @@ -43,46 +37,44 @@ internal partial class ExportAssetDownloader(string workingDirPath, bool reuse) if (reuse && File.Exists(filePath)) return _previousPathsByUrl[url] = filePath; + // Check for a file cached by the legacy naming scheme (5-char hash) and rename it + // to the new naming scheme to preserve backwards compatibility with existing exports + if (reuse) + { + var legacyFilePath = Path.Combine(workingDirPath, GetLegacyFileNameFromUrl(url)); + if (File.Exists(legacyFilePath)) + { + // Overwrite in case the destination file was created concurrently between our + // earlier existence check and this move operation + try + { + File.Move(legacyFilePath, filePath, overwrite: true); + return _previousPathsByUrl[url] = filePath; + } + catch (IOException) + { + // The legacy file was moved or deleted concurrently or something else happened. + // Upgrading old files is not crucial, so we can just move on. + } + } + } + Directory.CreateDirectory(workingDirPath); await Http.ResiliencePipeline.ExecuteAsync( async innerCancellationToken => { // Download the file - using var response = await Http.Client.GetAsync(url, innerCancellationToken); - await using (var output = File.Create(filePath)) - await response.Content.CopyToAsync(output, innerCancellationToken); + using var response = await Http.Client.GetAsync( + url, + HttpCompletionOption.ResponseHeadersRead, + innerCancellationToken + ); - // Try to set the file date according to the last-modified header - try - { - var lastModified = response - .Content.Headers.TryGetValue("Last-Modified") - ?.Pipe( - s => - DateTimeOffset.TryParse( - s, - CultureInfo.InvariantCulture, - DateTimeStyles.None, - out var instant - ) - ? instant - : (DateTimeOffset?)null - ); + response.EnsureSuccessStatusCode(); - if (lastModified is not null) - { - File.SetCreationTimeUtc(filePath, lastModified.Value.UtcDateTime); - File.SetLastWriteTimeUtc(filePath, lastModified.Value.UtcDateTime); - File.SetLastAccessTimeUtc(filePath, lastModified.Value.UtcDateTime); - } - } - catch - { - // This can apparently fail for some reason. - // Updating the file date is not a critical task, so we'll just ignore exceptions thrown here. - // https://github.com/Tyrrrz/DiscordChatExporter/issues/585 - } + await using var output = File.Create(filePath); + await response.Content.CopyToAsync(output, innerCancellationToken); }, cancellationToken ); @@ -93,36 +85,25 @@ internal partial class ExportAssetDownloader(string workingDirPath, bool reuse) internal partial class ExportAssetDownloader { - private static string GetUrlHash(string url) + private static string NormalizeUrl(string url) { // Remove signature parameters from Discord CDN URLs to normalize them - static string NormalizeUrl(string url) - { - var uri = new Uri(url); - if (!string.Equals(uri.Host, "cdn.discordapp.com", StringComparison.OrdinalIgnoreCase)) - return url; + var uri = new Uri(url); + if (!string.Equals(uri.Host, "cdn.discordapp.com", StringComparison.OrdinalIgnoreCase)) + return url; - var query = HttpUtility.ParseQueryString(uri.Query); - query.Remove("ex"); - query.Remove("is"); - query.Remove("hm"); + var query = HttpUtility.ParseQueryString(uri.Query); + query.Remove("ex"); + query.Remove("is"); + query.Remove("hm"); - return uri.GetLeftPart(UriPartial.Path) + query; - } - - return SHA256 - .HashData(Encoding.UTF8.GetBytes(NormalizeUrl(url))) - .ToHex() - // 5 chars ought to be enough for anybody - .Truncate(5); + return uri.GetLeftPart(UriPartial.Path) + query; } - private static string GetFileNameFromUrl(string url) + private static string GetFileNameFromUrl(string url, string urlHash) { - var urlHash = GetUrlHash(url); - // Try to extract the file name from URL - var fileName = Regex.Match(url, @".+/([^?]*)").Groups[1].Value; + var fileName = new Uri(url, UriKind.RelativeOrAbsolute).TryGetFileName(); // If it's not there, just use the URL hash as the file name if (string.IsNullOrWhiteSpace(fileName)) @@ -140,8 +121,29 @@ internal partial class ExportAssetDownloader fileExtension = ""; } - return PathEx.EscapeFileName( + return Path.EscapeFileName( fileNameWithoutExtension.Truncate(42) + '-' + urlHash + fileExtension ); } + + private static string GetFileNameFromUrl(string url) => + GetFileNameFromUrl( + url, + // 16 chars = 64 bits, reaches 1% collision probability at ~609 million files + SHA256 + .HashData(Encoding.UTF8.GetBytes(NormalizeUrl(url))) + .Pipe(Convert.ToHexStringLower) + .Truncate(16) + ); + + // Legacy naming used a 5-char hash, kept for backwards compatibility with existing exports + private static string GetLegacyFileNameFromUrl(string url) => + GetFileNameFromUrl( + url, + SHA256 + .HashData(Encoding.UTF8.GetBytes(NormalizeUrl(url))) + .Pipe(Convert.ToHexStringLower) + // 5 chars = 20 bits, reaches 1% collision probability at ~145 files + .Truncate(5) + ); } diff --git a/DiscordChatExporter.Core/Exporting/ExportContext.cs b/DiscordChatExporter.Core/Exporting/ExportContext.cs index 224c5aa1..3c7f5785 100644 --- a/DiscordChatExporter.Core/Exporting/ExportContext.cs +++ b/DiscordChatExporter.Core/Exporting/ExportContext.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Drawing; using System.IO; @@ -8,18 +8,21 @@ using System.Threading; using System.Threading.Tasks; using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Discord.Data; -using DiscordChatExporter.Core.Utils.Extensions; +using DiscordChatExporter.Core.Utils; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Exporting; internal class ExportContext(DiscordClient discord, ExportRequest request) { private readonly Dictionary _membersById = new(); - private readonly Dictionary _channelsById = new(); + private readonly Dictionary _channelsById = new(); private readonly Dictionary _rolesById = new(); - private readonly ExportAssetDownloader _assetDownloader = - new(request.AssetsDirPath, request.ShouldReuseAssets); + private readonly ExportAssetDownloader _assetDownloader = new( + request.AssetsDirPath, + request.ShouldReuseAssets + ); public DiscordClient Discord { get; } = discord; @@ -48,6 +51,21 @@ internal class ExportContext(DiscordClient discord, ExportRequest request) } } + // Threads are not preloaded, so we resolve them on demand + public async ValueTask PopulateChannelAsync( + Snowflake id, + CancellationToken cancellationToken = default + ) + { + if (_channelsById.ContainsKey(id)) + return; + + var channel = await Discord.TryGetChannelAsync(id, cancellationToken); + + // Store the result even if it's null, to avoid re-fetching non-existing channels + _channelsById[id] = channel; + } + // Because members cannot be pulled in bulk, we need to populate them on demand private async ValueTask PopulateMemberAsync( Snowflake id, @@ -93,11 +111,11 @@ internal class ExportContext(DiscordClient discord, ExportRequest request) public IReadOnlyList GetUserRoles(Snowflake id) => TryGetMember(id) - ?.RoleIds - .Select(TryGetRole) + ?.RoleIds.Select(TryGetRole) .WhereNotNull() .OrderByDescending(r => r.Position) - .ToArray() ?? Array.Empty(); + .ToArray() + ?? []; public Color? TryGetUserColor(Snowflake id) => GetUserRoles(id).Where(r => r.Color is not null).Select(r => r.Color).FirstOrDefault(); @@ -116,7 +134,7 @@ internal class ExportContext(DiscordClient discord, ExportRequest request) var relativeFilePath = Path.GetRelativePath(Request.OutputDirPath, filePath); // Prefer the relative path so that the export package can be copied around without breaking references. - // However, if the assets directory lies outside of the export directory, use the absolute path instead. + // However, if the assets directory lies outside the export directory, use the absolute path instead. var shouldUseAbsoluteFilePath = relativeFilePath.StartsWith( ".." + Path.DirectorySeparatorChar, @@ -131,14 +149,7 @@ internal class ExportContext(DiscordClient discord, ExportRequest request) // For HTML, the path needs to be properly formatted if (Request.Format is ExportFormat.HtmlDark or ExportFormat.HtmlLight) - { - // Format the path into a valid file URI - var href = new Uri(new Uri("file:///"), optimalFilePath).ToString(); - - // File schema does not support relative paths, so strip it if that's the case - // https://github.com/Tyrrrz/DiscordChatExporter/issues/1155 - return shouldUseAbsoluteFilePath ? href : href[8..]; - } + return Url.EncodeFilePath(optimalFilePath); return optimalFilePath; } diff --git a/DiscordChatExporter.Core/Exporting/ExportFormat.cs b/DiscordChatExporter.Core/Exporting/ExportFormat.cs index 923ceb10..6e92487d 100644 --- a/DiscordChatExporter.Core/Exporting/ExportFormat.cs +++ b/DiscordChatExporter.Core/Exporting/ExportFormat.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace DiscordChatExporter.Core.Exporting; @@ -8,30 +8,33 @@ public enum ExportFormat HtmlDark, HtmlLight, Csv, - Json + Json, } public static class ExportFormatExtensions { - public static string GetFileExtension(this ExportFormat format) => - format switch - { - ExportFormat.PlainText => "txt", - ExportFormat.HtmlDark => "html", - ExportFormat.HtmlLight => "html", - ExportFormat.Csv => "csv", - ExportFormat.Json => "json", - _ => throw new ArgumentOutOfRangeException(nameof(format)) - }; + extension(ExportFormat format) + { + public string GetFileExtension() => + format switch + { + ExportFormat.PlainText => "txt", + ExportFormat.HtmlDark => "html", + ExportFormat.HtmlLight => "html", + ExportFormat.Csv => "csv", + ExportFormat.Json => "json", + _ => throw new ArgumentOutOfRangeException(nameof(format)), + }; - public static string GetDisplayName(this ExportFormat format) => - format switch - { - ExportFormat.PlainText => "TXT", - ExportFormat.HtmlDark => "HTML (Dark)", - ExportFormat.HtmlLight => "HTML (Light)", - ExportFormat.Csv => "CSV", - ExportFormat.Json => "JSON", - _ => throw new ArgumentOutOfRangeException(nameof(format)) - }; + public string GetDisplayName() => + format switch + { + ExportFormat.PlainText => "TXT", + ExportFormat.HtmlDark => "HTML (Dark)", + ExportFormat.HtmlLight => "HTML (Light)", + ExportFormat.Csv => "CSV", + ExportFormat.Json => "JSON", + _ => throw new ArgumentOutOfRangeException(nameof(format)), + }; + } } diff --git a/DiscordChatExporter.Core/Exporting/ExportRequest.cs b/DiscordChatExporter.Core/Exporting/ExportRequest.cs index 411379e9..c722b388 100644 --- a/DiscordChatExporter.Core/Exporting/ExportRequest.cs +++ b/DiscordChatExporter.Core/Exporting/ExportRequest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.IO; using System.Text; @@ -7,8 +7,7 @@ using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Exporting.Filtering; using DiscordChatExporter.Core.Exporting.Partitioning; -using DiscordChatExporter.Core.Utils; -using DiscordChatExporter.Core.Utils.Extensions; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Exporting; @@ -34,6 +33,8 @@ public partial class ExportRequest public MessageFilter MessageFilter { get; } + public bool IsReverseMessageOrder { get; } + public bool ShouldFormatMarkdown { get; } public bool ShouldDownloadAssets { get; } @@ -56,6 +57,7 @@ public partial class ExportRequest Snowflake? before, PartitionLimit partitionLimit, MessageFilter messageFilter, + bool isReverseMessageOrder, bool shouldFormatMarkdown, bool shouldDownloadAssets, bool shouldReuseAssets, @@ -70,6 +72,7 @@ public partial class ExportRequest Before = before; PartitionLimit = partitionLimit; MessageFilter = messageFilter; + IsReverseMessageOrder = isReverseMessageOrder; ShouldFormatMarkdown = shouldFormatMarkdown; ShouldDownloadAssets = shouldDownloadAssets; ShouldReuseAssets = shouldReuseAssets; @@ -145,7 +148,7 @@ public partial class ExportRequest // File extension buffer.Append('.').Append(format.GetFileExtension()); - return PathEx.EscapeFileName(buffer.ToString()); + return Path.EscapeFileName(buffer.ToString()); } private static string FormatPath( @@ -160,7 +163,7 @@ public partial class ExportRequest path, "%.", m => - PathEx.EscapeFileName( + Path.EscapeFileName( m.Value switch { // On %T and %P, we have to make sure that we still get name and position of the category if the channel is a thread @@ -201,7 +204,7 @@ public partial class ExportRequest ), "%%" => "%", - _ => m.Value + _ => m.Value, } ) ); diff --git a/DiscordChatExporter.Core/Exporting/Filtering/BinaryExpressionKind.cs b/DiscordChatExporter.Core/Exporting/Filtering/BinaryExpressionKind.cs index 75147593..9409d333 100644 --- a/DiscordChatExporter.Core/Exporting/Filtering/BinaryExpressionKind.cs +++ b/DiscordChatExporter.Core/Exporting/Filtering/BinaryExpressionKind.cs @@ -1,7 +1,7 @@ -namespace DiscordChatExporter.Core.Exporting.Filtering; +namespace DiscordChatExporter.Core.Exporting.Filtering; internal enum BinaryExpressionKind { Or, - And + And, } diff --git a/DiscordChatExporter.Core/Exporting/Filtering/BinaryExpressionMessageFilter.cs b/DiscordChatExporter.Core/Exporting/Filtering/BinaryExpressionMessageFilter.cs index 73ab4839..517d8b2e 100644 --- a/DiscordChatExporter.Core/Exporting/Filtering/BinaryExpressionMessageFilter.cs +++ b/DiscordChatExporter.Core/Exporting/Filtering/BinaryExpressionMessageFilter.cs @@ -1,4 +1,4 @@ -using System; +using System; using DiscordChatExporter.Core.Discord.Data; namespace DiscordChatExporter.Core.Exporting.Filtering; @@ -14,6 +14,6 @@ internal class BinaryExpressionMessageFilter( { BinaryExpressionKind.Or => first.IsMatch(message) || second.IsMatch(message), BinaryExpressionKind.And => first.IsMatch(message) && second.IsMatch(message), - _ => throw new InvalidOperationException($"Unknown binary expression kind '{kind}'.") + _ => throw new InvalidOperationException($"Unknown binary expression kind '{kind}'."), }; } diff --git a/DiscordChatExporter.Core/Exporting/Filtering/ContainsMessageFilter.cs b/DiscordChatExporter.Core/Exporting/Filtering/ContainsMessageFilter.cs index 2c3c8efb..ba47a10f 100644 --- a/DiscordChatExporter.Core/Exporting/Filtering/ContainsMessageFilter.cs +++ b/DiscordChatExporter.Core/Exporting/Filtering/ContainsMessageFilter.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Text.RegularExpressions; using DiscordChatExporter.Core.Discord.Data; @@ -22,12 +22,11 @@ internal class ContainsMessageFilter(string text) : MessageFilter public override bool IsMatch(Message message) => IsMatch(message.Content) - || message.Embeds.Any( - e => - IsMatch(e.Title) - || IsMatch(e.Author?.Name) - || IsMatch(e.Description) - || IsMatch(e.Footer?.Text) - || e.Fields.Any(f => IsMatch(f.Name) || IsMatch(f.Value)) + || message.Embeds.Any(e => + IsMatch(e.Title) + || IsMatch(e.Author?.Name) + || IsMatch(e.Description) + || IsMatch(e.Footer?.Text) + || e.Fields.Any(f => IsMatch(f.Name) || IsMatch(f.Value)) ); } diff --git a/DiscordChatExporter.Core/Exporting/Filtering/FromMessageFilter.cs b/DiscordChatExporter.Core/Exporting/Filtering/FromMessageFilter.cs index 15c4c337..85dbe395 100644 --- a/DiscordChatExporter.Core/Exporting/Filtering/FromMessageFilter.cs +++ b/DiscordChatExporter.Core/Exporting/Filtering/FromMessageFilter.cs @@ -1,4 +1,4 @@ -using System; +using System; using DiscordChatExporter.Core.Discord.Data; namespace DiscordChatExporter.Core.Exporting.Filtering; diff --git a/DiscordChatExporter.Core/Exporting/Filtering/HasMessageFilter.cs b/DiscordChatExporter.Core/Exporting/Filtering/HasMessageFilter.cs index c061ad36..75b99fe4 100644 --- a/DiscordChatExporter.Core/Exporting/Filtering/HasMessageFilter.cs +++ b/DiscordChatExporter.Core/Exporting/Filtering/HasMessageFilter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Markdown.Parsing; @@ -17,15 +17,13 @@ internal class HasMessageFilter(MessageContentMatchKind kind) : MessageFilter MessageContentMatchKind.Image => message.Attachments.Any(file => file.IsImage), MessageContentMatchKind.Sound => message.Attachments.Any(file => file.IsAudio), MessageContentMatchKind.Pin => message.IsPinned, - MessageContentMatchKind.Invite - => MarkdownParser - .ExtractLinks(message.Content) - .Select(l => l.Url) - .Select(Invite.TryGetCodeFromUrl) - .Any(c => !string.IsNullOrWhiteSpace(c)), - _ - => throw new InvalidOperationException( - $"Unknown message content match kind '{kind}'." - ) + MessageContentMatchKind.Invite => MarkdownParser + .ExtractLinks(message.Content) + .Select(l => l.Url) + .Select(Invite.TryGetCodeFromUrl) + .Any(c => !string.IsNullOrWhiteSpace(c)), + _ => throw new InvalidOperationException( + $"Unknown message content match kind '{kind}'." + ), }; } diff --git a/DiscordChatExporter.Core/Exporting/Filtering/MentionsMessageFilter.cs b/DiscordChatExporter.Core/Exporting/Filtering/MentionsMessageFilter.cs index ae98b14d..c48edbea 100644 --- a/DiscordChatExporter.Core/Exporting/Filtering/MentionsMessageFilter.cs +++ b/DiscordChatExporter.Core/Exporting/Filtering/MentionsMessageFilter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using DiscordChatExporter.Core.Discord.Data; @@ -7,11 +7,10 @@ namespace DiscordChatExporter.Core.Exporting.Filtering; internal class MentionsMessageFilter(string value) : MessageFilter { public override bool IsMatch(Message message) => - message.MentionedUsers.Any( - user => - string.Equals(value, user.Name, StringComparison.OrdinalIgnoreCase) - || string.Equals(value, user.DisplayName, StringComparison.OrdinalIgnoreCase) - || string.Equals(value, user.FullName, StringComparison.OrdinalIgnoreCase) - || string.Equals(value, user.Id.ToString(), StringComparison.OrdinalIgnoreCase) + message.MentionedUsers.Any(user => + string.Equals(value, user.Name, StringComparison.OrdinalIgnoreCase) + || string.Equals(value, user.DisplayName, StringComparison.OrdinalIgnoreCase) + || string.Equals(value, user.FullName, StringComparison.OrdinalIgnoreCase) + || string.Equals(value, user.Id.ToString(), StringComparison.OrdinalIgnoreCase) ); } diff --git a/DiscordChatExporter.Core/Exporting/Filtering/MessageContentMatchKind.cs b/DiscordChatExporter.Core/Exporting/Filtering/MessageContentMatchKind.cs index d6496616..d73b5783 100644 --- a/DiscordChatExporter.Core/Exporting/Filtering/MessageContentMatchKind.cs +++ b/DiscordChatExporter.Core/Exporting/Filtering/MessageContentMatchKind.cs @@ -1,4 +1,4 @@ -namespace DiscordChatExporter.Core.Exporting.Filtering; +namespace DiscordChatExporter.Core.Exporting.Filtering; internal enum MessageContentMatchKind { @@ -9,5 +9,5 @@ internal enum MessageContentMatchKind Image, Sound, Pin, - Invite + Invite, } diff --git a/DiscordChatExporter.Core/Exporting/Filtering/MessageFilter.cs b/DiscordChatExporter.Core/Exporting/Filtering/MessageFilter.cs index 3f6f5e07..a68529bb 100644 --- a/DiscordChatExporter.Core/Exporting/Filtering/MessageFilter.cs +++ b/DiscordChatExporter.Core/Exporting/Filtering/MessageFilter.cs @@ -1,4 +1,4 @@ -using DiscordChatExporter.Core.Discord.Data; +using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Exporting.Filtering.Parsing; using Superpower; diff --git a/DiscordChatExporter.Core/Exporting/Filtering/NegatedMessageFilter.cs b/DiscordChatExporter.Core/Exporting/Filtering/NegatedMessageFilter.cs index e58ff9ce..8cf07921 100644 --- a/DiscordChatExporter.Core/Exporting/Filtering/NegatedMessageFilter.cs +++ b/DiscordChatExporter.Core/Exporting/Filtering/NegatedMessageFilter.cs @@ -1,4 +1,4 @@ -using DiscordChatExporter.Core.Discord.Data; +using DiscordChatExporter.Core.Discord.Data; namespace DiscordChatExporter.Core.Exporting.Filtering; diff --git a/DiscordChatExporter.Core/Exporting/Filtering/NullMessageFilter.cs b/DiscordChatExporter.Core/Exporting/Filtering/NullMessageFilter.cs index 3592092a..9dc9428a 100644 --- a/DiscordChatExporter.Core/Exporting/Filtering/NullMessageFilter.cs +++ b/DiscordChatExporter.Core/Exporting/Filtering/NullMessageFilter.cs @@ -1,4 +1,4 @@ -using DiscordChatExporter.Core.Discord.Data; +using DiscordChatExporter.Core.Discord.Data; namespace DiscordChatExporter.Core.Exporting.Filtering; diff --git a/DiscordChatExporter.Core/Exporting/Filtering/Parsing/FilterGrammar.cs b/DiscordChatExporter.Core/Exporting/Filtering/Parsing/FilterGrammar.cs index 8aaac425..2c2c764e 100644 --- a/DiscordChatExporter.Core/Exporting/Filtering/Parsing/FilterGrammar.cs +++ b/DiscordChatExporter.Core/Exporting/Filtering/Parsing/FilterGrammar.cs @@ -1,4 +1,4 @@ -using DiscordChatExporter.Core.Utils.Extensions; +using DiscordChatExporter.Core.Utils.Extensions; using Superpower; using Superpower.Parsers; @@ -30,8 +30,8 @@ internal static class FilterGrammar .OneOf(QuotedString, UnquotedString) .Named("text string"); - private static readonly TextParser ContainsFilter = String.Select( - v => (MessageFilter)new ContainsMessageFilter(v) + private static readonly TextParser ContainsFilter = String.Select(v => + (MessageFilter)new ContainsMessageFilter(v) ); private static readonly TextParser FromFilter = Span.EqualToIgnoreCase("from:") @@ -125,7 +125,7 @@ internal static class FilterGrammar op switch { '|' => new BinaryExpressionMessageFilter(left, right, BinaryExpressionKind.Or), - _ => new BinaryExpressionMessageFilter(left, right, BinaryExpressionKind.And) + _ => new BinaryExpressionMessageFilter(left, right, BinaryExpressionKind.And), } ); diff --git a/DiscordChatExporter.Core/Exporting/Filtering/ReactionMessageFilter.cs b/DiscordChatExporter.Core/Exporting/Filtering/ReactionMessageFilter.cs index eb7bb576..79385212 100644 --- a/DiscordChatExporter.Core/Exporting/Filtering/ReactionMessageFilter.cs +++ b/DiscordChatExporter.Core/Exporting/Filtering/ReactionMessageFilter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using DiscordChatExporter.Core.Discord.Data; @@ -7,10 +7,9 @@ namespace DiscordChatExporter.Core.Exporting.Filtering; internal class ReactionMessageFilter(string value) : MessageFilter { public override bool IsMatch(Message message) => - message.Reactions.Any( - r => - string.Equals(value, r.Emoji.Id?.ToString(), StringComparison.OrdinalIgnoreCase) - || string.Equals(value, r.Emoji.Name, StringComparison.OrdinalIgnoreCase) - || string.Equals(value, r.Emoji.Code, StringComparison.OrdinalIgnoreCase) + message.Reactions.Any(r => + string.Equals(value, r.Emoji.Id?.ToString(), StringComparison.OrdinalIgnoreCase) + || string.Equals(value, r.Emoji.Name, StringComparison.OrdinalIgnoreCase) + || string.Equals(value, r.Emoji.Code, StringComparison.OrdinalIgnoreCase) ); } diff --git a/DiscordChatExporter.Core/Exporting/HtmlMarkdownVisitor.cs b/DiscordChatExporter.Core/Exporting/HtmlMarkdownVisitor.cs index 29435db4..0104a6d8 100644 --- a/DiscordChatExporter.Core/Exporting/HtmlMarkdownVisitor.cs +++ b/DiscordChatExporter.Core/Exporting/HtmlMarkdownVisitor.cs @@ -1,14 +1,13 @@ -using System; +using System; using System.Linq; using System.Net; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Markdown; using DiscordChatExporter.Core.Markdown.Parsing; -using DiscordChatExporter.Core.Utils.Extensions; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Exporting; @@ -34,58 +33,51 @@ internal partial class HtmlMarkdownVisitor( { var (openingTag, closingTag) = formatting.Kind switch { - FormattingKind.Bold - => ( - // lang=html - "", - // lang=html - "" - ), + FormattingKind.Bold => ( + // lang=html + "", + // lang=html + "" + ), - FormattingKind.Italic - => ( - // lang=html - "", - // lang=html - "" - ), + FormattingKind.Italic => ( + // lang=html + "", + // lang=html + "" + ), - FormattingKind.Underline - => ( - // lang=html - "", - // lang=html - "" - ), + FormattingKind.Underline => ( + // lang=html + "", + // lang=html + "" + ), - FormattingKind.Strikethrough - => ( - // lang=html - "", - // lang=html - "" - ), + FormattingKind.Strikethrough => ( + // lang=html + "", + // lang=html + "" + ), - FormattingKind.Spoiler - => ( - // lang=html - """""", - // lang=html - """""" - ), + FormattingKind.Spoiler => ( + // lang=html + """""", + // lang=html + """""" + ), - FormattingKind.Quote - => ( - // lang=html - """
""", - // lang=html - """
""" - ), + FormattingKind.Quote => ( + // lang=html + """
""", + // lang=html + """
""" + ), - _ - => throw new InvalidOperationException( - $"Unknown formatting kind '{formatting.Kind}'." - ) + _ => throw new InvalidOperationException( + $"Unknown formatting kind '{formatting.Kind}'." + ), }; buffer.Append(openingTag); @@ -155,7 +147,9 @@ internal partial class HtmlMarkdownVisitor( buffer.Append( // lang=html $""" - {HtmlEncode(inlineCodeBlock.Code)} + {HtmlEncode( + inlineCodeBlock.Code + )} """ ); @@ -168,13 +162,15 @@ internal partial class HtmlMarkdownVisitor( ) { var highlightClass = !string.IsNullOrWhiteSpace(multiLineCodeBlock.Language) - ? $"language-{multiLineCodeBlock.Language}" + ? $"language-{HtmlEncode(multiLineCodeBlock.Language)}" : "nohighlight"; buffer.Append( // lang=html $""" - {HtmlEncode(multiLineCodeBlock.Code)} + {HtmlEncode( + multiLineCodeBlock.Code + )} """ ); @@ -213,7 +209,6 @@ internal partial class HtmlMarkdownVisitor( CancellationToken cancellationToken = default ) { - var emojiImageUrl = Emoji.GetImageUrl(emoji.Id, emoji.Name, emoji.IsAnimated); var jumboClass = isJumbo ? "chatlog__emoji--large" : ""; buffer.Append( @@ -222,9 +217,11 @@ internal partial class HtmlMarkdownVisitor( {emoji.Name} + alt="{HtmlEncode(emoji.Name)}" + title="{HtmlEncode(emoji.Code)}" + src="{HtmlEncode( + await context.ResolveAssetUrlAsync(emoji.ImageUrl, cancellationToken) + )}"> """ ); } @@ -267,12 +264,20 @@ internal partial class HtmlMarkdownVisitor( buffer.Append( // lang=html $""" - @{HtmlEncode(displayName)} + @{HtmlEncode( + displayName + )} """ ); } else if (mention.Kind == MentionKind.Channel) { + // Channel/thread mentions may reference threads that are not preloaded, + // so we resolve them on demand. + // https://github.com/Tyrrrz/DiscordChatExporter/issues/1261 + if (mention.TargetId is not null) + await context.PopulateChannelAsync(mention.TargetId.Value, cancellationToken); + var channel = mention.TargetId?.Pipe(context.TryGetChannel); var symbol = channel?.IsVoice == true ? "🔊" : "#"; var name = channel?.Name ?? "deleted-channel"; @@ -290,10 +295,8 @@ internal partial class HtmlMarkdownVisitor( var name = role?.Name ?? "deleted-role"; var color = role?.Color; - var style = color is not null - ? $""" - color: rgb({color.Value.R}, {color.Value.G}, {color.Value.B}); background-color: rgba({color.Value.R}, {color.Value.G}, {color.Value.B}, 0.1); - """ + var style = color is { } c + ? $"color: rgb({c.R}, {c.G}, {c.B}); background-color: rgba({c.R}, {c.G}, {c.B}, 0.1);" : null; buffer.Append( @@ -321,7 +324,9 @@ internal partial class HtmlMarkdownVisitor( buffer.Append( // lang=html $""" - {HtmlEncode(formatted)} + {HtmlEncode(formatted)} """ ); @@ -344,10 +349,8 @@ internal partial class HtmlMarkdownVisitor var isJumbo = isJumboAllowed - && nodes.All( - n => - n is EmojiNode - || n is TextNode textNode && string.IsNullOrWhiteSpace(textNode.Text) + && nodes.All(n => + n is EmojiNode || n is TextNode textNode && string.IsNullOrWhiteSpace(textNode.Text) ); var buffer = new StringBuilder(); diff --git a/DiscordChatExporter.Core/Exporting/HtmlMessageExtensions.cs b/DiscordChatExporter.Core/Exporting/HtmlMessageExtensions.cs index 260579be..c7846795 100644 --- a/DiscordChatExporter.Core/Exporting/HtmlMessageExtensions.cs +++ b/DiscordChatExporter.Core/Exporting/HtmlMessageExtensions.cs @@ -8,14 +8,20 @@ internal static class HtmlMessageExtensions { // Message content is hidden if it's a link to an embedded media // https://github.com/Tyrrrz/DiscordChatExporter/issues/682 - public static bool IsContentHidden(this Message message) + extension(Message message) { - if (message.Embeds.Count != 1) - return false; + public bool IsContentHidden() + { + if (message.Embeds.Count != 1) + return false; - var embed = message.Embeds[0]; + var embed = message.Embeds[0]; - return string.Equals(message.Content.Trim(), embed.Url, StringComparison.OrdinalIgnoreCase) - && embed.Kind is EmbedKind.Image or EmbedKind.Gifv; + return string.Equals( + message.Content.Trim(), + embed.Url, + StringComparison.OrdinalIgnoreCase + ) && embed.Kind is EmbedKind.Image or EmbedKind.Gifv; + } } } diff --git a/DiscordChatExporter.Core/Exporting/HtmlMessageWriter.cs b/DiscordChatExporter.Core/Exporting/HtmlMessageWriter.cs index 5d071b49..81bb8087 100644 --- a/DiscordChatExporter.Core/Exporting/HtmlMessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/HtmlMessageWriter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -15,8 +15,9 @@ internal class HtmlMessageWriter(Stream stream, ExportContext context, string th private readonly TextWriter _writer = new StreamWriter(stream); private readonly HtmlMinifier _minifier = new(); - private readonly List _messageGroup = new(); + private readonly List _messageGroup = []; + // Note: in reverse order, last message appears earlier than the first message private bool CanJoinGroup(Message message) { // If the group is empty, any message can join it @@ -90,7 +91,7 @@ internal class HtmlMessageWriter(Stream stream, ExportContext context, string th await new MessageGroupTemplate { Context = Context, - Messages = messages + Messages = messages, }.RenderAsync(cancellationToken) ) ); @@ -131,7 +132,7 @@ internal class HtmlMessageWriter(Stream stream, ExportContext context, string th await new PostambleTemplate { Context = Context, - MessagesWritten = MessagesWritten + MessagesWritten = MessagesWritten, }.RenderAsync(cancellationToken) ) ); diff --git a/DiscordChatExporter.Core/Exporting/JsonMessageWriter.cs b/DiscordChatExporter.Core/Exporting/JsonMessageWriter.cs index 2f8a4440..4d2e19cd 100644 --- a/DiscordChatExporter.Core/Exporting/JsonMessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/JsonMessageWriter.cs @@ -1,33 +1,34 @@ -using System; +using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text.Encodings.Web; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Discord.Data.Embeds; -using DiscordChatExporter.Core.Utils.Extensions; +using DiscordChatExporter.Core.Markdown.Parsing; using JsonExtensions.Writing; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Exporting; internal class JsonMessageWriter(Stream stream, ExportContext context) : MessageWriter(stream, context) { - private readonly Utf8JsonWriter _writer = - new( - stream, - new JsonWriterOptions - { - // https://github.com/Tyrrrz/DiscordChatExporter/issues/450 - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - Indented = true, - // Validation errors may mask actual failures - // https://github.com/Tyrrrz/DiscordChatExporter/issues/413 - SkipValidation = true - } - ); + private readonly Utf8JsonWriter _writer = new( + stream, + new JsonWriterOptions + { + // https://github.com/Tyrrrz/DiscordChatExporter/issues/450 + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + Indented = true, + // Validation errors may mask actual failures + // https://github.com/Tyrrrz/DiscordChatExporter/issues/413 + SkipValidation = true, + } + ); private async ValueTask FormatMarkdownAsync( string markdown, @@ -37,22 +38,31 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) ? await PlainTextMarkdownVisitor.FormatAsync(Context, markdown, cancellationToken) : markdown; - private async ValueTask WriteUserAsync(User user, CancellationToken cancellationToken = default) + private async ValueTask WriteUserAsync( + User user, + bool includeRoles = true, + CancellationToken cancellationToken = default + ) { _writer.WriteStartObject(); _writer.WriteString("id", user.Id.ToString()); _writer.WriteString("name", user.Name); _writer.WriteString("discriminator", user.DiscriminatorFormatted); + _writer.WriteString( "nickname", Context.TryGetMember(user.Id)?.DisplayName ?? user.DisplayName ); - _writer.WriteString("color", Context.TryGetUserColor(user.Id)?.ToHex()); + + _writer.WriteString("color", Context.TryGetUserColor(user.Id)?.ToHexString()); _writer.WriteBoolean("isBot", user.IsBot); - _writer.WritePropertyName("roles"); - await WriteRolesAsync(Context.GetUserRoles(user.Id), cancellationToken); + if (includeRoles) + { + _writer.WritePropertyName("roles"); + await WriteRolesAsync(Context.GetUserRoles(user.Id), cancellationToken); + } _writer.WriteString( "avatarUrl", @@ -66,6 +76,26 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) await _writer.FlushAsync(cancellationToken); } + private async ValueTask WriteEmojiAsync( + Emoji emoji, + CancellationToken cancellationToken = default + ) + { + _writer.WriteStartObject(); + + _writer.WriteString("id", emoji.Id.ToString()); + _writer.WriteString("name", emoji.Name); + _writer.WriteString("code", emoji.Code); + _writer.WriteBoolean("isAnimated", emoji.IsAnimated); + _writer.WriteString( + "imageUrl", + await Context.ResolveAssetUrlAsync(emoji.ImageUrl, cancellationToken) + ); + + _writer.WriteEndObject(); + await _writer.FlushAsync(cancellationToken); + } + private async ValueTask WriteRolesAsync( IReadOnlyList roles, CancellationToken cancellationToken = default @@ -79,7 +109,7 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) _writer.WriteString("id", role.Id.ToString()); _writer.WriteString("name", role.Name); - _writer.WriteString("color", role.Color?.ToHex()); + _writer.WriteString("color", role.Color?.ToHexString()); _writer.WriteNumber("position", role.Position); _writer.WriteEndObject(); @@ -89,6 +119,24 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) await _writer.FlushAsync(cancellationToken); } + private async ValueTask WriteAttachmentAsync( + Attachment attachment, + CancellationToken cancellationToken = default + ) + { + _writer.WriteStartObject(); + + _writer.WriteString("id", attachment.Id.ToString()); + _writer.WriteString( + "url", + await Context.ResolveAssetUrlAsync(attachment.Url, cancellationToken) + ); + _writer.WriteString("fileName", attachment.FileName); + _writer.WriteNumber("fileSizeBytes", attachment.FileSize.TotalBytes); + + _writer.WriteEndObject(); + } + private async ValueTask WriteEmbedAuthorAsync( EmbedAuthor embedAuthor, CancellationToken cancellationToken = default @@ -108,6 +156,8 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) cancellationToken ) ); + + _writer.WriteString("iconCanonicalUrl", embedAuthor.IconUrl); } _writer.WriteEndObject(); @@ -130,6 +180,8 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) cancellationToken ) ); + + _writer.WriteString("canonicalUrl", embedImage.Url); } _writer.WriteNumber("width", embedImage.Width); @@ -155,6 +207,8 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) cancellationToken ) ); + + _writer.WriteString("canonicalUrl", embedVideo.Url); } _writer.WriteNumber("width", embedVideo.Width); @@ -182,6 +236,8 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) cancellationToken ) ); + + _writer.WriteString("iconCanonicalUrl", embedFooter.IconUrl); } _writer.WriteEndObject(); @@ -225,7 +281,7 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) ); if (embed.Color is not null) - _writer.WriteString("color", embed.Color.Value.ToHex()); + _writer.WriteString("color", embed.Color.Value.ToHexString()); if (embed.Author is not null) { @@ -273,10 +329,48 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) _writer.WriteEndArray(); + // Inline emoji + _writer.WriteStartArray("inlineEmojis"); + + if (!string.IsNullOrWhiteSpace(embed.Description)) + { + foreach ( + var emoji in MarkdownParser + .ExtractEmojis(embed.Description) + .DistinctBy(e => e.Name, StringComparer.Ordinal) + ) + { + await WriteEmojiAsync( + new Emoji(emoji.Id, emoji.Name, emoji.IsAnimated), + cancellationToken + ); + } + } + + _writer.WriteEndArray(); + _writer.WriteEndObject(); await _writer.FlushAsync(cancellationToken); } + private async ValueTask WriteStickerAsync( + Sticker sticker, + CancellationToken cancellationToken = default + ) + { + _writer.WriteStartObject(); + + _writer.WriteString("id", sticker.Id.ToString()); + _writer.WriteString("name", sticker.Name); + _writer.WriteString("format", sticker.Format.ToString()); + _writer.WriteString( + "sourceUrl", + await Context.ResolveAssetUrlAsync(sticker.SourceUrl, cancellationToken) + ); + + _writer.WriteEndObject(); + } + public override async ValueTask WritePreambleAsync( CancellationToken cancellationToken = default ) @@ -373,25 +467,13 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) // Author _writer.WritePropertyName("author"); - await WriteUserAsync(message.Author, cancellationToken); + await WriteUserAsync(message.Author, true, cancellationToken); // Attachments _writer.WriteStartArray("attachments"); foreach (var attachment in message.Attachments) - { - _writer.WriteStartObject(); - - _writer.WriteString("id", attachment.Id.ToString()); - _writer.WriteString( - "url", - await Context.ResolveAssetUrlAsync(attachment.Url, cancellationToken) - ); - _writer.WriteString("fileName", attachment.FileName); - _writer.WriteNumber("fileSizeBytes", attachment.FileSize.TotalBytes); - - _writer.WriteEndObject(); - } + await WriteAttachmentAsync(attachment, cancellationToken); _writer.WriteEndArray(); @@ -407,19 +489,7 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) _writer.WriteStartArray("stickers"); foreach (var sticker in message.Stickers) - { - _writer.WriteStartObject(); - - _writer.WriteString("id", sticker.Id.ToString()); - _writer.WriteString("name", sticker.Name); - _writer.WriteString("format", sticker.Format.ToString()); - _writer.WriteString( - "sourceUrl", - await Context.ResolveAssetUrlAsync(sticker.SourceUrl, cancellationToken) - ); - - _writer.WriteEndObject(); - } + await WriteStickerAsync(sticker, cancellationToken); _writer.WriteEndArray(); @@ -431,20 +501,14 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) _writer.WriteStartObject(); // Emoji - _writer.WriteStartObject("emoji"); - _writer.WriteString("id", reaction.Emoji.Id.ToString()); - _writer.WriteString("name", reaction.Emoji.Name); - _writer.WriteString("code", reaction.Emoji.Code); - _writer.WriteBoolean("isAnimated", reaction.Emoji.IsAnimated); - _writer.WriteString( - "imageUrl", - await Context.ResolveAssetUrlAsync(reaction.Emoji.ImageUrl, cancellationToken) - ); - _writer.WriteEndObject(); + _writer.WritePropertyName("emoji"); + await WriteEmojiAsync(reaction.Emoji, cancellationToken); _writer.WriteNumber("count", reaction.Count); + // Reaction authors _writer.WriteStartArray("users"); + await foreach ( var user in Context.Discord.GetMessageReactionsAsync( Context.Request.Channel.Id, @@ -454,28 +518,7 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) ) ) { - _writer.WriteStartObject(); - - // Write limited user information without color and roles, - // so we can avoid fetching guild member information for each user. - _writer.WriteString("id", user.Id.ToString()); - _writer.WriteString("name", user.Name); - _writer.WriteString("discriminator", user.DiscriminatorFormatted); - _writer.WriteString( - "nickname", - Context.TryGetMember(user.Id)?.DisplayName ?? user.DisplayName - ); - _writer.WriteBoolean("isBot", user.IsBot); - - _writer.WriteString( - "avatarUrl", - await Context.ResolveAssetUrlAsync( - Context.TryGetMember(user.Id)?.AvatarUrl ?? user.AvatarUrl, - cancellationToken - ) - ); - - _writer.WriteEndObject(); + await WriteUserAsync(user, false, cancellationToken); } _writer.WriteEndArray(); @@ -487,9 +530,8 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) // Mentions _writer.WriteStartArray("mentions"); - foreach (var user in message.MentionedUsers) - await WriteUserAsync(user, cancellationToken); + await WriteUserAsync(user, true, cancellationToken); _writer.WriteEndArray(); @@ -497,12 +539,58 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) if (message.Reference is not null) { _writer.WriteStartObject("reference"); + _writer.WriteString("type", message.Reference.Kind.ToString()); _writer.WriteString("messageId", message.Reference.MessageId?.ToString()); _writer.WriteString("channelId", message.Reference.ChannelId?.ToString()); _writer.WriteString("guildId", message.Reference.GuildId?.ToString()); _writer.WriteEndObject(); } + // Forwarded message + if (message.ForwardedMessage is not null) + { + _writer.WriteStartObject("forwardedMessage"); + + _writer.WriteString( + "timestamp", + Context.NormalizeDate(message.ForwardedMessage.Timestamp) + ); + + _writer.WriteString( + "timestampEdited", + message.ForwardedMessage.EditedTimestamp?.Pipe(Context.NormalizeDate) + ); + + _writer.WriteString( + "content", + await FormatMarkdownAsync(message.ForwardedMessage.Content, cancellationToken) + ); + + // Forwarded attachments + _writer.WriteStartArray("attachments"); + + foreach (var attachment in message.ForwardedMessage.Attachments) + await WriteAttachmentAsync(attachment, cancellationToken); + + _writer.WriteEndArray(); + + // Forwarded embeds + _writer.WriteStartArray("embeds"); + foreach (var embed in message.ForwardedMessage.Embeds) + await WriteEmbedAsync(embed, cancellationToken); + _writer.WriteEndArray(); + + // Forwarded stickers + _writer.WriteStartArray("stickers"); + + foreach (var sticker in message.ForwardedMessage.Stickers) + await WriteStickerAsync(sticker, cancellationToken); + + _writer.WriteEndArray(); + + _writer.WriteEndObject(); + } + // Interaction if (message.Interaction is not null) { @@ -512,11 +600,28 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) _writer.WriteString("name", message.Interaction.Name); _writer.WritePropertyName("user"); - await WriteUserAsync(message.Interaction.User, cancellationToken); + await WriteUserAsync(message.Interaction.User, true, cancellationToken); _writer.WriteEndObject(); } + // Inline emoji + _writer.WriteStartArray("inlineEmojis"); + + foreach ( + var emoji in MarkdownParser + .ExtractEmojis(message.Content) + .DistinctBy(e => e.Name, StringComparer.Ordinal) + ) + { + await WriteEmojiAsync( + new Emoji(emoji.Id, emoji.Name, emoji.IsAnimated), + cancellationToken + ); + } + + _writer.WriteEndArray(); + _writer.WriteEndObject(); await _writer.FlushAsync(cancellationToken); } diff --git a/DiscordChatExporter.Core/Exporting/MessageExporter.cs b/DiscordChatExporter.Core/Exporting/MessageExporter.cs index 24b1e622..9db861be 100644 --- a/DiscordChatExporter.Core/Exporting/MessageExporter.cs +++ b/DiscordChatExporter.Core/Exporting/MessageExporter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -13,24 +13,7 @@ internal partial class MessageExporter(ExportContext context) : IAsyncDisposable public long MessagesExported { get; private set; } - private async ValueTask ResetWriterAsync(CancellationToken cancellationToken = default) - { - if (_writer is not null) - { - try - { - await _writer.WritePostambleAsync(cancellationToken); - } - // Writer must be disposed, even if it fails to write the postamble - finally - { - await _writer.DisposeAsync(); - _writer = null; - } - } - } - - private async ValueTask GetWriterAsync( + private async ValueTask InitializeWriterAsync( CancellationToken cancellationToken = default ) { @@ -43,7 +26,7 @@ internal partial class MessageExporter(ExportContext context) : IAsyncDisposable ) ) { - await ResetWriterAsync(cancellationToken); + await UninitializeWriterAsync(cancellationToken); _partitionIndex++; } @@ -60,17 +43,41 @@ internal partial class MessageExporter(ExportContext context) : IAsyncDisposable return _writer = writer; } + private async ValueTask UninitializeWriterAsync(CancellationToken cancellationToken = default) + { + if (_writer is not null) + { + try + { + await _writer.WritePostambleAsync(cancellationToken); + } + // Writer must be disposed, even if it fails to write the postamble + finally + { + await _writer.DisposeAsync(); + _writer = null; + } + } + } + public async ValueTask ExportMessageAsync( Message message, CancellationToken cancellationToken = default ) { - var writer = await GetWriterAsync(cancellationToken); + var writer = await InitializeWriterAsync(cancellationToken); await writer.WriteMessageAsync(message, cancellationToken); MessagesExported++; } - public async ValueTask DisposeAsync() => await ResetWriterAsync(); + public async ValueTask DisposeAsync() + { + // If not messages were written, force the creation of an empty file + if (MessagesExported <= 0) + _ = await InitializeWriterAsync(); + + await UninitializeWriterAsync(); + } } internal partial class MessageExporter @@ -100,13 +107,15 @@ internal partial class MessageExporter ExportFormat.PlainText => new PlainTextMessageWriter(File.Create(filePath), context), ExportFormat.Csv => new CsvMessageWriter(File.Create(filePath), context), ExportFormat.HtmlDark => new HtmlMessageWriter(File.Create(filePath), context, "Dark"), - ExportFormat.HtmlLight - => new HtmlMessageWriter(File.Create(filePath), context, "Light"), + ExportFormat.HtmlLight => new HtmlMessageWriter( + File.Create(filePath), + context, + "Light" + ), ExportFormat.Json => new JsonMessageWriter(File.Create(filePath), context), - _ - => throw new ArgumentOutOfRangeException( - nameof(format), - $"Unknown export format '{format}'." - ) + _ => throw new ArgumentOutOfRangeException( + nameof(format), + $"Unknown export format '{format}'." + ), }; } diff --git a/DiscordChatExporter.Core/Exporting/MessageGroupTemplate.cshtml b/DiscordChatExporter.Core/Exporting/MessageGroupTemplate.cshtml index 92f8a43c..b3c06837 100644 --- a/DiscordChatExporter.Core/Exporting/MessageGroupTemplate.cshtml +++ b/DiscordChatExporter.Core/Exporting/MessageGroupTemplate.cshtml @@ -2,10 +2,12 @@ @using System.Collections.Generic @using System.Linq @using System.Threading.Tasks +@using RazorBlade @using DiscordChatExporter.Core.Discord.Data @using DiscordChatExporter.Core.Discord.Data.Embeds @using DiscordChatExporter.Core.Markdown.Parsing @using DiscordChatExporter.Core.Utils.Extensions +@using PowerKit.Extensions @inherits RazorBlade.HtmlTemplate @@ -22,19 +24,19 @@ string FormatDate(DateTimeOffset instant, string format = "g") => Context.FormatDate(instant, format); - async ValueTask FormatMarkdownAsync(string markdown) => + async ValueTask FormatMarkdownAsync(string markdown) => Context.Request.ShouldFormatMarkdown - ? await HtmlMarkdownVisitor.FormatAsync(Context, markdown, true, CancellationToken) - : markdown; + ? Html.Raw(await HtmlMarkdownVisitor.FormatAsync(Context, markdown, true, CancellationToken)) + : Html.Raw(Html.Encode(markdown)); - async ValueTask FormatEmbedMarkdownAsync(string markdown) => + async ValueTask FormatEmbedMarkdownAsync(string markdown) => Context.Request.ShouldFormatMarkdown - ? await HtmlMarkdownVisitor.FormatAsync(Context, markdown, false, CancellationToken) - : markdown; + ? Html.Raw(await HtmlMarkdownVisitor.FormatAsync(Context, markdown, false, CancellationToken)) + : Html.Raw(Html.Encode(markdown)); }
-@foreach (var (message, i) in Messages.WithIndex()) +@foreach (var (i, message) in Messages.Index()) { var isFirst = i == 0; @@ -178,7 +180,7 @@ @if (!string.IsNullOrWhiteSpace(message.ReferencedMessage.Content) && !message.ReferencedMessage.IsContentHidden()) { - @Html.Raw(await FormatEmbedMarkdownAsync(message.ReferencedMessage.Content)) + @(await FormatEmbedMarkdownAsync(message.ReferencedMessage.Content)) } else if (message.ReferencedMessage.Attachments.Any() || message.ReferencedMessage.Embeds.Any()) { @@ -251,7 +253,7 @@ @* Text *@ @if (!string.IsNullOrWhiteSpace(message.Content) && !message.IsContentHidden()) { - @Html.Raw(await FormatMarkdownAsync(message.Content)) + @(await FormatMarkdownAsync(message.Content)) } @* Edited timestamp *@ @@ -262,6 +264,95 @@
} + @* Forwarded message *@ + @if (message is { IsForwarded: true, ForwardedMessage: not null }) + { +
+
+ + + + Forwarded +
+ + @* Forwarded content *@ + @if (!string.IsNullOrWhiteSpace(message.ForwardedMessage.Content)) + { +
+ @(await FormatMarkdownAsync(message.ForwardedMessage.Content)) +
+ } + + @* Forwarded attachments *@ + @if (message.ForwardedMessage.Attachments.Any()) + { +
+ @foreach (var attachment in message.ForwardedMessage.Attachments) + { + @if (attachment.IsImage) + { + + @(attachment.Description ?? + + } + else if (attachment.IsVideo) + { + + } + else if (attachment.IsAudio) + { + + } + else + { +
+ + + + +
+ @attachment.FileSize +
+
+ } + } +
+ } + + @* Forwarded stickers *@ + @foreach (var sticker in message.ForwardedMessage.Stickers) + { +
+ @if (sticker.IsImage) + { + Sticker + } + else if (sticker.Format == StickerFormat.Lottie) + { +
+ } +
+ } + + @* Forwarded timestamp *@ +
+ Originally sent: @FormatDate(message.ForwardedMessage.Timestamp) + @if (message.ForwardedMessage.EditedTimestamp is not null) + { + (edited) + } +
+
+ } + @* Attachments *@ @foreach (var attachment in message.Attachments) { @@ -388,7 +479,7 @@
@if (!string.IsNullOrWhiteSpace(embed.Author.IconUrl)) { - Author icon + Author icon } @if (!string.IsNullOrWhiteSpace(embed.Author.Name)) @@ -414,19 +505,21 @@ @if (!string.IsNullOrWhiteSpace(embed.Url)) { -
@Html.Raw(await FormatEmbedMarkdownAsync(embed.Title))
+
@(await FormatEmbedMarkdownAsync(embed.Title))
} else { -
@Html.Raw(await FormatEmbedMarkdownAsync(embed.Title))
+
@(await FormatEmbedMarkdownAsync(embed.Title))
}
} - @* Video player *@ + @* Video thumbnail *@
- + + YouTube video thumbnail +
@@ -441,21 +534,29 @@ embed.Thumbnail?.ProxyUrl ?? embed.Thumbnail?.Url ?? embed.Url; + var embedImageCanonicalUrl = + embed.Image?.Url ?? embed.Thumbnail?.Url ?? embed.Url; + } // Generic video embed - else if (embed.Kind == EmbedKind.Video && !string.IsNullOrWhiteSpace(embed.Url)) + else if (embed.Kind == EmbedKind.Video + && !string.IsNullOrWhiteSpace(embed.Url) + // Twitch clips cannot be embedded in local HTML files + && embed.TryGetTwitchClip() is null) { var embedVideoUrl = embed.Video?.ProxyUrl ?? embed.Video?.Url ?? embed.Url; + var embedVideoCanonicalUrl = embed.Video?.Url ?? embed.Url; +
-
@@ -467,8 +568,10 @@ embed.Video?.ProxyUrl ?? embed.Video?.Url ?? embed.Url; + var embedVideoCanonicalUrl = embed.Video?.Url ?? embed.Url; +
-
@@ -496,7 +599,7 @@
@if (!string.IsNullOrWhiteSpace(embed.Author.IconUrl)) { - Author icon + Author icon } @if (!string.IsNullOrWhiteSpace(embed.Author.Name)) @@ -522,12 +625,12 @@ @if (!string.IsNullOrWhiteSpace(embed.Url)) { -
@Html.Raw(await FormatEmbedMarkdownAsync(embed.Title))
+
@(await FormatEmbedMarkdownAsync(embed.Title))
} else { -
@Html.Raw(await FormatEmbedMarkdownAsync(embed.Title))
+
@(await FormatEmbedMarkdownAsync(embed.Title))
}
} @@ -536,7 +639,7 @@ @if (!string.IsNullOrWhiteSpace(embed.Description)) {
-
@Html.Raw(await FormatEmbedMarkdownAsync(embed.Description))
+
@(await FormatEmbedMarkdownAsync(embed.Description))
} @@ -550,14 +653,14 @@ @if (!string.IsNullOrWhiteSpace(field.Name)) {
-
@Html.Raw(await FormatEmbedMarkdownAsync(field.Name))
+
@(await FormatEmbedMarkdownAsync(field.Name))
} @if (!string.IsNullOrWhiteSpace(field.Value)) {
-
@Html.Raw(await FormatEmbedMarkdownAsync(field.Value))
+
@(await FormatEmbedMarkdownAsync(field.Value))
} @@ -571,7 +674,7 @@ { } @@ -587,7 +690,7 @@ { } @@ -602,7 +705,7 @@ @* Footer icon *@ @if (!string.IsNullOrWhiteSpace(embed.Footer?.IconUrl)) { - Footer icon + Footer icon } diff --git a/DiscordChatExporter.Core/Exporting/MessageWriter.cs b/DiscordChatExporter.Core/Exporting/MessageWriter.cs index 8c0820db..d7003537 100644 --- a/DiscordChatExporter.Core/Exporting/MessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/MessageWriter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Threading; using System.Threading.Tasks; diff --git a/DiscordChatExporter.Core/Exporting/Partitioning/FileSizePartitionLimit.cs b/DiscordChatExporter.Core/Exporting/Partitioning/FileSizePartitionLimit.cs index 520894e4..d0d732b8 100644 --- a/DiscordChatExporter.Core/Exporting/Partitioning/FileSizePartitionLimit.cs +++ b/DiscordChatExporter.Core/Exporting/Partitioning/FileSizePartitionLimit.cs @@ -1,4 +1,4 @@ -namespace DiscordChatExporter.Core.Exporting.Partitioning; +namespace DiscordChatExporter.Core.Exporting.Partitioning; internal class FileSizePartitionLimit(long limit) : PartitionLimit { diff --git a/DiscordChatExporter.Core/Exporting/Partitioning/MessageCountPartitionLimit.cs b/DiscordChatExporter.Core/Exporting/Partitioning/MessageCountPartitionLimit.cs index d97e6ead..18030b31 100644 --- a/DiscordChatExporter.Core/Exporting/Partitioning/MessageCountPartitionLimit.cs +++ b/DiscordChatExporter.Core/Exporting/Partitioning/MessageCountPartitionLimit.cs @@ -1,4 +1,4 @@ -namespace DiscordChatExporter.Core.Exporting.Partitioning; +namespace DiscordChatExporter.Core.Exporting.Partitioning; internal class MessageCountPartitionLimit(long limit) : PartitionLimit { diff --git a/DiscordChatExporter.Core/Exporting/Partitioning/NullPartitionLimit.cs b/DiscordChatExporter.Core/Exporting/Partitioning/NullPartitionLimit.cs index 1fee660d..d9a4c157 100644 --- a/DiscordChatExporter.Core/Exporting/Partitioning/NullPartitionLimit.cs +++ b/DiscordChatExporter.Core/Exporting/Partitioning/NullPartitionLimit.cs @@ -1,4 +1,4 @@ -namespace DiscordChatExporter.Core.Exporting.Partitioning; +namespace DiscordChatExporter.Core.Exporting.Partitioning; internal class NullPartitionLimit : PartitionLimit { diff --git a/DiscordChatExporter.Core/Exporting/Partitioning/PartitionLimit.cs b/DiscordChatExporter.Core/Exporting/Partitioning/PartitionLimit.cs index 016a6650..45f74145 100644 --- a/DiscordChatExporter.Core/Exporting/Partitioning/PartitionLimit.cs +++ b/DiscordChatExporter.Core/Exporting/Partitioning/PartitionLimit.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Text.RegularExpressions; @@ -37,7 +37,7 @@ public partial class PartitionLimit "M" => 1_000_000, "K" => 1_000, "" => 1, - _ => -1 + _ => -1, }; if (magnitude < 0) diff --git a/DiscordChatExporter.Core/Exporting/PlainTextMarkdownVisitor.cs b/DiscordChatExporter.Core/Exporting/PlainTextMarkdownVisitor.cs index 5e7a0ca8..c495e312 100644 --- a/DiscordChatExporter.Core/Exporting/PlainTextMarkdownVisitor.cs +++ b/DiscordChatExporter.Core/Exporting/PlainTextMarkdownVisitor.cs @@ -1,9 +1,9 @@ -using System.Text; +using System.Text; using System.Threading; using System.Threading.Tasks; using DiscordChatExporter.Core.Markdown; using DiscordChatExporter.Core.Markdown.Parsing; -using DiscordChatExporter.Core.Utils.Extensions; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Exporting; @@ -57,6 +57,12 @@ internal partial class PlainTextMarkdownVisitor(ExportContext context, StringBui } else if (mention.Kind == MentionKind.Channel) { + // Channel/thread mentions may reference threads that are not preloaded, + // so we resolve them on demand. + // https://github.com/Tyrrrz/DiscordChatExporter/issues/1261 + if (mention.TargetId is not null) + await context.PopulateChannelAsync(mention.TargetId.Value, cancellationToken); + var channel = mention.TargetId?.Pipe(context.TryGetChannel); var name = channel?.Name ?? "deleted-channel"; diff --git a/DiscordChatExporter.Core/Exporting/PlainTextMessageExtensions.cs b/DiscordChatExporter.Core/Exporting/PlainTextMessageExtensions.cs index 572cfaa7..e12ada26 100644 --- a/DiscordChatExporter.Core/Exporting/PlainTextMessageExtensions.cs +++ b/DiscordChatExporter.Core/Exporting/PlainTextMessageExtensions.cs @@ -1,46 +1,44 @@ -using System.Globalization; +using System.Globalization; using System.Linq; using DiscordChatExporter.Core.Discord.Data; -using DiscordChatExporter.Core.Utils.Extensions; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Exporting; internal static class PlainTextMessageExtensions { - public static string GetFallbackContent(this Message message) => - message.Kind switch - { - MessageKind.RecipientAdd - => message.MentionedUsers.Any() + extension(Message message) + { + public string GetFallbackContent() => + message.Kind switch + { + MessageKind.RecipientAdd => message.MentionedUsers.Any() ? $"Added {message.MentionedUsers.First().DisplayName} to the group." : "Added a recipient.", - MessageKind.RecipientRemove - => message.MentionedUsers.Any() + MessageKind.RecipientRemove => message.MentionedUsers.Any() ? message.Author.Id == message.MentionedUsers.First().Id ? "Left the group." : $"Removed {message.MentionedUsers.First().DisplayName} from the group." : "Removed a recipient.", - MessageKind.Call - => $"Started a call that lasted { - message - .CallEndedTimestamp? - .Pipe(t => t - message.Timestamp) - .Pipe(t => t.TotalMinutes) - .ToString("n0", CultureInfo.InvariantCulture) ?? "0" - } minutes.", + MessageKind.Call => + $"Started a call that lasted {message + .CallEndedTimestamp? + .Pipe(t => t - message.Timestamp) + .Pipe(t => t.TotalMinutes) + .ToString("n0", CultureInfo.InvariantCulture) ?? "0"} minutes.", - MessageKind.ChannelNameChange - => !string.IsNullOrWhiteSpace(message.Content) + MessageKind.ChannelNameChange => !string.IsNullOrWhiteSpace(message.Content) ? $"Changed the channel name: {message.Content}" : "Changed the channel name.", - MessageKind.ChannelIconChange => "Changed the channel icon.", - MessageKind.ChannelPinnedMessage => "Pinned a message.", - MessageKind.ThreadCreated => "Started a thread.", - MessageKind.GuildMemberJoin => "Joined the server.", + MessageKind.ChannelIconChange => "Changed the channel icon.", + MessageKind.ChannelPinnedMessage => "Pinned a message.", + MessageKind.ThreadCreated => "Started a thread.", + MessageKind.GuildMemberJoin => "Joined the server.", - _ => message.Content - }; + _ => message.Content, + }; + } } diff --git a/DiscordChatExporter.Core/Exporting/PlainTextMessageWriter.cs b/DiscordChatExporter.Core/Exporting/PlainTextMessageWriter.cs index 38206ac5..b61e2785 100644 --- a/DiscordChatExporter.Core/Exporting/PlainTextMessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/PlainTextMessageWriter.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; @@ -172,18 +172,21 @@ internal class PlainTextMessageWriter(Stream stream, ExportContext context) await _writer.WriteLineAsync("{Reactions}"); - foreach (var reaction in reactions) + foreach (var (i, reaction) in reactions.Index()) { cancellationToken.ThrowIfCancellationRequested(); + if (i > 0) + { + await _writer.WriteAsync(' '); + } + await _writer.WriteAsync(reaction.Emoji.Name); if (reaction.Count > 1) { await _writer.WriteAsync($" ({reaction.Count})"); } - - await _writer.WriteAsync(' '); } await _writer.WriteLineAsync(); @@ -220,6 +223,31 @@ internal class PlainTextMessageWriter(Stream stream, ExportContext context) await _writer.WriteLineAsync(); } + private async ValueTask WriteForwardedMessageAsync( + MessageSnapshot forwardedMessage, + CancellationToken cancellationToken = default + ) + { + await _writer.WriteLineAsync("{Forwarded Message}"); + + if (!string.IsNullOrWhiteSpace(forwardedMessage.Content)) + { + await _writer.WriteLineAsync( + await FormatMarkdownAsync(forwardedMessage.Content, cancellationToken) + ); + } + + await _writer.WriteLineAsync( + $"Originally sent: {Context.FormatDate(forwardedMessage.Timestamp)}" + ); + + await WriteAttachmentsAsync(forwardedMessage.Attachments, cancellationToken); + await WriteEmbedsAsync(forwardedMessage.Embeds, cancellationToken); + await WriteStickersAsync(forwardedMessage.Stickers, cancellationToken); + + await _writer.WriteLineAsync(); + } + public override async ValueTask WriteMessageAsync( Message message, CancellationToken cancellationToken = default @@ -244,6 +272,12 @@ internal class PlainTextMessageWriter(Stream stream, ExportContext context) await _writer.WriteLineAsync(); + // Forwarded message content + if (message.ForwardedMessage is not null) + { + await WriteForwardedMessageAsync(message.ForwardedMessage, cancellationToken); + } + // Attachments, embeds, reactions, etc. await WriteAttachmentsAsync(message.Attachments, cancellationToken); await WriteEmbedsAsync(message.Embeds, cancellationToken); diff --git a/DiscordChatExporter.Core/Exporting/PostambleTemplate.cshtml b/DiscordChatExporter.Core/Exporting/PostambleTemplate.cshtml index 0bea929f..34e90ca6 100644 --- a/DiscordChatExporter.Core/Exporting/PostambleTemplate.cshtml +++ b/DiscordChatExporter.Core/Exporting/PostambleTemplate.cshtml @@ -1,4 +1,4 @@ -@using System +@using System @inherits RazorBlade.HtmlTemplate diff --git a/DiscordChatExporter.Core/Exporting/PreambleTemplate.cshtml b/DiscordChatExporter.Core/Exporting/PreambleTemplate.cshtml index a71fd831..1b5f300a 100644 --- a/DiscordChatExporter.Core/Exporting/PreambleTemplate.cshtml +++ b/DiscordChatExporter.Core/Exporting/PreambleTemplate.cshtml @@ -1,5 +1,6 @@ -@using System +@using System @using System.Threading.Tasks +@using RazorBlade @inherits RazorBlade.HtmlTemplate @@ -24,10 +25,10 @@ string FormatDate(DateTimeOffset instant, string format = "g") => Context.FormatDate(instant, format); - async ValueTask FormatMarkdownAsync(string markdown) => + async ValueTask FormatMarkdownAsync(string markdown) => Context.Request.ShouldFormatMarkdown - ? await HtmlMarkdownVisitor.FormatAsync(Context, markdown, true, CancellationToken) - : markdown; + ? Html.Raw(await HtmlMarkdownVisitor.FormatAsync(Context, markdown, true, CancellationToken)) + : Html.Raw(Html.Encode(markdown)); } @@ -304,6 +305,52 @@ unicode-bidi: bidi-override; } + .chatlog__forwarded { + display: flex; + flex-direction: column; + margin-bottom: 0.15rem; + padding: 0.5rem; + border-left: 4px solid @Themed("#4f545c", "#c7ccd1"); + border-radius: 4px; + background-color: @Themed("rgba(46, 48, 54, 0.3)", "rgba(249, 249, 249, 0.3)"); + } + + .chatlog__forwarded-header { + display: flex; + align-items: center; + margin-bottom: 0.25rem; + color: @Themed("#b5b6b8", "#5f5f60"); + font-size: 0.75rem; + font-weight: 600; + } + + .chatlog__forwarded-icon { + width: 16px; + height: 16px; + margin-right: 0.25rem; + } + + .chatlog__forwarded-content { + color: @Themed("#dcddde", "#2e3338"); + font-size: 0.95rem; + } + + .chatlog__forwarded-attachments { + margin-top: 0.3rem; + } + + .chatlog__forwarded-attachment { + max-width: 300px; + max-height: 200px; + border-radius: 3px; + } + + .chatlog__forwarded-timestamp { + margin-top: 0.25rem; + color: @Themed("#a3a6aa", "#5e6772"); + font-size: 0.75rem; + } + .chatlog__system-notification-icon { width: 18px; height: 18px; @@ -703,13 +750,19 @@ border: 0; } + .chatlog__embed-twitch { + border: 0; + } + .chatlog__embed-youtube-container { margin-top: 0.6rem; } - .chatlog__embed-youtube { - border: 0; + .chatlog__embed-youtube-thumbnail { + max-width: 400px; + max-height: 225px; border-radius: 3px; + cursor: pointer; } .chatlog__sticker { @@ -993,6 +1046,9 @@ + + + @@ -1008,7 +1064,7 @@ @if (!string.IsNullOrWhiteSpace(Context.Request.Channel.Topic)) { -
@Html.Raw(await FormatMarkdownAsync(Context.Request.Channel.Topic))
+
@(await FormatMarkdownAsync(Context.Request.Channel.Topic))
} @if (Context.Request.After is not null || Context.Request.Before is not null) diff --git a/DiscordChatExporter.Core/Markdown/EmojiNode.cs b/DiscordChatExporter.Core/Markdown/EmojiNode.cs index cc2d2cc7..53bc0676 100644 --- a/DiscordChatExporter.Core/Markdown/EmojiNode.cs +++ b/DiscordChatExporter.Core/Markdown/EmojiNode.cs @@ -1,5 +1,5 @@ -using DiscordChatExporter.Core.Discord; -using DiscordChatExporter.Core.Utils; +using DiscordChatExporter.Core.Discord; +using DiscordChatExporter.Core.Discord.Data; namespace DiscordChatExporter.Core.Markdown; @@ -11,11 +11,17 @@ internal record EmojiNode( bool IsAnimated ) : MarkdownNode { - public bool IsCustomEmoji => Id is not null; - - // Name of a custom emoji (e.g. LUL) or name of a standard emoji (e.g. slight_smile) - public string Code => IsCustomEmoji ? Name : EmojiIndex.TryGetCode(Name) ?? Name; + // This coupling is unsound from the domain-design perspective, but it helps us reuse + // some code for now. We can refactor this later, if the coupling becomes a problem. + private readonly Emoji _emoji = new(Id, Name, IsAnimated); public EmojiNode(string name) : this(null, name, false) { } + + public bool IsCustomEmoji => _emoji.IsCustomEmoji; + + // Name of a custom emoji (e.g. LUL) or name of a standard emoji (e.g. slight_smile) + public string Code => _emoji.Code; + + public string ImageUrl => _emoji.ImageUrl; } diff --git a/DiscordChatExporter.Core/Markdown/FormattingKind.cs b/DiscordChatExporter.Core/Markdown/FormattingKind.cs index 658def7c..bd8fa5ea 100644 --- a/DiscordChatExporter.Core/Markdown/FormattingKind.cs +++ b/DiscordChatExporter.Core/Markdown/FormattingKind.cs @@ -1,4 +1,4 @@ -namespace DiscordChatExporter.Core.Markdown; +namespace DiscordChatExporter.Core.Markdown; internal enum FormattingKind { @@ -7,5 +7,5 @@ internal enum FormattingKind Underline, Strikethrough, Spoiler, - Quote + Quote, } diff --git a/DiscordChatExporter.Core/Markdown/FormattingNode.cs b/DiscordChatExporter.Core/Markdown/FormattingNode.cs index 5920119e..f088619f 100644 --- a/DiscordChatExporter.Core/Markdown/FormattingNode.cs +++ b/DiscordChatExporter.Core/Markdown/FormattingNode.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DiscordChatExporter.Core.Markdown; diff --git a/DiscordChatExporter.Core/Markdown/HeadingNode.cs b/DiscordChatExporter.Core/Markdown/HeadingNode.cs index 7f35991e..ecb689d0 100644 --- a/DiscordChatExporter.Core/Markdown/HeadingNode.cs +++ b/DiscordChatExporter.Core/Markdown/HeadingNode.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DiscordChatExporter.Core.Markdown; diff --git a/DiscordChatExporter.Core/Markdown/IContainerNode.cs b/DiscordChatExporter.Core/Markdown/IContainerNode.cs index 596baf4d..22a59c8f 100644 --- a/DiscordChatExporter.Core/Markdown/IContainerNode.cs +++ b/DiscordChatExporter.Core/Markdown/IContainerNode.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DiscordChatExporter.Core.Markdown; diff --git a/DiscordChatExporter.Core/Markdown/InlineCodeBlockNode.cs b/DiscordChatExporter.Core/Markdown/InlineCodeBlockNode.cs index 0db91a2e..8d29edc0 100644 --- a/DiscordChatExporter.Core/Markdown/InlineCodeBlockNode.cs +++ b/DiscordChatExporter.Core/Markdown/InlineCodeBlockNode.cs @@ -1,3 +1,3 @@ -namespace DiscordChatExporter.Core.Markdown; +namespace DiscordChatExporter.Core.Markdown; internal record InlineCodeBlockNode(string Code) : MarkdownNode; diff --git a/DiscordChatExporter.Core/Markdown/LinkNode.cs b/DiscordChatExporter.Core/Markdown/LinkNode.cs index 4f9d7e40..47a5cd2c 100644 --- a/DiscordChatExporter.Core/Markdown/LinkNode.cs +++ b/DiscordChatExporter.Core/Markdown/LinkNode.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DiscordChatExporter.Core.Markdown; diff --git a/DiscordChatExporter.Core/Markdown/ListItemNode.cs b/DiscordChatExporter.Core/Markdown/ListItemNode.cs index e980e534..3898434d 100644 --- a/DiscordChatExporter.Core/Markdown/ListItemNode.cs +++ b/DiscordChatExporter.Core/Markdown/ListItemNode.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DiscordChatExporter.Core.Markdown; diff --git a/DiscordChatExporter.Core/Markdown/ListNode.cs b/DiscordChatExporter.Core/Markdown/ListNode.cs index 2d6f6fd9..183cbc31 100644 --- a/DiscordChatExporter.Core/Markdown/ListNode.cs +++ b/DiscordChatExporter.Core/Markdown/ListNode.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DiscordChatExporter.Core.Markdown; diff --git a/DiscordChatExporter.Core/Markdown/MarkdownNode.cs b/DiscordChatExporter.Core/Markdown/MarkdownNode.cs index 77bcbb33..977904f9 100644 --- a/DiscordChatExporter.Core/Markdown/MarkdownNode.cs +++ b/DiscordChatExporter.Core/Markdown/MarkdownNode.cs @@ -1,3 +1,3 @@ -namespace DiscordChatExporter.Core.Markdown; +namespace DiscordChatExporter.Core.Markdown; internal abstract record MarkdownNode; diff --git a/DiscordChatExporter.Core/Markdown/MentionKind.cs b/DiscordChatExporter.Core/Markdown/MentionKind.cs index dbd1bffb..44088e2d 100644 --- a/DiscordChatExporter.Core/Markdown/MentionKind.cs +++ b/DiscordChatExporter.Core/Markdown/MentionKind.cs @@ -1,4 +1,4 @@ -namespace DiscordChatExporter.Core.Markdown; +namespace DiscordChatExporter.Core.Markdown; internal enum MentionKind { @@ -6,5 +6,5 @@ internal enum MentionKind Here, User, Channel, - Role + Role, } diff --git a/DiscordChatExporter.Core/Markdown/MentionNode.cs b/DiscordChatExporter.Core/Markdown/MentionNode.cs index d558893c..1c00272e 100644 --- a/DiscordChatExporter.Core/Markdown/MentionNode.cs +++ b/DiscordChatExporter.Core/Markdown/MentionNode.cs @@ -1,4 +1,4 @@ -using DiscordChatExporter.Core.Discord; +using DiscordChatExporter.Core.Discord; namespace DiscordChatExporter.Core.Markdown; diff --git a/DiscordChatExporter.Core/Markdown/MultiLineCodeBlockNode.cs b/DiscordChatExporter.Core/Markdown/MultiLineCodeBlockNode.cs index 0f78b96b..4fe07735 100644 --- a/DiscordChatExporter.Core/Markdown/MultiLineCodeBlockNode.cs +++ b/DiscordChatExporter.Core/Markdown/MultiLineCodeBlockNode.cs @@ -1,3 +1,3 @@ -namespace DiscordChatExporter.Core.Markdown; +namespace DiscordChatExporter.Core.Markdown; internal record MultiLineCodeBlockNode(string Language, string Code) : MarkdownNode; diff --git a/DiscordChatExporter.Core/Markdown/Parsing/AggregateMatcher.cs b/DiscordChatExporter.Core/Markdown/Parsing/AggregateMatcher.cs index 6a3e6e7c..95706bea 100644 --- a/DiscordChatExporter.Core/Markdown/Parsing/AggregateMatcher.cs +++ b/DiscordChatExporter.Core/Markdown/Parsing/AggregateMatcher.cs @@ -1,21 +1,20 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DiscordChatExporter.Core.Markdown.Parsing; -internal class AggregateMatcher(IReadOnlyList> matchers) : IMatcher +internal class AggregateMatcher( + params IReadOnlyList> matchers +) : IMatcher { - public AggregateMatcher(params IMatcher[] matchers) - : this((IReadOnlyList>)matchers) { } - - public ParsedMatch? TryMatch(StringSegment segment) + public ParsedMatch? TryMatch(TContext context, StringSegment segment) { - ParsedMatch? earliestMatch = null; + ParsedMatch? earliestMatch = null; // Try to match the input with each matcher and get the match with the lowest start index foreach (var matcher in matchers) { // Try to match - var match = matcher.TryMatch(segment); + var match = matcher.TryMatch(context, segment); // If there's no match - continue if (match is null) @@ -26,7 +25,9 @@ internal class AggregateMatcher(IReadOnlyList> matchers) : IMatch earliestMatch is null || match.Segment.StartIndex < earliestMatch.Segment.StartIndex ) + { earliestMatch = match; + } // If the earliest match starts at the very beginning - break, // because it's impossible to find a match earlier than that diff --git a/DiscordChatExporter.Core/Markdown/Parsing/IMatcher.cs b/DiscordChatExporter.Core/Markdown/Parsing/IMatcher.cs index 4c71c0a0..3a5821c5 100644 --- a/DiscordChatExporter.Core/Markdown/Parsing/IMatcher.cs +++ b/DiscordChatExporter.Core/Markdown/Parsing/IMatcher.cs @@ -1,19 +1,20 @@ -using System; +using System; using System.Collections.Generic; namespace DiscordChatExporter.Core.Markdown.Parsing; -internal interface IMatcher +internal interface IMatcher { - ParsedMatch? TryMatch(StringSegment segment); + ParsedMatch? TryMatch(TContext context, StringSegment segment); } internal static class MatcherExtensions { - public static IEnumerable> MatchAll( - this IMatcher matcher, + public static IEnumerable> MatchAll( + this IMatcher matcher, + TContext context, StringSegment segment, - Func transformFallback + Func fallbackTransform ) { // Loop through segments divided by individual matches @@ -22,6 +23,7 @@ internal static class MatcherExtensions { // Find a match within this segment var match = matcher.TryMatch( + context, segment.Relocate(currentIndex, segment.EndIndex - currentIndex) ); @@ -36,9 +38,9 @@ internal static class MatcherExtensions match.Segment.StartIndex - currentIndex ); - yield return new ParsedMatch( + yield return new ParsedMatch( fallbackSegment, - transformFallback(fallbackSegment) + fallbackTransform(context, fallbackSegment) ); } @@ -53,7 +55,10 @@ internal static class MatcherExtensions { var fallbackSegment = segment.Relocate(currentIndex, segment.EndIndex - currentIndex); - yield return new ParsedMatch(fallbackSegment, transformFallback(fallbackSegment)); + yield return new ParsedMatch( + fallbackSegment, + fallbackTransform(context, fallbackSegment) + ); } } } diff --git a/DiscordChatExporter.Core/Markdown/Parsing/MarkdownContext.cs b/DiscordChatExporter.Core/Markdown/Parsing/MarkdownContext.cs new file mode 100644 index 00000000..0235c683 --- /dev/null +++ b/DiscordChatExporter.Core/Markdown/Parsing/MarkdownContext.cs @@ -0,0 +1,3 @@ +namespace DiscordChatExporter.Core.Markdown.Parsing; + +internal readonly record struct MarkdownContext(int Depth = 0); diff --git a/DiscordChatExporter.Core/Markdown/Parsing/MarkdownParser.cs b/DiscordChatExporter.Core/Markdown/Parsing/MarkdownParser.cs index 28b813af..f0fb1a17 100644 --- a/DiscordChatExporter.Core/Markdown/Parsing/MarkdownParser.cs +++ b/DiscordChatExporter.Core/Markdown/Parsing/MarkdownParser.cs @@ -1,11 +1,11 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Utils; -using DiscordChatExporter.Core.Utils.Extensions; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Markdown.Parsing; @@ -23,15 +23,15 @@ internal static partial class MarkdownParser /* Formatting */ - private static readonly IMatcher BoldFormattingNodeMatcher = - new RegexMatcher( + private static readonly IMatcher BoldFormattingNodeMatcher = + new RegexMatcher( // There must be exactly two closing asterisks. new Regex(@"\*\*(.+?)\*\*(?!\*)", DefaultRegexOptions | RegexOptions.Singleline), - (s, m) => new FormattingNode(FormattingKind.Bold, Parse(s.Relocate(m.Groups[1]))) + (c, s, m) => new FormattingNode(FormattingKind.Bold, Parse(c, s.Relocate(m.Groups[1]))) ); - private static readonly IMatcher ItalicFormattingNodeMatcher = - new RegexMatcher( + private static readonly IMatcher ItalicFormattingNodeMatcher = + new RegexMatcher( // There must be exactly one closing asterisk. // Opening asterisk must not be followed by whitespace. // Closing asterisk must not be preceded by whitespace. @@ -39,221 +39,204 @@ internal static partial class MarkdownParser @"\*(?!\s)(.+?)(? new FormattingNode(FormattingKind.Italic, Parse(s.Relocate(m.Groups[1]))) + (c, s, m) => + new FormattingNode(FormattingKind.Italic, Parse(c, s.Relocate(m.Groups[1]))) ); - private static readonly IMatcher ItalicBoldFormattingNodeMatcher = - new RegexMatcher( - // There must be exactly three closing asterisks. - new Regex(@"\*(\*\*.+?\*\*)\*(?!\*)", DefaultRegexOptions | RegexOptions.Singleline), - (s, m) => - new FormattingNode( - FormattingKind.Italic, - Parse(s.Relocate(m.Groups[1]), BoldFormattingNodeMatcher) - ) - ); - - private static readonly IMatcher ItalicAltFormattingNodeMatcher = - new RegexMatcher( - // Closing underscore must not be followed by a word character. - new Regex(@"_(.+?)_(?!\w)", DefaultRegexOptions | RegexOptions.Singleline), - (s, m) => new FormattingNode(FormattingKind.Italic, Parse(s.Relocate(m.Groups[1]))) - ); - - private static readonly IMatcher UnderlineFormattingNodeMatcher = - new RegexMatcher( - // There must be exactly two closing underscores. - new Regex(@"__(.+?)__(?!_)", DefaultRegexOptions | RegexOptions.Singleline), - (s, m) => new FormattingNode(FormattingKind.Underline, Parse(s.Relocate(m.Groups[1]))) - ); - - private static readonly IMatcher ItalicUnderlineFormattingNodeMatcher = - new RegexMatcher( - // There must be exactly three closing underscores. - new Regex(@"_(__.+?__)_(?!_)", DefaultRegexOptions | RegexOptions.Singleline), - (s, m) => - new FormattingNode( - FormattingKind.Italic, - Parse(s.Relocate(m.Groups[1]), UnderlineFormattingNodeMatcher) - ) - ); - - private static readonly IMatcher StrikethroughFormattingNodeMatcher = - new RegexMatcher( - new Regex(@"~~(.+?)~~", DefaultRegexOptions | RegexOptions.Singleline), - (s, m) => - new FormattingNode(FormattingKind.Strikethrough, Parse(s.Relocate(m.Groups[1]))) - ); - - private static readonly IMatcher SpoilerFormattingNodeMatcher = - new RegexMatcher( - new Regex(@"\|\|(.+?)\|\|", DefaultRegexOptions | RegexOptions.Singleline), - (s, m) => new FormattingNode(FormattingKind.Spoiler, Parse(s.Relocate(m.Groups[1]))) - ); - - private static readonly IMatcher SingleLineQuoteNodeMatcher = - new RegexMatcher( - // Include the linebreak in the content so that the lines are preserved in quotes. - new Regex(@"^>\s(.+\n?)", DefaultRegexOptions), - (s, m) => new FormattingNode(FormattingKind.Quote, Parse(s.Relocate(m.Groups[1]))) - ); - - private static readonly IMatcher RepeatedSingleLineQuoteNodeMatcher = - new RegexMatcher( - // Include the linebreaks in the content, so that the lines are preserved in quotes. - // Empty content is allowed within quotes. - // https://github.com/Tyrrrz/DiscordChatExporter/issues/1115 - new Regex(@"(?:^>\s(.*\n?)){2,}", DefaultRegexOptions), - (s, m) => - new FormattingNode( - FormattingKind.Quote, - m.Groups[1].Captures.SelectMany(c => Parse(s.Relocate(c))).ToArray() - ) - ); - - private static readonly IMatcher MultiLineQuoteNodeMatcher = - new RegexMatcher( - new Regex(@"^>>>\s(.+)", DefaultRegexOptions | RegexOptions.Singleline), - (s, m) => new FormattingNode(FormattingKind.Quote, Parse(s.Relocate(m.Groups[1]))) - ); - - private static readonly IMatcher HeadingNodeMatcher = - new RegexMatcher( - // Consume the linebreak so that it's not attached to following nodes. - new Regex(@"^(\#{1,3})\s(.+)\n", DefaultRegexOptions), - (s, m) => new HeadingNode(m.Groups[1].Length, Parse(s.Relocate(m.Groups[2]))) - ); - - private static readonly IMatcher ListNodeMatcher = new RegexMatcher( - // Can be preceded by whitespace, which specifies the list's nesting level. - // Following lines that start with (level+1) whitespace are considered part of the list item. - // Consume the linebreak so that it's not attached to following nodes. - new Regex(@"^(\s*)(?:[\-\*]\s(.+(?:\n\s\1.*)*)?\n?)+", DefaultRegexOptions), - (s, m) => - new ListNode( - m.Groups[2].Captures.Select(c => new ListItemNode(Parse(s.Relocate(c)))).ToArray() + private static readonly IMatcher< + MarkdownContext, + MarkdownNode + > ItalicBoldFormattingNodeMatcher = new RegexMatcher( + // There must be exactly three closing asterisks. + new Regex(@"\*(\*\*.+?\*\*)\*(?!\*)", DefaultRegexOptions | RegexOptions.Singleline), + (c, s, m) => + new FormattingNode( + FormattingKind.Italic, + Parse(c, s.Relocate(m.Groups[1]), BoldFormattingNodeMatcher) ) ); - /* Code blocks */ - - private static readonly IMatcher InlineCodeBlockNodeMatcher = - new RegexMatcher( - // One or two backticks are allowed, but they must match on both sides. - new Regex(@"(`{1,2})([^`]+)\1", DefaultRegexOptions | RegexOptions.Singleline), - (_, m) => new InlineCodeBlockNode(m.Groups[2].Value) + private static readonly IMatcher ItalicAltFormattingNodeMatcher = + new RegexMatcher( + // Closing underscore must not be followed by a word character. + new Regex(@"_(.+?)_(?!\w)", DefaultRegexOptions | RegexOptions.Singleline), + (c, s, m) => + new FormattingNode(FormattingKind.Italic, Parse(c, s.Relocate(m.Groups[1]))) ); - private static readonly IMatcher MultiLineCodeBlockNodeMatcher = - new RegexMatcher( + private static readonly IMatcher UnderlineFormattingNodeMatcher = + new RegexMatcher( + // There must be exactly two closing underscores. + new Regex(@"__(.+?)__(?!_)", DefaultRegexOptions | RegexOptions.Singleline), + (c, s, m) => + new FormattingNode(FormattingKind.Underline, Parse(c, s.Relocate(m.Groups[1]))) + ); + + private static readonly IMatcher< + MarkdownContext, + MarkdownNode + > ItalicUnderlineFormattingNodeMatcher = new RegexMatcher( + // There must be exactly three closing underscores. + new Regex(@"_(__.+?__)_(?!_)", DefaultRegexOptions | RegexOptions.Singleline), + (c, s, m) => + new FormattingNode( + FormattingKind.Italic, + Parse(c, s.Relocate(m.Groups[1]), UnderlineFormattingNodeMatcher) + ) + ); + + private static readonly IMatcher< + MarkdownContext, + MarkdownNode + > StrikethroughFormattingNodeMatcher = new RegexMatcher( + new Regex(@"~~(.+?)~~", DefaultRegexOptions | RegexOptions.Singleline), + (c, s, m) => + new FormattingNode(FormattingKind.Strikethrough, Parse(c, s.Relocate(m.Groups[1]))) + ); + + private static readonly IMatcher SpoilerFormattingNodeMatcher = + new RegexMatcher( + new Regex(@"\|\|(.+?)\|\|", DefaultRegexOptions | RegexOptions.Singleline), + (c, s, m) => + new FormattingNode(FormattingKind.Spoiler, Parse(c, s.Relocate(m.Groups[1]))) + ); + + private static readonly IMatcher SingleLineQuoteNodeMatcher = + new RegexMatcher( + // Include the linebreak in the content so that the lines are preserved in quotes. + new Regex(@"^>\s(.+\n?)", DefaultRegexOptions), + (c, s, m) => new FormattingNode(FormattingKind.Quote, Parse(c, s.Relocate(m.Groups[1]))) + ); + + private static readonly IMatcher< + MarkdownContext, + MarkdownNode + > RepeatedSingleLineQuoteNodeMatcher = new RegexMatcher( + // Include the linebreaks in the content, so that the lines are preserved in quotes. + // Empty content is allowed within quotes. + // https://github.com/Tyrrrz/DiscordChatExporter/issues/1115 + new Regex(@"(?:^>\s(.*\n?)){2,}", DefaultRegexOptions), + (c, s, m) => + new FormattingNode( + FormattingKind.Quote, + m.Groups[1].Captures.SelectMany(r => Parse(c, s.Relocate(r))).ToArray() + ) + ); + + private static readonly IMatcher MultiLineQuoteNodeMatcher = + new RegexMatcher( + new Regex(@"^>>>\s(.+)", DefaultRegexOptions | RegexOptions.Singleline), + (c, s, m) => new FormattingNode(FormattingKind.Quote, Parse(c, s.Relocate(m.Groups[1]))) + ); + + private static readonly IMatcher HeadingNodeMatcher = + new RegexMatcher( + // Consume the linebreak so that it's not attached to following nodes. + new Regex(@"^(\#{1,3})\s(.+)\n", DefaultRegexOptions), + (c, s, m) => new HeadingNode(m.Groups[1].Length, Parse(c, s.Relocate(m.Groups[2]))) + ); + + private static readonly IMatcher ListNodeMatcher = + new RegexMatcher( + // Can be preceded by whitespace, which specifies the list's nesting level. + // Following lines that start with (level+1) whitespace are considered part of the list item. + // Consume the linebreak so that it's not attached to following nodes. + new Regex(@"^(\s*)(?:[\-\*]\s(.+(?:\n\s\1.*)*)?\n?)+", DefaultRegexOptions), + (c, s, m) => + new ListNode( + m.Groups[2] + .Captures.Select(x => new ListItemNode(Parse(c, s.Relocate(x)))) + .ToArray() + ) + ); + + /* Code blocks */ + + private static readonly IMatcher InlineCodeBlockNodeMatcher = + new RegexMatcher( + // One or two backticks are allowed, but they must match on both sides. + new Regex(@"(`{1,2})([^`]+)\1", DefaultRegexOptions | RegexOptions.Singleline), + (_, _, m) => new InlineCodeBlockNode(m.Groups[2].Value) + ); + + private static readonly IMatcher MultiLineCodeBlockNodeMatcher = + new RegexMatcher( // Language identifier is one word immediately after opening backticks, followed immediately by a linebreak. // Blank lines at the beginning and at the end of content are trimmed. new Regex(@"```(?:(\w*)\n)?(.+?)```", DefaultRegexOptions | RegexOptions.Singleline), - (_, m) => + (_, _, m) => new MultiLineCodeBlockNode(m.Groups[1].Value, m.Groups[2].Value.Trim('\r', '\n')) ); /* Mentions */ - private static readonly IMatcher EveryoneMentionNodeMatcher = - new StringMatcher( + private static readonly IMatcher EveryoneMentionNodeMatcher = + new StringMatcher( "@everyone", - _ => new MentionNode(null, MentionKind.Everyone) + (_, _) => new MentionNode(null, MentionKind.Everyone) ); - private static readonly IMatcher HereMentionNodeMatcher = - new StringMatcher("@here", _ => new MentionNode(null, MentionKind.Here)); + private static readonly IMatcher HereMentionNodeMatcher = + new StringMatcher( + "@here", + (_, _) => new MentionNode(null, MentionKind.Here) + ); - private static readonly IMatcher UserMentionNodeMatcher = - new RegexMatcher( + private static readonly IMatcher UserMentionNodeMatcher = + new RegexMatcher( // Capture <@123456> or <@!123456> new Regex(@"<@!?(\d+)>", DefaultRegexOptions), - (_, m) => new MentionNode(Snowflake.TryParse(m.Groups[1].Value), MentionKind.User) + (_, _, m) => new MentionNode(Snowflake.TryParse(m.Groups[1].Value), MentionKind.User) ); - private static readonly IMatcher ChannelMentionNodeMatcher = - new RegexMatcher( + private static readonly IMatcher ChannelMentionNodeMatcher = + new RegexMatcher( // Capture <#123456> new Regex(@"<\#!?(\d+)>", DefaultRegexOptions), - (_, m) => new MentionNode(Snowflake.TryParse(m.Groups[1].Value), MentionKind.Channel) + (_, _, m) => new MentionNode(Snowflake.TryParse(m.Groups[1].Value), MentionKind.Channel) ); - private static readonly IMatcher RoleMentionNodeMatcher = - new RegexMatcher( + private static readonly IMatcher RoleMentionNodeMatcher = + new RegexMatcher( // Capture <@&123456> new Regex(@"<@&(\d+)>", DefaultRegexOptions), - (_, m) => new MentionNode(Snowflake.TryParse(m.Groups[1].Value), MentionKind.Role) + (_, _, m) => new MentionNode(Snowflake.TryParse(m.Groups[1].Value), MentionKind.Role) ); /* Emoji */ - private static readonly IMatcher StandardEmojiNodeMatcher = - new RegexMatcher( + private static readonly IMatcher StandardEmojiNodeMatcher = + new RegexMatcher( new Regex( - @"(" - + - // Country flag emoji (two regional indicator surrogate pairs) - @"(?:\uD83C[\uDDE6-\uDDFF]){2}|" - + - // Digit emoji (digit followed by enclosing mark) - @"\d\p{Me}|" - + - // Surrogate pair - @"\p{Cs}{2}|" - + - // Miscellaneous characters - @"[" - + @"\u2600-\u2604" - + @"\u260E\u2611" - + @"\u2614-\u2615" - + @"\u2618\u261D\u2620" - + @"\u2622-\u2623" - + @"\u2626\u262A" - + @"\u262E-\u262F" - + @"\u2638-\u263A" - + @"\u2640\u2642" - + @"\u2648-\u2653" - + @"\u265F-\u2660" - + @"\u2663" - + @"\u2665-\u2666" - + @"\u2668\u267B" - + @"\u267E-\u267F" - + @"\u2692-\u2697" - + @"\u2699" - + @"\u269B-\u269C" - + @"\u26A0-\u26A1" - + @"\u26A7" - + @"\u26AA-\u26AB" - + @"\u26B0-\u26B1" - + @"\u26BD-\u26BE" - + @"\u26C4-\u26C5" - + @"\u26C8" - + @"\u26CE-\u26CF" - + @"\u26D1" - + @"\u26D3-\u26D4" - + @"\u26E9-\u26EA" - + @"\u26F0-\u26F5" - + @"\u26F7-\u26FA" - + @"\u26FD" - + @"]" - + @")", + // Build a pattern from all known emoji, sorted longest-first so that compound + // emoji (e.g. sequences with ZWJ or skin-tone modifiers) are matched before + // their individual components. + "(" + + string.Join( + "|", + EmojiIndex + .GetAllNames() + .OrderByDescending(e => e.Length) + .Select(Regex.Escape) + ) + + ")", DefaultRegexOptions ), - (_, m) => new EmojiNode(m.Groups[1].Value) + (_, _, m) => new EmojiNode(m.Groups[1].Value) ); - private static readonly IMatcher CodedStandardEmojiNodeMatcher = - new RegexMatcher( + private static readonly IMatcher CodedStandardEmojiNodeMatcher = + new RegexMatcher( // Capture :thinking: new Regex(@":([\w_]+):", DefaultRegexOptions), - (_, m) => EmojiIndex.TryGetName(m.Groups[1].Value)?.Pipe(n => new EmojiNode(n)) + (_, _, m) => EmojiIndex.TryGetName(m.Groups[1].Value)?.Pipe(n => new EmojiNode(n)) ); - private static readonly IMatcher CustomEmojiNodeMatcher = - new RegexMatcher( + private static readonly IMatcher CustomEmojiNodeMatcher = + new RegexMatcher( // Capture <:lul:123456> or new Regex(@"<(a)?:(.+?):(\d+?)>", DefaultRegexOptions), - (_, m) => + (_, _, m) => new EmojiNode( Snowflake.TryParse(m.Groups[3].Value), m.Groups[2].Value, @@ -263,70 +246,72 @@ internal static partial class MarkdownParser /* Links */ - private static readonly IMatcher AutoLinkNodeMatcher = - new RegexMatcher( + private static readonly IMatcher AutoLinkNodeMatcher = + new RegexMatcher( // Any non-whitespace character after http:// or https:// // until the last punctuation character or whitespace. new Regex(@"(https?://\S*[^\.,:;""'\s])", DefaultRegexOptions), - (_, m) => new LinkNode(m.Groups[1].Value) + (_, _, m) => new LinkNode(m.Groups[1].Value) ); - private static readonly IMatcher HiddenLinkNodeMatcher = - new RegexMatcher( + private static readonly IMatcher HiddenLinkNodeMatcher = + new RegexMatcher( // Same as auto link but also surrounded by angular brackets new Regex(@"<(https?://\S*[^\.,:;""'\s])>", DefaultRegexOptions), - (_, m) => new LinkNode(m.Groups[1].Value) + (_, _, m) => new LinkNode(m.Groups[1].Value) ); - private static readonly IMatcher MaskedLinkNodeMatcher = - new RegexMatcher( + private static readonly IMatcher MaskedLinkNodeMatcher = + new RegexMatcher( // Capture [title](link) new Regex(@"\[(.+?)\]\((.+?)\)", DefaultRegexOptions), - (s, m) => new LinkNode(m.Groups[2].Value, Parse(s.Relocate(m.Groups[1]))) + (c, s, m) => new LinkNode(m.Groups[2].Value, Parse(c, s.Relocate(m.Groups[1]))) ); /* Text */ - private static readonly IMatcher ShrugTextNodeMatcher = - new StringMatcher( + private static readonly IMatcher ShrugTextNodeMatcher = + new StringMatcher( // Capture the shrug kaomoji. // This escapes it from matching for formatting. @"¯\_(ツ)_/¯", - s => new TextNode(s.ToString()) + (_, s) => new TextNode(s.ToString()) ); - private static readonly IMatcher IgnoredEmojiTextNodeMatcher = - new RegexMatcher( + private static readonly IMatcher IgnoredEmojiTextNodeMatcher = + new RegexMatcher( // Capture some specific emoji that don't get rendered. // This escapes them from matching for emoji. new Regex(@"([\u26A7\u2640\u2642\u2695\u267E\u00A9\u00AE\u2122])", DefaultRegexOptions), - (_, m) => new TextNode(m.Groups[1].Value) + (_, _, m) => new TextNode(m.Groups[1].Value) ); - private static readonly IMatcher EscapedSymbolTextNodeMatcher = - new RegexMatcher( + private static readonly IMatcher EscapedSymbolTextNodeMatcher = + new RegexMatcher( // Capture any "symbol/other" character or surrogate pair preceded by a backslash. // This escapes them from matching for emoji. // https://github.com/Tyrrrz/DiscordChatExporter/issues/230 new Regex(@"\\(\p{So}|\p{Cs}{2})", DefaultRegexOptions), - (_, m) => new TextNode(m.Groups[1].Value) + (_, _, m) => new TextNode(m.Groups[1].Value) ); - private static readonly IMatcher EscapedCharacterTextNodeMatcher = - new RegexMatcher( - // Capture any non-whitespace, non latin alphanumeric character preceded by a backslash. - // This escapes them from matching for formatting or other tokens. - new Regex(@"\\([^a-zA-Z0-9\s])", DefaultRegexOptions), - (_, m) => new TextNode(m.Groups[1].Value) - ); + private static readonly IMatcher< + MarkdownContext, + MarkdownNode + > EscapedCharacterTextNodeMatcher = new RegexMatcher( + // Capture any non-whitespace, non latin alphanumeric character preceded by a backslash. + // This escapes them from matching for formatting or other tokens. + new Regex(@"\\([^a-zA-Z0-9\s])", DefaultRegexOptions), + (_, _, m) => new TextNode(m.Groups[1].Value) + ); /* Misc */ - private static readonly IMatcher TimestampNodeMatcher = - new RegexMatcher( + private static readonly IMatcher TimestampNodeMatcher = + new RegexMatcher( // Capture or new Regex(@"", DefaultRegexOptions), - (_, m) => + (_, _, m) => { try { @@ -357,10 +342,9 @@ internal static partial class MarkdownParser null => null, // Unknown format: throw an exception to consider this timestamp invalid // https://github.com/Tyrrrz/DiscordChatExporter/issues/1156 - var f - => throw new InvalidOperationException( - $"Unknown timestamp format '{f}'." - ) + var f => throw new InvalidOperationException( + $"Unknown timestamp format '{f}'." + ), }; return new TimestampNode(instant, format); @@ -382,50 +366,51 @@ internal static partial class MarkdownParser ); // Matchers that have similar patterns are ordered from most specific to least specific - private static readonly IMatcher NodeMatcher = new AggregateMatcher( - // Escaped text - ShrugTextNodeMatcher, - IgnoredEmojiTextNodeMatcher, - EscapedSymbolTextNodeMatcher, - EscapedCharacterTextNodeMatcher, - // Formatting - ItalicBoldFormattingNodeMatcher, - ItalicUnderlineFormattingNodeMatcher, - BoldFormattingNodeMatcher, - ItalicFormattingNodeMatcher, - UnderlineFormattingNodeMatcher, - ItalicAltFormattingNodeMatcher, - StrikethroughFormattingNodeMatcher, - SpoilerFormattingNodeMatcher, - MultiLineQuoteNodeMatcher, - RepeatedSingleLineQuoteNodeMatcher, - SingleLineQuoteNodeMatcher, - HeadingNodeMatcher, - ListNodeMatcher, - // Code blocks - MultiLineCodeBlockNodeMatcher, - InlineCodeBlockNodeMatcher, - // Mentions - EveryoneMentionNodeMatcher, - HereMentionNodeMatcher, - UserMentionNodeMatcher, - ChannelMentionNodeMatcher, - RoleMentionNodeMatcher, - // Links - MaskedLinkNodeMatcher, - AutoLinkNodeMatcher, - HiddenLinkNodeMatcher, - // Emoji - StandardEmojiNodeMatcher, - CustomEmojiNodeMatcher, - CodedStandardEmojiNodeMatcher, - // Misc - TimestampNodeMatcher - ); + private static readonly IMatcher NodeMatcher = + new AggregateMatcher( + // Escaped text + ShrugTextNodeMatcher, + IgnoredEmojiTextNodeMatcher, + EscapedSymbolTextNodeMatcher, + EscapedCharacterTextNodeMatcher, + // Formatting + ItalicBoldFormattingNodeMatcher, + ItalicUnderlineFormattingNodeMatcher, + BoldFormattingNodeMatcher, + ItalicFormattingNodeMatcher, + UnderlineFormattingNodeMatcher, + ItalicAltFormattingNodeMatcher, + StrikethroughFormattingNodeMatcher, + SpoilerFormattingNodeMatcher, + MultiLineQuoteNodeMatcher, + RepeatedSingleLineQuoteNodeMatcher, + SingleLineQuoteNodeMatcher, + HeadingNodeMatcher, + ListNodeMatcher, + // Code blocks + MultiLineCodeBlockNodeMatcher, + InlineCodeBlockNodeMatcher, + // Mentions + EveryoneMentionNodeMatcher, + HereMentionNodeMatcher, + UserMentionNodeMatcher, + ChannelMentionNodeMatcher, + RoleMentionNodeMatcher, + // Links + MaskedLinkNodeMatcher, + AutoLinkNodeMatcher, + HiddenLinkNodeMatcher, + // Emoji + StandardEmojiNodeMatcher, + CustomEmojiNodeMatcher, + CodedStandardEmojiNodeMatcher, + // Misc + TimestampNodeMatcher + ); // Minimal set of matchers for non-multimedia formats (e.g. plain text) - private static readonly IMatcher MinimalNodeMatcher = - new AggregateMatcher( + private static readonly IMatcher MinimalNodeMatcher = + new AggregateMatcher( // Mentions EveryoneMentionNodeMatcher, HereMentionNodeMatcher, @@ -439,42 +424,75 @@ internal static partial class MarkdownParser ); private static IReadOnlyList Parse( + MarkdownContext context, StringSegment segment, - IMatcher matcher - ) => matcher.MatchAll(segment, s => new TextNode(s.ToString())).Select(r => r.Value).ToArray(); + IMatcher matcher + ) + { + // Limit recursion depth to a reasonable number to prevent + // stack overflow on messages with inadvertently deep nesting. + // Example: ********************************* (repeat ad nauseam) + // https://github.com/Tyrrrz/DiscordChatExporter/issues/1214 + if (context.Depth >= 32) + return [new TextNode(segment.ToString())]; + + return matcher + .MatchAll( + new MarkdownContext(context.Depth + 1), + segment, + (_, s) => new TextNode(s.ToString()) + ) + .Select(r => r.Value) + .ToArray(); + } } internal static partial class MarkdownParser { - private static IReadOnlyList Parse(StringSegment segment) => - Parse(segment, NodeMatcher); - - public static IReadOnlyList Parse(string markdown) => - Parse(new StringSegment(markdown)); - - private static IReadOnlyList ParseMinimal(StringSegment segment) => - Parse(segment, MinimalNodeMatcher); - - public static IReadOnlyList ParseMinimal(string markdown) => - ParseMinimal(new StringSegment(markdown)); - - private static void ExtractLinks(IEnumerable nodes, ICollection links) + private static void Extract( + IEnumerable nodes, + ICollection extractedNodes + ) + where TNode : MarkdownNode { foreach (var node in nodes) { - if (node is LinkNode linkNode) - links.Add(linkNode); + if (node is TNode extractedNode) + extractedNodes.Add(extractedNode); if (node is IContainerNode containerNode) - ExtractLinks(containerNode.Children, links); + Extract(containerNode.Children, extractedNodes); } } - public static IReadOnlyList ExtractLinks(string markdown) + public static IReadOnlyList Extract(string markdown) + where TNode : MarkdownNode { - var links = new List(); - ExtractLinks(Parse(markdown), links); + var extractedNodes = new List(); + Extract(Parse(markdown), extractedNodes); - return links; + return extractedNodes; } + + public static IReadOnlyList ExtractLinks(string markdown) => + Extract(markdown); + + public static IReadOnlyList ExtractEmojis(string markdown) => + Extract(markdown); + + private static IReadOnlyList Parse( + MarkdownContext context, + StringSegment segment + ) => Parse(context, segment, NodeMatcher); + + public static IReadOnlyList Parse(string markdown) => + Parse(new MarkdownContext(), new StringSegment(markdown)); + + private static IReadOnlyList ParseMinimal( + MarkdownContext context, + StringSegment segment + ) => Parse(context, segment, MinimalNodeMatcher); + + public static IReadOnlyList ParseMinimal(string markdown) => + ParseMinimal(new MarkdownContext(), new StringSegment(markdown)); } diff --git a/DiscordChatExporter.Core/Markdown/Parsing/MarkdownVisitor.cs b/DiscordChatExporter.Core/Markdown/Parsing/MarkdownVisitor.cs index f80644b5..a30c9b10 100644 --- a/DiscordChatExporter.Core/Markdown/Parsing/MarkdownVisitor.cs +++ b/DiscordChatExporter.Core/Markdown/Parsing/MarkdownVisitor.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/DiscordChatExporter.Core/Markdown/Parsing/ParsedMatch.cs b/DiscordChatExporter.Core/Markdown/Parsing/ParsedMatch.cs index a7471f19..24758a45 100644 --- a/DiscordChatExporter.Core/Markdown/Parsing/ParsedMatch.cs +++ b/DiscordChatExporter.Core/Markdown/Parsing/ParsedMatch.cs @@ -1,4 +1,4 @@ -namespace DiscordChatExporter.Core.Markdown.Parsing; +namespace DiscordChatExporter.Core.Markdown.Parsing; internal class ParsedMatch(StringSegment segment, T value) { diff --git a/DiscordChatExporter.Core/Markdown/Parsing/RegexMatcher.cs b/DiscordChatExporter.Core/Markdown/Parsing/RegexMatcher.cs index 1fa6288b..1914fd76 100644 --- a/DiscordChatExporter.Core/Markdown/Parsing/RegexMatcher.cs +++ b/DiscordChatExporter.Core/Markdown/Parsing/RegexMatcher.cs @@ -1,11 +1,14 @@ -using System; +using System; using System.Text.RegularExpressions; namespace DiscordChatExporter.Core.Markdown.Parsing; -internal class RegexMatcher(Regex regex, Func transform) : IMatcher +internal class RegexMatcher( + Regex regex, + Func transform +) : IMatcher { - public ParsedMatch? TryMatch(StringSegment segment) + public ParsedMatch? TryMatch(TContext context, StringSegment segment) { var match = regex.Match(segment.Source, segment.StartIndex, segment.Length); if (!match.Success) @@ -20,8 +23,8 @@ internal class RegexMatcher(Regex regex, Func trans return null; var segmentMatch = segment.Relocate(match); - var value = transform(segmentMatch, match); + var value = transform(context, segmentMatch, match); - return value is not null ? new ParsedMatch(segmentMatch, value) : null; + return value is not null ? new ParsedMatch(segmentMatch, value) : null; } } diff --git a/DiscordChatExporter.Core/Markdown/Parsing/StringMatcher.cs b/DiscordChatExporter.Core/Markdown/Parsing/StringMatcher.cs index 881046c9..671bff26 100644 --- a/DiscordChatExporter.Core/Markdown/Parsing/StringMatcher.cs +++ b/DiscordChatExporter.Core/Markdown/Parsing/StringMatcher.cs @@ -1,17 +1,17 @@ -using System; +using System; namespace DiscordChatExporter.Core.Markdown.Parsing; -internal class StringMatcher( +internal class StringMatcher( string needle, StringComparison comparison, - Func transform -) : IMatcher + Func transform +) : IMatcher { - public StringMatcher(string needle, Func transform) + public StringMatcher(string needle, Func transform) : this(needle, StringComparison.Ordinal, transform) { } - public ParsedMatch? TryMatch(StringSegment segment) + public ParsedMatch? TryMatch(TContext context, StringSegment segment) { var index = segment.Source.IndexOf(needle, segment.StartIndex, segment.Length, comparison); @@ -19,8 +19,8 @@ internal class StringMatcher( return null; var segmentMatch = segment.Relocate(index, needle.Length); - var value = transform(segmentMatch); + var value = transform(context, segmentMatch); - return value is not null ? new ParsedMatch(segmentMatch, value) : null; + return value is not null ? new ParsedMatch(segmentMatch, value) : null; } } diff --git a/DiscordChatExporter.Core/Markdown/Parsing/StringSegment.cs b/DiscordChatExporter.Core/Markdown/Parsing/StringSegment.cs index bc35e941..e284325c 100644 --- a/DiscordChatExporter.Core/Markdown/Parsing/StringSegment.cs +++ b/DiscordChatExporter.Core/Markdown/Parsing/StringSegment.cs @@ -1,4 +1,4 @@ -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; namespace DiscordChatExporter.Core.Markdown.Parsing; @@ -14,5 +14,5 @@ internal readonly record struct StringSegment(string Source, int StartIndex, int public StringSegment Relocate(Capture capture) => Relocate(capture.Index, capture.Length); - public override string ToString() => Source.Substring(StartIndex, Length); + public override string ToString() => Source[StartIndex..EndIndex]; } diff --git a/DiscordChatExporter.Core/Markdown/TextNode.cs b/DiscordChatExporter.Core/Markdown/TextNode.cs index c9f5d837..fe46e457 100644 --- a/DiscordChatExporter.Core/Markdown/TextNode.cs +++ b/DiscordChatExporter.Core/Markdown/TextNode.cs @@ -1,3 +1,3 @@ -namespace DiscordChatExporter.Core.Markdown; +namespace DiscordChatExporter.Core.Markdown; internal record TextNode(string Text) : MarkdownNode; diff --git a/DiscordChatExporter.Core/Markdown/TimestampNode.cs b/DiscordChatExporter.Core/Markdown/TimestampNode.cs index 594c8d9a..eb144f2d 100644 --- a/DiscordChatExporter.Core/Markdown/TimestampNode.cs +++ b/DiscordChatExporter.Core/Markdown/TimestampNode.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace DiscordChatExporter.Core.Markdown; diff --git a/DiscordChatExporter.Core/Utils/Docker.cs b/DiscordChatExporter.Core/Utils/Docker.cs index 36c71547..0f3ab720 100644 --- a/DiscordChatExporter.Core/Utils/Docker.cs +++ b/DiscordChatExporter.Core/Utils/Docker.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace DiscordChatExporter.Core.Utils; diff --git a/DiscordChatExporter.Core/Utils/Extensions/AsyncCollectionExtensions.cs b/DiscordChatExporter.Core/Utils/Extensions/AsyncCollectionExtensions.cs deleted file mode 100644 index 1016a973..00000000 --- a/DiscordChatExporter.Core/Utils/Extensions/AsyncCollectionExtensions.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -namespace DiscordChatExporter.Core.Utils.Extensions; - -public static class AsyncCollectionExtensions -{ - private static async ValueTask> CollectAsync( - this IAsyncEnumerable asyncEnumerable - ) - { - var list = new List(); - - await foreach (var i in asyncEnumerable) - list.Add(i); - - return list; - } - - public static ValueTaskAwaiter> GetAwaiter( - this IAsyncEnumerable asyncEnumerable - ) => asyncEnumerable.CollectAsync().GetAwaiter(); -} diff --git a/DiscordChatExporter.Core/Utils/Extensions/BinaryExtensions.cs b/DiscordChatExporter.Core/Utils/Extensions/BinaryExtensions.cs deleted file mode 100644 index a585f258..00000000 --- a/DiscordChatExporter.Core/Utils/Extensions/BinaryExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Globalization; -using System.Text; - -namespace DiscordChatExporter.Core.Utils.Extensions; - -public static class BinaryExtensions -{ - public static string ToHex(this byte[] data, bool isUpperCase = true) - { - var buffer = new StringBuilder(2 * data.Length); - - foreach (var b in data) - { - buffer.Append(b.ToString(isUpperCase ? "X2" : "x2", CultureInfo.InvariantCulture)); - } - - return buffer.ToString(); - } -} diff --git a/DiscordChatExporter.Core/Utils/Extensions/CollectionExtensions.cs b/DiscordChatExporter.Core/Utils/Extensions/CollectionExtensions.cs deleted file mode 100644 index b999ed86..00000000 --- a/DiscordChatExporter.Core/Utils/Extensions/CollectionExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections.Generic; - -namespace DiscordChatExporter.Core.Utils.Extensions; - -public static class CollectionExtensions -{ - public static IEnumerable ToSingletonEnumerable(this T obj) - { - yield return obj; - } - - public static IEnumerable<(T value, int index)> WithIndex(this IEnumerable source) - { - var i = 0; - foreach (var o in source) - yield return (o, i++); - } - - public static IEnumerable WhereNotNull(this IEnumerable source) - where T : class - { - foreach (var o in source) - { - if (o is not null) - yield return o; - } - } -} diff --git a/DiscordChatExporter.Core/Utils/Extensions/ColorExtensions.cs b/DiscordChatExporter.Core/Utils/Extensions/ColorExtensions.cs deleted file mode 100644 index 91b7dd02..00000000 --- a/DiscordChatExporter.Core/Utils/Extensions/ColorExtensions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Drawing; - -namespace DiscordChatExporter.Core.Utils.Extensions; - -public static class ColorExtensions -{ - public static Color WithAlpha(this Color color, int alpha) => Color.FromArgb(alpha, color); - - public static Color ResetAlpha(this Color color) => color.WithAlpha(255); - - public static int ToRgb(this Color color) => color.ToArgb() & 0xffffff; - - public static string ToHex(this Color color) => $"#{color.R:X2}{color.G:X2}{color.B:X2}"; -} diff --git a/DiscordChatExporter.Core/Utils/Extensions/ExceptionExtensions.cs b/DiscordChatExporter.Core/Utils/Extensions/ExceptionExtensions.cs deleted file mode 100644 index 8c7f3c40..00000000 --- a/DiscordChatExporter.Core/Utils/Extensions/ExceptionExtensions.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace DiscordChatExporter.Core.Utils.Extensions; - -public static class ExceptionExtensions -{ - private static void PopulateChildren(this Exception exception, ICollection children) - { - if (exception is AggregateException aggregateException) - { - foreach (var innerException in aggregateException.InnerExceptions) - { - children.Add(innerException); - PopulateChildren(innerException, children); - } - } - else if (exception.InnerException is not null) - { - children.Add(exception.InnerException); - PopulateChildren(exception.InnerException, children); - } - } - - public static IReadOnlyList GetSelfAndChildren(this Exception exception) - { - var children = new List { exception }; - PopulateChildren(exception, children); - return children; - } -} diff --git a/DiscordChatExporter.Core/Utils/Extensions/GenericExtensions.cs b/DiscordChatExporter.Core/Utils/Extensions/GenericExtensions.cs deleted file mode 100644 index c91b720a..00000000 --- a/DiscordChatExporter.Core/Utils/Extensions/GenericExtensions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace DiscordChatExporter.Core.Utils.Extensions; - -public static class GenericExtensions -{ - public static TOut Pipe(this TIn input, Func transform) => - transform(input); - - public static T? NullIf(this T value, Func predicate) - where T : struct => !predicate(value) ? value : null; - - public static T? NullIfDefault(this T value) - where T : struct => value.NullIf(v => EqualityComparer.Default.Equals(v, default)); -} diff --git a/DiscordChatExporter.Core/Utils/Extensions/HttpExtensions.cs b/DiscordChatExporter.Core/Utils/Extensions/HttpExtensions.cs deleted file mode 100644 index 06626eaf..00000000 --- a/DiscordChatExporter.Core/Utils/Extensions/HttpExtensions.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Net.Http.Headers; - -namespace DiscordChatExporter.Core.Utils.Extensions; - -public static class HttpExtensions -{ - public static string? TryGetValue(this HttpHeaders headers, string name) => - headers.TryGetValues(name, out var values) ? string.Concat(values) : null; -} diff --git a/DiscordChatExporter.Core/Utils/Extensions/StringExtensions.cs b/DiscordChatExporter.Core/Utils/Extensions/StringExtensions.cs deleted file mode 100644 index de035fbe..00000000 --- a/DiscordChatExporter.Core/Utils/Extensions/StringExtensions.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Text; - -namespace DiscordChatExporter.Core.Utils.Extensions; - -public static class StringExtensions -{ - public static string? NullIfWhiteSpace(this string str) => - !string.IsNullOrWhiteSpace(str) ? str : null; - - public static string Truncate(this string str, int charCount) => - str.Length > charCount ? str[..charCount] : str; - - public static string ToSpaceSeparatedWords(this string str) - { - var builder = new StringBuilder(str.Length * 2); - - foreach (var c in str) - { - if (char.IsUpper(c) && builder.Length > 0) - builder.Append(' '); - - builder.Append(c); - } - - return builder.ToString(); - } - - public static T? ParseEnumOrNull(this string str, bool ignoreCase = true) - where T : struct, Enum => Enum.TryParse(str, ignoreCase, out var result) ? result : null; - - public static StringBuilder AppendIfNotEmpty(this StringBuilder builder, char value) => - builder.Length > 0 ? builder.Append(value) : builder; -} diff --git a/DiscordChatExporter.Core/Utils/Extensions/SuperpowerExtensions.cs b/DiscordChatExporter.Core/Utils/Extensions/SuperpowerExtensions.cs index 8d933ecf..84747cdf 100644 --- a/DiscordChatExporter.Core/Utils/Extensions/SuperpowerExtensions.cs +++ b/DiscordChatExporter.Core/Utils/Extensions/SuperpowerExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics.CodeAnalysis; using Superpower; using Superpower.Parsers; @@ -7,21 +7,21 @@ namespace DiscordChatExporter.Core.Utils.Extensions; public static class SuperpowerExtensions { - public static TextParser Text(this TextParser parser) => - parser.Select(chars => new string(chars)); + extension(TextParser parser) + { + public TextParser Token() => + parser.Between(Character.WhiteSpace.IgnoreMany(), Character.WhiteSpace.IgnoreMany()); - public static TextParser Token(this TextParser parser) => - parser.Between(Character.WhiteSpace.IgnoreMany(), Character.WhiteSpace.IgnoreMany()); - - // Only used for debugging while writing Superpower parsers. - // From https://twitter.com/nblumhardt/status/1389349059786264578 - [ExcludeFromCodeCoverage] - public static TextParser Log(this TextParser parser, string description) => - i => - { - Console.WriteLine($"Trying {description} ->"); - var r = parser(i); - Console.WriteLine($"Result was {r}"); - return r; - }; + // Only used for debugging while writing Superpower parsers. + // From https://twitter.com/nblumhardt/status/1389349059786264578 + [ExcludeFromCodeCoverage] + public TextParser Log(string description) => + i => + { + Console.WriteLine($"Trying {description} ->"); + var r = parser(i); + Console.WriteLine($"Result was {r}"); + return r; + }; + } } diff --git a/DiscordChatExporter.Core/Utils/Extensions/TimeSpanExtensions.cs b/DiscordChatExporter.Core/Utils/Extensions/TimeSpanExtensions.cs deleted file mode 100644 index 11a0192a..00000000 --- a/DiscordChatExporter.Core/Utils/Extensions/TimeSpanExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace DiscordChatExporter.Core.Utils.Extensions; - -public static class TimeSpanExtensions -{ - public static TimeSpan Clamp(this TimeSpan value, TimeSpan min, TimeSpan max) - { - if (value < min) - return min; - - if (value > max) - return max; - - return value; - } -} diff --git a/DiscordChatExporter.Core/Utils/Http.cs b/DiscordChatExporter.Core/Utils/Http.cs index f08984f8..1b053c02 100644 --- a/DiscordChatExporter.Core/Utils/Http.cs +++ b/DiscordChatExporter.Core/Utils/Http.cs @@ -1,13 +1,13 @@ -using System; +using System; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Sockets; using System.Security.Authentication; using System.Threading.Tasks; -using DiscordChatExporter.Core.Utils.Extensions; using Polly; using Polly.Retry; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Utils; @@ -24,12 +24,11 @@ public static class Http private static bool IsRetryableException(Exception exception) => exception - .GetSelfAndChildren() - .Any( - ex => - ex is TimeoutException or SocketException or AuthenticationException - || ex is HttpRequestException hrex - && IsRetryableStatusCode(hrex.StatusCode ?? HttpStatusCode.OK) + .GetSelfAndDescendants() + .Any(ex => + ex is TimeoutException or SocketException or AuthenticationException + || ex is HttpRequestException hrex + && IsRetryableStatusCode(hrex.StatusCode ?? HttpStatusCode.OK) ); public static ResiliencePipeline ResiliencePipeline { get; } = @@ -40,7 +39,7 @@ public static class Http ShouldHandle = new PredicateBuilder().Handle(IsRetryableException), MaxRetryAttempts = 4, BackoffType = DelayBackoffType.Exponential, - Delay = TimeSpan.FromSeconds(1) + Delay = TimeSpan.FromSeconds(1), } ) .Build(); @@ -69,7 +68,7 @@ public static class Http return ValueTask.FromResult( TimeSpan.FromSeconds(Math.Pow(2, args.AttemptNumber) + 1) ); - } + }, } ) .Build(); diff --git a/DiscordChatExporter.Core/Utils/PathEx.cs b/DiscordChatExporter.Core/Utils/PathEx.cs deleted file mode 100644 index c85b54aa..00000000 --- a/DiscordChatExporter.Core/Utils/PathEx.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Runtime.InteropServices; -using System.Text; - -namespace DiscordChatExporter.Core.Utils; - -public static class PathEx -{ - private static readonly HashSet InvalidFileNameChars = - new(Path.GetInvalidFileNameChars()); - - public static string EscapeFileName(string path) - { - var buffer = new StringBuilder(path.Length); - - foreach (var c in path) - buffer.Append(!InvalidFileNameChars.Contains(c) ? c : '_'); - - // File names cannot end with a dot on Windows - // https://github.com/Tyrrrz/DiscordChatExporter/issues/977 - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - while (buffer.Length > 0 && buffer[^1] == '.') - buffer.Remove(buffer.Length - 1, 1); - } - - return buffer.ToString(); - } -} diff --git a/DiscordChatExporter.Core/Utils/Url.cs b/DiscordChatExporter.Core/Utils/Url.cs new file mode 100644 index 00000000..91c96fb7 --- /dev/null +++ b/DiscordChatExporter.Core/Utils/Url.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; +using System.Text; + +namespace DiscordChatExporter.Core.Utils; + +public static class Url +{ + public static string EncodeFilePath(string filePath) + { + var buffer = new StringBuilder(); + var position = 0; + + // For absolute paths, prepend file:// protocol for proper browser handling + if (Path.IsPathFullyQualified(filePath)) + { + buffer.Append("file://"); + + // On Windows, we need to add an extra slash before the drive letter + // e.g., file:///C:/path instead of file://C:/path + if (!filePath.StartsWith('/') && !filePath.StartsWith('\\')) + { + buffer.Append('/'); + } + } + + while (true) + { + if (position >= filePath.Length) + break; + + var separatorIndex = filePath.IndexOfAny([':', '/', '\\'], position); + if (separatorIndex < 0) + { + buffer.Append(Uri.EscapeDataString(filePath[position..])); + break; + } + + // Append the segment + buffer.Append(Uri.EscapeDataString(filePath[position..separatorIndex])); + + // Append the separator + buffer.Append( + filePath[separatorIndex] switch + { + // Normalize slashes + '\\' => '/', + var c => c, + } + ); + + position = separatorIndex + 1; + } + + return buffer.ToString(); + } +} diff --git a/DiscordChatExporter.Core/Utils/UrlBuilder.cs b/DiscordChatExporter.Core/Utils/UrlBuilder.cs index d2985ba2..6a263769 100644 --- a/DiscordChatExporter.Core/Utils/UrlBuilder.cs +++ b/DiscordChatExporter.Core/Utils/UrlBuilder.cs @@ -1,8 +1,8 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; -using System.Net; using System.Text; +using PowerKit.Extensions; namespace DiscordChatExporter.Core.Utils; @@ -10,8 +10,9 @@ public class UrlBuilder { private string _path = ""; - private readonly Dictionary _queryParameters = - new(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary _queryParameters = new( + StringComparer.OrdinalIgnoreCase + ); public UrlBuilder SetPath(string path) { @@ -24,8 +25,8 @@ public class UrlBuilder if (ignoreUnsetValue && string.IsNullOrWhiteSpace(value)) return this; - var keyEncoded = WebUtility.UrlEncode(key); - var valueEncoded = WebUtility.UrlEncode(value); + var keyEncoded = Uri.EscapeDataString(key); + var valueEncoded = value?.Pipe(Uri.EscapeDataString); _queryParameters[keyEncoded] = valueEncoded; return this; @@ -38,9 +39,11 @@ public class UrlBuilder buffer.Append(_path); if (_queryParameters.Any()) + { buffer .Append('?') .AppendJoin('&', _queryParameters.Select(kvp => $"{kvp.Key}={kvp.Value}")); + } return buffer.ToString(); } diff --git a/DiscordChatExporter.Gui/App.axaml b/DiscordChatExporter.Gui/App.axaml new file mode 100644 index 00000000..fc1c25bc --- /dev/null +++ b/DiscordChatExporter.Gui/App.axaml @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DiscordChatExporter.Gui/App.axaml.cs b/DiscordChatExporter.Gui/App.axaml.cs new file mode 100644 index 00000000..37179c5c --- /dev/null +++ b/DiscordChatExporter.Gui/App.axaml.cs @@ -0,0 +1,147 @@ +using System; +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using Avalonia.Media; +using Avalonia.Platform; +using DiscordChatExporter.Gui.Framework; +using DiscordChatExporter.Gui.Localization; +using DiscordChatExporter.Gui.Services; +using DiscordChatExporter.Gui.Utils.Extensions; +using DiscordChatExporter.Gui.ViewModels; +using DiscordChatExporter.Gui.ViewModels.Components; +using DiscordChatExporter.Gui.ViewModels.Dialogs; +using Material.Styles.Themes; +using Microsoft.Extensions.DependencyInjection; +using PowerKit; +using PowerKit.Extensions; + +namespace DiscordChatExporter.Gui; + +public partial class App : Application, IDisposable +{ + private readonly ServiceProvider _services; + private readonly SettingsService _settingsService; + + private readonly IDisposable _eventSubscription; + + private bool _isDisposed; + + public App() + { + var services = new ServiceCollection(); + + // Framework + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + // Services + services.AddSingleton(); + services.AddSingleton(); + + // Localization + services.AddSingleton(); + + // View models + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + + _services = services.BuildServiceProvider(true); + _settingsService = _services.GetRequiredService(); + + // Re-initialize the theme when the user changes it + _eventSubscription = Disposable.Merge( + _settingsService.WatchProperty( + o => o.Theme, + v => + { + RequestedThemeVariant = v switch + { + ThemeVariant.Light => Avalonia.Styling.ThemeVariant.Light, + ThemeVariant.Dark => Avalonia.Styling.ThemeVariant.Dark, + _ => Avalonia.Styling.ThemeVariant.Default, + }; + + InitializeTheme(); + } + ) + ); + } + + private void InitializeTheme() + { + var actualTheme = RequestedThemeVariant?.Key switch + { + "Light" => PlatformThemeVariant.Light, + "Dark" => PlatformThemeVariant.Dark, + _ => PlatformSettings?.GetColorValues().ThemeVariant ?? PlatformThemeVariant.Light, + }; + + this.LocateMaterialTheme().CurrentTheme = + actualTheme == PlatformThemeVariant.Light + ? Theme.Create(Theme.Light, Color.Parse("#343838"), Color.Parse("#F9A825")) + : Theme.Create(Theme.Dark, Color.Parse("#E8E8E8"), Color.Parse("#F9A825")); + } + + public override void Initialize() + { + base.Initialize(); + + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + // Load settings + _settingsService.Load(); + + // Initialize and configure the main window + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + var viewManager = _services.GetRequiredService(); + var viewModelManager = _services.GetRequiredService(); + + desktop.MainWindow = viewManager.TryBindWindow(viewModelManager.GetMainViewModel()); + + // Although `App.Dispose()` is invoked from `Program.Main(...)`, on some platforms + // it may be called too late in the shutdown lifecycle. Attach an exit + // handler to ensure timely disposal as a safeguard. + // https://github.com/Tyrrrz/YoutubeDownloader/issues/795 + desktop.Exit += (_, _) => Dispose(); + } + + // Initialize the theme for the first time; must be done after the main window is created + InitializeTheme(); + + base.OnFrameworkInitializationCompleted(); + } + + private void Application_OnActualThemeVariantChanged(object? sender, EventArgs args) => + // Re-initialize the theme when the system theme changes + InitializeTheme(); + + public void Dispose() + { + if (_isDisposed) + return; + + _isDisposed = true; + + _eventSubscription.Dispose(); + _services.Dispose(); + } +} + +public partial class App +{ + public static void Shutdown(int exitCode = 0) + { + if (Current?.ApplicationLifetime?.TryShutdown(exitCode) != true) + Environment.Exit(exitCode); + } +} diff --git a/DiscordChatExporter.Gui/App.xaml b/DiscordChatExporter.Gui/App.xaml deleted file mode 100644 index e90a123d..00000000 --- a/DiscordChatExporter.Gui/App.xaml +++ /dev/null @@ -1,543 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/DiscordChatExporter.Gui/App.xaml.cs b/DiscordChatExporter.Gui/App.xaml.cs deleted file mode 100644 index 35c890cf..00000000 --- a/DiscordChatExporter.Gui/App.xaml.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Reflection; -using DiscordChatExporter.Gui.Utils; -using MaterialDesignThemes.Wpf; - -namespace DiscordChatExporter.Gui; - -public partial class App -{ - private static Assembly Assembly { get; } = typeof(App).Assembly; - - public static string Name { get; } = Assembly.GetName().Name!; - - public static Version Version { get; } = Assembly.GetName().Version!; - - public static string VersionString { get; } = Version.ToString(3); - - public static string ProjectUrl { get; } = "https://github.com/Tyrrrz/DiscordChatExporter"; - - public static string LatestReleaseUrl { get; } = ProjectUrl + "/releases/latest"; - - public static string DocumentationUrl { get; } = ProjectUrl + "/tree/master/.docs"; -} - -public partial class App -{ - private static Theme LightTheme { get; } = - Theme.Create( - new MaterialDesignLightTheme(), - MediaColor.FromHex("#343838"), - MediaColor.FromHex("#F9A825") - ); - - private static Theme DarkTheme { get; } = - Theme.Create( - new MaterialDesignDarkTheme(), - MediaColor.FromHex("#E8E8E8"), - MediaColor.FromHex("#F9A825") - ); - - public static void SetLightTheme() - { - var paletteHelper = new PaletteHelper(); - paletteHelper.SetTheme(LightTheme); - } - - public static void SetDarkTheme() - { - var paletteHelper = new PaletteHelper(); - paletteHelper.SetTheme(DarkTheme); - } -} diff --git a/DiscordChatExporter.Gui/Behaviors/ChannelMultiSelectionListBoxBehavior.cs b/DiscordChatExporter.Gui/Behaviors/ChannelMultiSelectionListBoxBehavior.cs deleted file mode 100644 index 66071f53..00000000 --- a/DiscordChatExporter.Gui/Behaviors/ChannelMultiSelectionListBoxBehavior.cs +++ /dev/null @@ -1,5 +0,0 @@ -using DiscordChatExporter.Core.Discord.Data; - -namespace DiscordChatExporter.Gui.Behaviors; - -public class ChannelMultiSelectionListBoxBehavior : MultiSelectionListBoxBehavior; diff --git a/DiscordChatExporter.Gui/Behaviors/MultiSelectionListBoxBehavior.cs b/DiscordChatExporter.Gui/Behaviors/MultiSelectionListBoxBehavior.cs deleted file mode 100644 index 8e8242ec..00000000 --- a/DiscordChatExporter.Gui/Behaviors/MultiSelectionListBoxBehavior.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System.Collections; -using System.Collections.Specialized; -using System.Linq; -using System.Windows; -using System.Windows.Controls; -using Microsoft.Xaml.Behaviors; - -namespace DiscordChatExporter.Gui.Behaviors; - -public class MultiSelectionListBoxBehavior : Behavior -{ - public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register( - nameof(SelectedItems), - typeof(IList), - typeof(MultiSelectionListBoxBehavior), - new FrameworkPropertyMetadata( - null, - FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, - OnSelectedItemsChanged - ) - ); - - private static void OnSelectedItemsChanged( - DependencyObject sender, - DependencyPropertyChangedEventArgs args - ) - { - var behavior = (MultiSelectionListBoxBehavior)sender; - if (behavior._modelHandled) - return; - - if (behavior.AssociatedObject is null) - return; - - behavior._modelHandled = true; - behavior.SelectItems(); - behavior._modelHandled = false; - } - - private bool _viewHandled; - private bool _modelHandled; - - public IList? SelectedItems - { - get => (IList?)GetValue(SelectedItemsProperty); - set => SetValue(SelectedItemsProperty, value); - } - - // Propagate selected items from the model to the view - private void SelectItems() - { - _viewHandled = true; - - AssociatedObject.SelectedItems.Clear(); - if (SelectedItems is not null) - { - foreach (var item in SelectedItems) - AssociatedObject.SelectedItems.Add(item); - } - - _viewHandled = false; - } - - // Propagate selected items from the view to the model - private void OnListBoxSelectionChanged(object? sender, SelectionChangedEventArgs args) - { - if (_viewHandled) - return; - if (AssociatedObject.Items.SourceCollection is null) - return; - - SelectedItems = AssociatedObject.SelectedItems.Cast().ToArray(); - } - - private void OnListBoxItemsChanged(object? sender, NotifyCollectionChangedEventArgs args) - { - if (_viewHandled) - return; - if (AssociatedObject.Items.SourceCollection is null) - return; - SelectItems(); - } - - protected override void OnAttached() - { - base.OnAttached(); - - AssociatedObject.SelectionChanged += OnListBoxSelectionChanged; - ((INotifyCollectionChanged)AssociatedObject.Items).CollectionChanged += - OnListBoxItemsChanged; - } - - protected override void OnDetaching() - { - base.OnDetaching(); - - if (AssociatedObject is not null) - { - AssociatedObject.SelectionChanged -= OnListBoxSelectionChanged; - ((INotifyCollectionChanged)AssociatedObject.Items).CollectionChanged -= - OnListBoxItemsChanged; - } - } -} diff --git a/DiscordChatExporter.Gui/Bootstrapper.cs b/DiscordChatExporter.Gui/Bootstrapper.cs deleted file mode 100644 index 1be1e8b1..00000000 --- a/DiscordChatExporter.Gui/Bootstrapper.cs +++ /dev/null @@ -1,45 +0,0 @@ -using DiscordChatExporter.Gui.Services; -using DiscordChatExporter.Gui.ViewModels; -using DiscordChatExporter.Gui.ViewModels.Framework; -using Stylet; -using StyletIoC; -#if !DEBUG -using System.Windows; -using System.Windows.Threading; -#endif - -namespace DiscordChatExporter.Gui; - -public class Bootstrapper : Bootstrapper -{ - protected override void OnStart() - { - base.OnStart(); - - // Set the default theme. - // Preferred theme will be set later, once the settings are loaded. - App.SetLightTheme(); - } - - protected override void ConfigureIoC(IStyletIoCBuilder builder) - { - base.ConfigureIoC(builder); - - builder.Bind().ToSelf().InSingletonScope(); - builder.Bind().ToAbstractFactory(); - } - -#if !DEBUG - protected override void OnUnhandledException(DispatcherUnhandledExceptionEventArgs args) - { - base.OnUnhandledException(args); - - MessageBox.Show( - args.Exception.ToString(), - "Error occured", - MessageBoxButton.OK, - MessageBoxImage.Error - ); - } -#endif -} diff --git a/DiscordChatExporter.Gui/Converters/ChannelToGroupKeyConverter.cs b/DiscordChatExporter.Gui/Converters/ChannelToGroupKeyConverter.cs deleted file mode 100644 index 37c91ba7..00000000 --- a/DiscordChatExporter.Gui/Converters/ChannelToGroupKeyConverter.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; -using DiscordChatExporter.Core.Discord.Data; - -namespace DiscordChatExporter.Gui.Converters; - -[ValueConversion(typeof(Channel), typeof(string))] -public class ChannelToGroupKeyConverter : IValueConverter -{ - public static ChannelToGroupKeyConverter Instance { get; } = new(); - - public object? Convert( - object? value, - Type targetType, - object? parameter, - CultureInfo culture - ) => - value switch - { - Channel { IsThread: true, Parent: not null } channel - => $"Threads in #{channel.Parent.Name}", - - Channel channel => channel.Parent?.Name ?? "???", - - _ => null - }; - - public object ConvertBack( - object? value, - Type targetType, - object? parameter, - CultureInfo culture - ) => throw new NotSupportedException(); -} diff --git a/DiscordChatExporter.Gui/Converters/ChannelToHierarchicalNameStringConverter.cs b/DiscordChatExporter.Gui/Converters/ChannelToHierarchicalNameStringConverter.cs new file mode 100644 index 00000000..fadd6905 --- /dev/null +++ b/DiscordChatExporter.Gui/Converters/ChannelToHierarchicalNameStringConverter.cs @@ -0,0 +1,25 @@ +using System; +using System.Globalization; +using Avalonia.Data.Converters; +using DiscordChatExporter.Core.Discord.Data; + +namespace DiscordChatExporter.Gui.Converters; + +public class ChannelToHierarchicalNameStringConverter : IValueConverter +{ + public static ChannelToHierarchicalNameStringConverter Instance { get; } = new(); + + public object? Convert( + object? value, + Type targetType, + object? parameter, + CultureInfo culture + ) => value is Channel channel ? channel.GetHierarchicalName() : null; + + public object ConvertBack( + object? value, + Type targetType, + object? parameter, + CultureInfo culture + ) => throw new NotSupportedException(); +} diff --git a/DiscordChatExporter.Gui/Converters/DateTimeOffsetToDateTimeConverter.cs b/DiscordChatExporter.Gui/Converters/DateTimeOffsetToDateTimeConverter.cs deleted file mode 100644 index 4511336e..00000000 --- a/DiscordChatExporter.Gui/Converters/DateTimeOffsetToDateTimeConverter.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; - -namespace DiscordChatExporter.Gui.Converters; - -[ValueConversion(typeof(DateTimeOffset?), typeof(DateTime?))] -public class DateTimeOffsetToDateTimeConverter : IValueConverter -{ - public static DateTimeOffsetToDateTimeConverter Instance { get; } = new(); - - public object? Convert( - object? value, - Type targetType, - object? parameter, - CultureInfo culture - ) => - value is DateTimeOffset dateTimeOffsetValue - ? dateTimeOffsetValue.DateTime - : default(DateTime?); - - public object? ConvertBack( - object? value, - Type targetType, - object? parameter, - CultureInfo culture - ) => - value is DateTime dateTimeValue - ? new DateTimeOffset(dateTimeValue) - : default(DateTimeOffset?); -} diff --git a/DiscordChatExporter.Gui/Converters/ExportFormatToStringConverter.cs b/DiscordChatExporter.Gui/Converters/ExportFormatToStringConverter.cs index fe8a1322..bea826e5 100644 --- a/DiscordChatExporter.Gui/Converters/ExportFormatToStringConverter.cs +++ b/DiscordChatExporter.Gui/Converters/ExportFormatToStringConverter.cs @@ -1,11 +1,10 @@ -using System; +using System; using System.Globalization; -using System.Windows.Data; +using Avalonia.Data.Converters; using DiscordChatExporter.Core.Exporting; namespace DiscordChatExporter.Gui.Converters; -[ValueConversion(typeof(ExportFormat), typeof(string))] public class ExportFormatToStringConverter : IValueConverter { public static ExportFormatToStringConverter Instance { get; } = new(); @@ -15,7 +14,7 @@ public class ExportFormatToStringConverter : IValueConverter Type targetType, object? parameter, CultureInfo culture - ) => value is ExportFormat exportFormatValue ? exportFormatValue.GetDisplayName() : default; + ) => value is ExportFormat format ? format.GetDisplayName() : default; public object ConvertBack( object? value, diff --git a/DiscordChatExporter.Gui/Converters/InverseBoolConverter.cs b/DiscordChatExporter.Gui/Converters/InverseBoolConverter.cs deleted file mode 100644 index d6a8ad04..00000000 --- a/DiscordChatExporter.Gui/Converters/InverseBoolConverter.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; - -namespace DiscordChatExporter.Gui.Converters; - -[ValueConversion(typeof(bool), typeof(bool))] -public class InverseBoolConverter : IValueConverter -{ - public static InverseBoolConverter Instance { get; } = new(); - - public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) => - value is false; - - public object ConvertBack( - object? value, - Type targetType, - object? parameter, - CultureInfo culture - ) => value is false; -} diff --git a/DiscordChatExporter.Gui/Converters/LocaleToDisplayNameConverter.cs b/DiscordChatExporter.Gui/Converters/LocaleToDisplayNameStringConverter.cs similarity index 65% rename from DiscordChatExporter.Gui/Converters/LocaleToDisplayNameConverter.cs rename to DiscordChatExporter.Gui/Converters/LocaleToDisplayNameStringConverter.cs index f7cff19c..de0ce774 100644 --- a/DiscordChatExporter.Gui/Converters/LocaleToDisplayNameConverter.cs +++ b/DiscordChatExporter.Gui/Converters/LocaleToDisplayNameStringConverter.cs @@ -1,18 +1,17 @@ -using System; +using System; using System.Globalization; -using System.Windows.Data; +using Avalonia.Data.Converters; namespace DiscordChatExporter.Gui.Converters; -[ValueConversion(typeof(string), typeof(string))] -public class LocaleToDisplayNameConverter : IValueConverter +public class LocaleToDisplayNameStringConverter : IValueConverter { - public static LocaleToDisplayNameConverter Instance { get; } = new(); + public static LocaleToDisplayNameStringConverter Instance { get; } = new(); public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) => value is string locale && !string.IsNullOrWhiteSpace(locale) ? CultureInfo.GetCultureInfo(locale).DisplayName - : "System default"; + : "System"; public object ConvertBack( object? value, diff --git a/DiscordChatExporter.Gui/Converters/MarkdownToInlinesConverter.cs b/DiscordChatExporter.Gui/Converters/MarkdownToInlinesConverter.cs new file mode 100644 index 00000000..8fdcc29e --- /dev/null +++ b/DiscordChatExporter.Gui/Converters/MarkdownToInlinesConverter.cs @@ -0,0 +1,168 @@ +using System; +using System.Globalization; +using System.Linq; +using Avalonia.Controls.Documents; +using Avalonia.Data.Converters; +using Avalonia.Media; +using DiscordChatExporter.Gui.Utils.Extensions; +using DiscordChatExporter.Gui.Views.Controls; +using Markdig; +using Markdig.Syntax; +using Markdig.Syntax.Inlines; +using MarkdownInline = Markdig.Syntax.Inlines.Inline; + +namespace DiscordChatExporter.Gui.Converters; + +public class MarkdownToInlinesConverter : IValueConverter +{ + public static readonly MarkdownToInlinesConverter Instance = new(); + + private static readonly MarkdownPipeline MarkdownPipeline = new MarkdownPipelineBuilder() + .UseEmphasisExtras() + .Build(); + + private static void ProcessInline( + InlineCollection inlines, + MarkdownInline markdownInline, + FontWeight? fontWeight = null, + FontStyle? fontStyle = null, + TextDecorationCollection? textDecorations = null + ) + { + switch (markdownInline) + { + case LiteralInline literal: + { + var run = new Run(literal.Content.ToString()); + + if (fontWeight is not null) + run.FontWeight = fontWeight.Value; + if (fontStyle is not null) + run.FontStyle = fontStyle.Value; + if (textDecorations is not null) + run.TextDecorations = textDecorations; + + inlines.Add(run); + break; + } + + case LineBreakInline: + { + inlines.Add(new LineBreak()); + break; + } + + case EmphasisInline emphasis: + { + var newWeight = fontWeight; + var newStyle = fontStyle; + var newDecorations = textDecorations; + + switch (emphasis.DelimiterChar) + { + case '*' or '_' when emphasis.DelimiterCount == 2: + newWeight = FontWeight.SemiBold; + break; + case '*' or '_': + newStyle = FontStyle.Italic; + break; + case '~': + newDecorations = TextDecorations.Strikethrough; + break; + case '+': + newDecorations = TextDecorations.Underline; + break; + } + + foreach (var child in emphasis) + ProcessInline(inlines, child, newWeight, newStyle, newDecorations); + + break; + } + + case LinkInline link: + { + inlines.Add(new HyperLink { Text = link.GetInnerText(), Url = link.Url }); + break; + } + + case ContainerInline container: + { + foreach (var child in container) + ProcessInline(inlines, child, fontWeight, fontStyle, textDecorations); + + break; + } + } + } + + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + var inlines = new InlineCollection(); + if (value is not string { Length: > 0 } text) + return inlines; + + var isFirst = true; + + foreach (var block in Markdown.Parse(text, MarkdownPipeline)) + { + switch (block) + { + case ParagraphBlock { Inline: not null } paragraph: + { + if (!isFirst) + { + // Insert a blank line between paragraphs + inlines.Add(new LineBreak()); + inlines.Add(new LineBreak()); + } + + isFirst = false; + + foreach (var markdownInline in paragraph.Inline!) + ProcessInline(inlines, markdownInline); + + break; + } + + case ListBlock list: + { + var itemOrder = 1; + if (list.IsOrdered && int.TryParse(list.OrderedStart, out var startNum)) + itemOrder = startNum; + + foreach (var listItem in list.OfType()) + { + if (!isFirst) + inlines.Add(new LineBreak()); + isFirst = false; + + var prefix = list.IsOrdered ? $"{itemOrder++}. " : $"{list.BulletType} "; + + inlines.Add(new Run(prefix)); + + foreach (var subBlock in listItem.OfType()) + { + if (subBlock is { Inline: not null }) + { + foreach (var markdownInline in subBlock.Inline) + ProcessInline(inlines, markdownInline); + } + } + } + + break; + } + } + } + + return inlines; + } + + public object? ConvertBack( + object? value, + Type targetType, + object? parameter, + CultureInfo culture + ) => throw new NotSupportedException(); +} diff --git a/DiscordChatExporter.Gui/Converters/RateLimitPreferenceToStringConverter.cs b/DiscordChatExporter.Gui/Converters/RateLimitPreferenceToStringConverter.cs new file mode 100644 index 00000000..49107a02 --- /dev/null +++ b/DiscordChatExporter.Gui/Converters/RateLimitPreferenceToStringConverter.cs @@ -0,0 +1,28 @@ +using System; +using System.Globalization; +using Avalonia.Data.Converters; +using DiscordChatExporter.Core.Discord; + +namespace DiscordChatExporter.Gui.Converters; + +public class RateLimitPreferenceToStringConverter : IValueConverter +{ + public static RateLimitPreferenceToStringConverter Instance { get; } = new(); + + public object? Convert( + object? value, + Type targetType, + object? parameter, + CultureInfo culture + ) => + value is RateLimitPreference rateLimitPreference + ? rateLimitPreference.GetDisplayName() + : default; + + public object ConvertBack( + object? value, + Type targetType, + object? parameter, + CultureInfo culture + ) => throw new NotSupportedException(); +} diff --git a/DiscordChatExporter.Gui/Converters/SnowflakeToDateTimeOffsetConverter.cs b/DiscordChatExporter.Gui/Converters/SnowflakeToTimestampStringConverter.cs similarity index 57% rename from DiscordChatExporter.Gui/Converters/SnowflakeToDateTimeOffsetConverter.cs rename to DiscordChatExporter.Gui/Converters/SnowflakeToTimestampStringConverter.cs index b4b0f015..475c1787 100644 --- a/DiscordChatExporter.Gui/Converters/SnowflakeToDateTimeOffsetConverter.cs +++ b/DiscordChatExporter.Gui/Converters/SnowflakeToTimestampStringConverter.cs @@ -1,21 +1,20 @@ -using System; +using System; using System.Globalization; -using System.Windows.Data; +using Avalonia.Data.Converters; using DiscordChatExporter.Core.Discord; namespace DiscordChatExporter.Gui.Converters; -[ValueConversion(typeof(Snowflake?), typeof(DateTimeOffset?))] -public class SnowflakeToDateTimeOffsetConverter : IValueConverter +public class SnowflakeToTimestampStringConverter : IValueConverter { - public static SnowflakeToDateTimeOffsetConverter Instance { get; } = new(); + public static SnowflakeToTimestampStringConverter Instance { get; } = new(); public object? Convert( object? value, Type targetType, object? parameter, CultureInfo culture - ) => value is Snowflake snowflake ? snowflake.ToDate() : null; + ) => value is Snowflake snowflake ? snowflake.ToDate().ToString("g", culture) : null; public object ConvertBack( object? value, diff --git a/DiscordChatExporter.Gui/Converters/TimeSpanToDateTimeConverter.cs b/DiscordChatExporter.Gui/Converters/TimeSpanToDateTimeConverter.cs deleted file mode 100644 index 658d7a5f..00000000 --- a/DiscordChatExporter.Gui/Converters/TimeSpanToDateTimeConverter.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; - -namespace DiscordChatExporter.Gui.Converters; - -[ValueConversion(typeof(TimeSpan?), typeof(DateTime?))] -public class TimeSpanToDateTimeConverter : IValueConverter -{ - public static TimeSpanToDateTimeConverter Instance { get; } = new(); - - public object? Convert( - object? value, - Type targetType, - object? parameter, - CultureInfo culture - ) => value is TimeSpan timeSpanValue ? DateTime.Today.Add(timeSpanValue) : default(DateTime?); - - public object? ConvertBack( - object? value, - Type targetType, - object? parameter, - CultureInfo culture - ) => value is DateTime dateTimeValue ? dateTimeValue.TimeOfDay : default(TimeSpan?); -} diff --git a/DiscordChatExporter.Gui/DiscordChatExporter.Gui.csproj b/DiscordChatExporter.Gui/DiscordChatExporter.Gui.csproj index fad90a59..11c78eaa 100644 --- a/DiscordChatExporter.Gui/DiscordChatExporter.Gui.csproj +++ b/DiscordChatExporter.Gui/DiscordChatExporter.Gui.csproj @@ -1,33 +1,56 @@ - - + WinExe - $(TargetFramework)-windows DiscordChatExporter - true ../favicon.ico + true + false + + + + HimalayanPinkSalt - + + + + + + false + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + - + - \ No newline at end of file + + + + diff --git a/DiscordChatExporter.Gui/FodyWeavers.xml b/DiscordChatExporter.Gui/FodyWeavers.xml deleted file mode 100644 index 4e68ed1a..00000000 --- a/DiscordChatExporter.Gui/FodyWeavers.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/DiscordChatExporter.Gui/FodyWeavers.xsd b/DiscordChatExporter.Gui/FodyWeavers.xsd deleted file mode 100644 index 69dbe488..00000000 --- a/DiscordChatExporter.Gui/FodyWeavers.xsd +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - Used to control if the On_PropertyName_Changed feature is enabled. - - - - - Used to control if the Dependent properties feature is enabled. - - - - - Used to control if the IsChanged property feature is enabled. - - - - - Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form. - - - - - Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project. - - - - - Used to control if equality checks should use the Equals method resolved from the base class. - - - - - Used to control if equality checks should use the static Equals method resolved from the base class. - - - - - Used to turn off build warnings from this weaver. - - - - - Used to turn off build warnings about mismatched On_PropertyName_Changed methods. - - - - - - - - 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. - - - - - A comma-separated list of error codes that can be safely ignored in assembly verification. - - - - - 'false' to turn off automatic generation of the XML Schema file. - - - - - \ No newline at end of file diff --git a/DiscordChatExporter.Gui/Framework/DialogManager.cs b/DiscordChatExporter.Gui/Framework/DialogManager.cs new file mode 100644 index 00000000..b3a1714e --- /dev/null +++ b/DiscordChatExporter.Gui/Framework/DialogManager.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Platform.Storage; +using DialogHostAvalonia; +using DiscordChatExporter.Gui.Utils.Extensions; + +namespace DiscordChatExporter.Gui.Framework; + +public class DialogManager : IDisposable +{ + private readonly SemaphoreSlim _dialogLock = new(1, 1); + + public async Task ShowDialogAsync(DialogViewModelBase dialog) + { + await _dialogLock.WaitAsync(); + try + { + await DialogHost.Show( + dialog, + // It's fine to await in a void method here because it's an event handler + // ReSharper disable once AsyncVoidLambda + async (object _, DialogOpenedEventArgs args) => + { + await dialog.WaitForCloseAsync(); + + try + { + args.Session.Close(); + } + catch (InvalidOperationException) + { + // Dialog host is already processing a close operation + } + } + ); + + // Yield to allow DialogHost to fully reset its state before + // another dialog is shown (e.g. when dialogs are shown sequentially). + await Task.Yield(); + + return dialog.DialogResult; + } + finally + { + _dialogLock.Release(); + } + } + + public async Task PromptSaveFilePathAsync( + IReadOnlyList? fileTypes = null, + string defaultFilePath = "" + ) + { + var topLevel = + Application.Current?.ApplicationLifetime?.TryGetTopLevel() + ?? throw new ApplicationException("Could not find the top-level visual element."); + + var file = await topLevel.StorageProvider.SaveFilePickerAsync( + new FilePickerSaveOptions + { + FileTypeChoices = fileTypes, + SuggestedFileName = defaultFilePath, + DefaultExtension = Path.GetExtension(defaultFilePath).TrimStart('.'), + } + ); + + return file?.TryGetLocalPath() ?? file?.Path.ToString(); + } + + public async Task PromptDirectoryPathAsync(string defaultDirPath = "") + { + var topLevel = + Application.Current?.ApplicationLifetime?.TryGetTopLevel() + ?? throw new ApplicationException("Could not find the top-level visual element."); + + var result = await topLevel.StorageProvider.OpenFolderPickerAsync( + new FolderPickerOpenOptions + { + AllowMultiple = false, + SuggestedStartLocation = await topLevel.StorageProvider.TryGetFolderFromPathAsync( + defaultDirPath + ), + } + ); + + var directory = result.FirstOrDefault(); + if (directory is null) + return null; + + return directory.TryGetLocalPath() ?? directory.Path.ToString(); + } + + public void Dispose() => _dialogLock.Dispose(); +} diff --git a/DiscordChatExporter.Gui/Framework/DialogViewModelBase.cs b/DiscordChatExporter.Gui/Framework/DialogViewModelBase.cs new file mode 100644 index 00000000..75b7317f --- /dev/null +++ b/DiscordChatExporter.Gui/Framework/DialogViewModelBase.cs @@ -0,0 +1,26 @@ +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; + +namespace DiscordChatExporter.Gui.Framework; + +public abstract partial class DialogViewModelBase : ViewModelBase +{ + private readonly TaskCompletionSource _closeTcs = new( + TaskCreationOptions.RunContinuationsAsynchronously + ); + + [ObservableProperty] + public partial T? DialogResult { get; set; } + + [RelayCommand] + protected void Close(T dialogResult) + { + DialogResult = dialogResult; + _closeTcs.TrySetResult(dialogResult); + } + + public async Task WaitForCloseAsync() => await _closeTcs.Task; +} + +public abstract class DialogViewModelBase : DialogViewModelBase; diff --git a/DiscordChatExporter.Gui/Framework/SnackbarManager.cs b/DiscordChatExporter.Gui/Framework/SnackbarManager.cs new file mode 100644 index 00000000..d3dbb5d7 --- /dev/null +++ b/DiscordChatExporter.Gui/Framework/SnackbarManager.cs @@ -0,0 +1,34 @@ +using System; +using Avalonia.Threading; +using Material.Styles.Controls; +using Material.Styles.Models; + +namespace DiscordChatExporter.Gui.Framework; + +public class SnackbarManager +{ + private readonly TimeSpan _defaultDuration = TimeSpan.FromSeconds(5); + + public void Notify(string message, TimeSpan? duration = null) => + SnackbarHost.Post( + new SnackbarModel(message, duration ?? _defaultDuration), + null, + DispatcherPriority.Normal + ); + + public void Notify( + string message, + string actionText, + Action actionHandler, + TimeSpan? duration = null + ) => + SnackbarHost.Post( + new SnackbarModel( + message, + duration ?? _defaultDuration, + new SnackbarButtonModel { Text = actionText, Action = actionHandler } + ), + null, + DispatcherPriority.Normal + ); +} diff --git a/DiscordChatExporter.Gui/Framework/ThemeVariant.cs b/DiscordChatExporter.Gui/Framework/ThemeVariant.cs new file mode 100644 index 00000000..a524f791 --- /dev/null +++ b/DiscordChatExporter.Gui/Framework/ThemeVariant.cs @@ -0,0 +1,8 @@ +namespace DiscordChatExporter.Gui.Framework; + +public enum ThemeVariant +{ + System, + Light, + Dark, +} diff --git a/DiscordChatExporter.Gui/Framework/UserControl.cs b/DiscordChatExporter.Gui/Framework/UserControl.cs new file mode 100644 index 00000000..68082f8f --- /dev/null +++ b/DiscordChatExporter.Gui/Framework/UserControl.cs @@ -0,0 +1,18 @@ +using System; +using Avalonia.Controls; + +namespace DiscordChatExporter.Gui.Framework; + +public class UserControl : UserControl +{ + public new TDataContext DataContext + { + get => + base.DataContext is TDataContext dataContext + ? dataContext + : throw new InvalidCastException( + $"DataContext is null or not of the expected type '{typeof(TDataContext).FullName}'." + ); + set => base.DataContext = value; + } +} diff --git a/DiscordChatExporter.Gui/Framework/ViewManager.cs b/DiscordChatExporter.Gui/Framework/ViewManager.cs new file mode 100644 index 00000000..5ae9e1a4 --- /dev/null +++ b/DiscordChatExporter.Gui/Framework/ViewManager.cs @@ -0,0 +1,50 @@ +using Avalonia.Controls; +using Avalonia.Controls.Templates; +using DiscordChatExporter.Gui.ViewModels; +using DiscordChatExporter.Gui.ViewModels.Components; +using DiscordChatExporter.Gui.ViewModels.Dialogs; +using DiscordChatExporter.Gui.Views; +using DiscordChatExporter.Gui.Views.Components; +using DiscordChatExporter.Gui.Views.Dialogs; + +namespace DiscordChatExporter.Gui.Framework; + +public partial class ViewManager +{ + private Control? TryCreateView(ViewModelBase viewModel) => + viewModel switch + { + MainViewModel => new MainView(), + DashboardViewModel => new DashboardView(), + ExportSetupViewModel => new ExportSetupView(), + MessageBoxViewModel => new MessageBoxView(), + SettingsViewModel => new SettingsView(), + _ => null, + }; + + public Control? TryBindView(ViewModelBase viewModel) + { + var view = TryCreateView(viewModel); + if (view is null) + return null; + + view.DataContext ??= viewModel; + view.Loaded += async (_, _) => await viewModel.InitializeAsync(); + + return view; + } + + public UserControl? TryBindUserControl(T viewModel) + where T : ViewModelBase => TryBindView(viewModel) as UserControl; + + public Window? TryBindWindow(T viewModel) + where T : ViewModelBase => TryBindView(viewModel) as Window; +} + +public partial class ViewManager : IDataTemplate +{ + bool IDataTemplate.Match(object? data) => data is ViewModelBase; + + Control? ITemplate.Build(object? data) => + data is ViewModelBase viewModel ? TryBindView(viewModel) : null; +} diff --git a/DiscordChatExporter.Gui/Framework/ViewModelBase.cs b/DiscordChatExporter.Gui/Framework/ViewModelBase.cs new file mode 100644 index 00000000..a9fb36f4 --- /dev/null +++ b/DiscordChatExporter.Gui/Framework/ViewModelBase.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace DiscordChatExporter.Gui.Framework; + +public abstract class ViewModelBase : ObservableObject, IDisposable +{ + ~ViewModelBase() => Dispose(false); + + protected void OnAllPropertiesChanged() => OnPropertyChanged(string.Empty); + + public virtual Task InitializeAsync() => Task.CompletedTask; + + protected virtual void Dispose(bool disposing) { } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } +} diff --git a/DiscordChatExporter.Gui/Framework/ViewModelManager.cs b/DiscordChatExporter.Gui/Framework/ViewModelManager.cs new file mode 100644 index 00000000..0e25c2e7 --- /dev/null +++ b/DiscordChatExporter.Gui/Framework/ViewModelManager.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using DiscordChatExporter.Core.Discord.Data; +using DiscordChatExporter.Gui.Localization; +using DiscordChatExporter.Gui.ViewModels; +using DiscordChatExporter.Gui.ViewModels.Components; +using DiscordChatExporter.Gui.ViewModels.Dialogs; +using Microsoft.Extensions.DependencyInjection; + +namespace DiscordChatExporter.Gui.Framework; + +public class ViewModelManager(IServiceProvider services, LocalizationManager localizationManager) +{ + public MainViewModel GetMainViewModel() => services.GetRequiredService(); + + public DashboardViewModel GetDashboardViewModel() => + services.GetRequiredService(); + + public ExportSetupViewModel GetExportSetupViewModel( + Guild guild, + IReadOnlyList channels + ) + { + var viewModel = services.GetRequiredService(); + + viewModel.Guild = guild; + viewModel.Channels = channels; + + return viewModel; + } + + public MessageBoxViewModel GetMessageBoxViewModel( + string title, + string message, + string? okButtonText, + string? cancelButtonText + ) + { + var viewModel = services.GetRequiredService(); + + viewModel.Title = title; + viewModel.Message = message; + viewModel.DefaultButtonText = okButtonText; + viewModel.CancelButtonText = cancelButtonText; + + return viewModel; + } + + public MessageBoxViewModel GetMessageBoxViewModel(string title, string message) => + GetMessageBoxViewModel(title, message, localizationManager.CloseButton, null); + + public SettingsViewModel GetSettingsViewModel() => + services.GetRequiredService(); +} diff --git a/DiscordChatExporter.Gui/Framework/Window.cs b/DiscordChatExporter.Gui/Framework/Window.cs new file mode 100644 index 00000000..cdfaef71 --- /dev/null +++ b/DiscordChatExporter.Gui/Framework/Window.cs @@ -0,0 +1,18 @@ +using System; +using Avalonia.Controls; + +namespace DiscordChatExporter.Gui.Framework; + +public class Window : Window +{ + public new TDataContext DataContext + { + get => + base.DataContext is TDataContext dataContext + ? dataContext + : throw new InvalidCastException( + $"DataContext is null or not of the expected type '{typeof(TDataContext).FullName}'." + ); + set => base.DataContext = value; + } +} diff --git a/DiscordChatExporter.Gui/Localization/Language.cs b/DiscordChatExporter.Gui/Localization/Language.cs new file mode 100644 index 00000000..faebe5bf --- /dev/null +++ b/DiscordChatExporter.Gui/Localization/Language.cs @@ -0,0 +1,11 @@ +namespace DiscordChatExporter.Gui.Localization; + +public enum Language +{ + System, + English, + Ukrainian, + German, + French, + Spanish, +} diff --git a/DiscordChatExporter.Gui/Localization/LocalizationManager.English.cs b/DiscordChatExporter.Gui/Localization/LocalizationManager.English.cs new file mode 100644 index 00000000..d85ea435 --- /dev/null +++ b/DiscordChatExporter.Gui/Localization/LocalizationManager.English.cs @@ -0,0 +1,156 @@ +using System.Collections.Generic; + +namespace DiscordChatExporter.Gui.Localization; + +public partial class LocalizationManager +{ + private static readonly IReadOnlyDictionary EnglishLocalization = + new Dictionary + { + // Dashboard + [nameof(PullGuildsTooltip)] = "Pull available servers and channels (Enter)", + [nameof(SettingsTooltip)] = "Settings", + [nameof(LastMessageSentTooltip)] = "Last message sent:", + [nameof(TokenPlaceholderText)] = "Token", + // Token instructions (personal account) + [nameof(TokenPersonalHeader)] = "To get the token for your personal account:", + [nameof(TokenPersonalTosWarning)] = + "* Automating user accounts is technically against TOS — **use at your own risk**!", + [nameof(TokenPersonalInstructions)] = """ + 1. Open Discord in your web browser and login + 2. Open any server or direct message channel + 3. Press **Ctrl+Shift+I** to show developer tools + 4. Navigate to the **Network** tab + 5. Press **Ctrl+R** to reload + 6. Switch between random channels to trigger network requests + 7. Search for a request that starts with **messages** + 8. Select the **Headers** tab on the right + 9. Scroll down to the **Request Headers** section + 10. Copy the value of the **authorization** header + """, + // Token instructions (bot) + [nameof(TokenBotHeader)] = "To get the token for your bot:", + [nameof(TokenBotInstructions)] = """ + The token is generated during bot creation. If you lost it, generate a new one: + + 1. Open Discord [developer portal](https://discord.com/developers/applications) + 2. Open your application's settings + 3. Navigate to the **Bot** section on the left + 4. Under **Token** click **Reset Token** + 5. Click **Yes, do it!** and authenticate to confirm + * Integrations using the previous token will stop working until updated + * Your bot needs to have the **Message Content Intent** enabled to read messages + """, + [nameof(TokenHelpText)] = + "If you have questions or issues, please refer to the [documentation](https://github.com/Tyrrrz/DiscordChatExporter/tree/prime/.docs)", + // Settings + [nameof(SettingsTitle)] = "Settings", + [nameof(ThemeLabel)] = "Theme", + [nameof(ThemeTooltip)] = "Preferred user interface theme", + [nameof(LanguageLabel)] = "Language", + [nameof(LanguageTooltip)] = "Preferred user interface language", + [nameof(AutoUpdateLabel)] = "Auto-update", + [nameof(AutoUpdateTooltip)] = "Perform automatic updates on every launch", + [nameof(PersistTokenLabel)] = "Persist token", + [nameof(PersistTokenTooltip)] = """ + Save the last used token to a file so that it can be persisted between sessions. + **Warning**: although the token is stored with encryption, it may still be recovered by an attacker who has access to your system. + """, + [nameof(RateLimitPreferenceLabel)] = "Rate limit preference", + [nameof(RateLimitPreferenceTooltip)] = + "Whether to respect advisory rate limits. If disabled, only hard rate limits (i.e. 429 responses) will be respected.", + [nameof(ShowThreadsLabel)] = "Show threads", + [nameof(ShowThreadsTooltip)] = "Which types of threads to show in the channel list", + [nameof(LocaleLabel)] = "Locale", + [nameof(LocaleTooltip)] = "Locale to use when formatting dates and numbers", + [nameof(NormalizeToUtcLabel)] = "Normalize to UTC", + [nameof(NormalizeToUtcTooltip)] = "Normalize all timestamps to UTC+0", + [nameof(ParallelLimitLabel)] = "Parallel limit", + [nameof(ParallelLimitTooltip)] = "How many channels can be exported at the same time", + // Export Setup + [nameof(ChannelsSelectedText)] = "channels selected", + [nameof(OutputPathLabel)] = "Output path", + [nameof(OutputPathTooltip)] = """ + Output file or directory path. + + If a directory is specified, file names will be generated automatically based on the channel names and export parameters. + + Directory paths must end with a slash to avoid ambiguity. + + Available template tokens: + **%g** — server ID + **%G** — server name + **%t** — category ID + **%T** — category name + **%c** — channel ID + **%C** — channel name + **%p** — channel position + **%P** — category position + **%a** — after date + **%b** — before date + **%d** — current date + """, + [nameof(FormatLabel)] = "Format", + [nameof(FormatTooltip)] = "Export format", + [nameof(AfterDateLabel)] = "After (date)", + [nameof(AfterDateTooltip)] = "Only include messages sent after this date", + [nameof(BeforeDateLabel)] = "Before (date)", + [nameof(BeforeDateTooltip)] = "Only include messages sent before this date", + [nameof(AfterTimeLabel)] = "After (time)", + [nameof(AfterTimeTooltip)] = "Only include messages sent after this time", + [nameof(BeforeTimeLabel)] = "Before (time)", + [nameof(BeforeTimeTooltip)] = "Only include messages sent before this time", + [nameof(PartitionLimitLabel)] = "Partition limit", + [nameof(PartitionLimitTooltip)] = + "Split the output into partitions, each limited to the specified number of messages (e.g. '100') or file size (e.g. '10mb')", + [nameof(MessageFilterLabel)] = "Message filter", + [nameof(MessageFilterTooltip)] = + "Only include messages that satisfy this filter (e.g. 'from:foo#1234' or 'has:image'). See the documentation for more info.", + [nameof(ReverseMessageOrderLabel)] = "Reverse messages", + [nameof(ReverseMessageOrderTooltip)] = + "Export messages in reverse chronological order (newest first)", + [nameof(FormatMarkdownLabel)] = "Format markdown", + [nameof(FormatMarkdownTooltip)] = + "Process markdown, mentions, and other special tokens", + [nameof(DownloadAssetsLabel)] = "Download assets", + [nameof(DownloadAssetsTooltip)] = + "Download assets referenced by the export (user avatars, attached files, embedded images, etc.)", + [nameof(ReuseAssetsLabel)] = "Reuse assets", + [nameof(ReuseAssetsTooltip)] = + "Reuse previously downloaded assets to avoid redundant requests", + [nameof(AssetsDirPathLabel)] = "Assets directory path", + [nameof(AssetsDirPathTooltip)] = + "Download assets to this directory. If not specified, the asset directory path will be derived from the output path.", + [nameof(AdvancedOptionsTooltip)] = "Toggle advanced options", + [nameof(ExportButton)] = "EXPORT", + // Common buttons + [nameof(CloseButton)] = "CLOSE", + [nameof(CancelButton)] = "CANCEL", + // Dialog messages + [nameof(UkraineSupportTitle)] = "Thank you for supporting Ukraine!", + [nameof(UkraineSupportMessage)] = """ + As Russia wages a genocidal war against my country, I'm grateful to everyone who continues to stand with Ukraine in our fight for freedom. + + Click LEARN MORE to find ways that you can help. + """, + [nameof(LearnMoreButton)] = "LEARN MORE", + [nameof(UnstableBuildTitle)] = "Unstable build warning", + [nameof(UnstableBuildMessage)] = """ + You're using a development build of {0}. These builds are not thoroughly tested and may contain bugs. + + Auto-updates are disabled for development builds. + + Click SEE RELEASES if you want to download a stable release instead. + """, + [nameof(SeeReleasesButton)] = "SEE RELEASES", + [nameof(UpdateDownloadingMessage)] = "Downloading update to {0} v{1}...", + [nameof(UpdateReadyMessage)] = + "Update has been downloaded and will be installed when you exit", + [nameof(UpdateInstallNowButton)] = "INSTALL NOW", + [nameof(UpdateFailedMessage)] = "Failed to perform application update", + [nameof(ErrorPullingGuildsTitle)] = "Error pulling servers", + [nameof(ErrorPullingChannelsTitle)] = "Error pulling channels", + [nameof(ErrorExportingTitle)] = "Error exporting channel(s)", + [nameof(SuccessfulExportMessage)] = "Successfully exported {0} channel(s)", + }; +} diff --git a/DiscordChatExporter.Gui/Localization/LocalizationManager.French.cs b/DiscordChatExporter.Gui/Localization/LocalizationManager.French.cs new file mode 100644 index 00000000..dbe2c65a --- /dev/null +++ b/DiscordChatExporter.Gui/Localization/LocalizationManager.French.cs @@ -0,0 +1,158 @@ +using System.Collections.Generic; + +namespace DiscordChatExporter.Gui.Localization; + +public partial class LocalizationManager +{ + private static readonly IReadOnlyDictionary FrenchLocalization = new Dictionary< + string, + string + > + { + // Dashboard + [nameof(PullGuildsTooltip)] = "Charger les serveurs et canaux disponibles (Entrée)", + [nameof(SettingsTooltip)] = "Paramètres", + [nameof(LastMessageSentTooltip)] = "Dernier message envoyé :", + [nameof(TokenPlaceholderText)] = "Token", + // Token instructions (personal account) + [nameof(TokenPersonalHeader)] = "Obtenir le token pour votre compte personnel :", + [nameof(TokenPersonalTosWarning)] = + "* L'automatisation des comptes est techniquement contraire aux CGU — **à vos risques et périls**!", + [nameof(TokenPersonalInstructions)] = """ + 1. Ouvrez Discord dans votre navigateur web et connectez-vous + 2. Ouvrez n'importe quel serveur ou canal de message direct + 3. Appuyez sur **Ctrl+Shift+I** pour afficher les outils de développement + 4. Naviguez vers l'onglet **Network** + 5. Appuyez sur **Ctrl+R** pour recharger + 6. Changez de canal pour déclencher des requêtes réseau + 7. Cherchez une requête commençant par **messages** + 8. Sélectionnez l'onglet **Headers** à droite + 9. Faites défiler jusqu'à la section **Request Headers** + 10. Copiez la valeur de l'en-tête **authorization** + """, + // Token instructions (bot) + [nameof(TokenBotHeader)] = "Obtenir le token pour votre bot :", + [nameof(TokenBotInstructions)] = """ + Le token est généré lors de la création du bot. Si vous l'avez perdu, générez-en un nouveau : + + 1. Ouvrez Discord [portail développeur](https://discord.com/developers/applications) + 2. Ouvrez les paramètres de votre application + 3. Naviguez vers la section **Bot** à gauche + 4. Sous **Token**, cliquez sur **Reset Token** + 5. Cliquez sur **Yes, do it!** et confirmez + * Les intégrations utilisant l'ancien token cesseront de fonctionner jusqu'à leur mise à jour + * Votre bot doit avoir l'option **Message Content Intent** activée pour lire les messages + """, + [nameof(TokenHelpText)] = + "Pour les questions ou problèmes, veuillez consulter la [documentation](https://github.com/Tyrrrz/DiscordChatExporter/tree/prime/.docs)", + // Settings + [nameof(SettingsTitle)] = "Paramètres", + [nameof(ThemeLabel)] = "Thème", + [nameof(ThemeTooltip)] = "Thème d'interface préféré", + [nameof(LanguageLabel)] = "Langue", + [nameof(LanguageTooltip)] = "Langue d'interface préférée", + [nameof(AutoUpdateLabel)] = "Mise à jour automatique", + [nameof(AutoUpdateTooltip)] = "Effectuer des mises à jour automatiques à chaque lancement", + [nameof(PersistTokenLabel)] = "Conserver le token", + [nameof(PersistTokenTooltip)] = """ + Enregistrer le dernier token utilisé dans un fichier pour le conserver entre les sessions. + **Avertissement** : bien que le token soit stocké avec chiffrement, il peut toujours être récupéré par un attaquant ayant accès à votre système. + """, + [nameof(RateLimitPreferenceLabel)] = "Préférence de limite de débit", + [nameof(RateLimitPreferenceTooltip)] = + "Indique s'il faut respecter les limites de débit recommandées. Si désactivé, seules les limites strictes (réponses 429) seront respectées.", + [nameof(ShowThreadsLabel)] = "Afficher les fils", + [nameof(ShowThreadsTooltip)] = "Quels types de fils afficher dans la liste des canaux", + [nameof(LocaleLabel)] = "Locale", + [nameof(LocaleTooltip)] = "Locale à utiliser pour le formatage des dates et des nombres", + [nameof(NormalizeToUtcLabel)] = "Normaliser en UTC", + [nameof(NormalizeToUtcTooltip)] = "Normaliser tous les horodatages en UTC+0", + [nameof(ParallelLimitLabel)] = "Limite parallèle", + [nameof(ParallelLimitTooltip)] = "Combien de canaux peuvent être exportés simultanément", + // Export Setup + [nameof(ChannelsSelectedText)] = "canaux sélectionnés", + [nameof(OutputPathLabel)] = "Chemin de sortie", + [nameof(OutputPathTooltip)] = """ + Chemin du fichier ou répertoire de sortie. + + Si un répertoire est spécifié, les noms de fichiers seront générés automatiquement en fonction des noms de canaux et des paramètres d'exportation. + + Les chemins de répertoire doivent se terminer par un slash pour éviter toute ambiguïté. + + Jetons de modèle disponibles : + **%g** — ID du serveur + **%G** — nom du serveur + **%t** — ID de la catégorie + **%T** — nom de la catégorie + **%c** — ID du canal + **%C** — nom du canal + **%p** — position du canal + **%P** — position de la catégorie + **%a** — date après + **%b** — date avant + **%d** — date actuelle + """, + [nameof(FormatLabel)] = "Format", + [nameof(FormatTooltip)] = "Format d'exportation", + [nameof(AfterDateLabel)] = "Après (date)", + [nameof(AfterDateTooltip)] = "Inclure uniquement les messages envoyés après cette date", + [nameof(BeforeDateLabel)] = "Avant (date)", + [nameof(BeforeDateTooltip)] = "Inclure uniquement les messages envoyés avant cette date", + [nameof(AfterTimeLabel)] = "Après (heure)", + [nameof(AfterTimeTooltip)] = "Inclure uniquement les messages envoyés après cette heure", + [nameof(BeforeTimeLabel)] = "Avant (heure)", + [nameof(BeforeTimeTooltip)] = "Inclure uniquement les messages envoyés avant cette heure", + [nameof(PartitionLimitLabel)] = "Limite de partition", + [nameof(PartitionLimitTooltip)] = + "Diviser la sortie en partitions, chacune limitée au nombre de messages spécifié (ex. '100') ou à la taille de fichier (ex. '10mb')", + [nameof(MessageFilterLabel)] = "Filtre de messages", + [nameof(MessageFilterTooltip)] = + "Inclure uniquement les messages satisfaisant ce filtre (ex. 'from:foo#1234' ou 'has:image'). Voir la documentation pour plus d'informations.", + [nameof(ReverseMessageOrderLabel)] = "Inverser l'ordre des messages", + [nameof(ReverseMessageOrderTooltip)] = + "Exporter les messages en ordre chronologique inversé (les plus récents en premier)", + [nameof(FormatMarkdownLabel)] = "Formater le markdown", + [nameof(FormatMarkdownTooltip)] = + "Traiter le markdown, les mentions et autres tokens spéciaux", + [nameof(DownloadAssetsLabel)] = "Télécharger les ressources", + [nameof(DownloadAssetsTooltip)] = + "Télécharger les ressources référencées par l'export (avatars, fichiers joints, images intégrées, etc.)", + [nameof(ReuseAssetsLabel)] = "Réutiliser les ressources", + [nameof(ReuseAssetsTooltip)] = + "Réutiliser les ressources précédemment téléchargées pour éviter les requêtes redondantes", + [nameof(AssetsDirPathLabel)] = "Chemin du dossier des ressources", + [nameof(AssetsDirPathTooltip)] = + "Télécharger les ressources dans ce dossier. Si non spécifié, le chemin sera dérivé du chemin de sortie.", + [nameof(AdvancedOptionsTooltip)] = "Basculer les options avancées", + [nameof(ExportButton)] = "EXPORTER", + // Common buttons + [nameof(CloseButton)] = "FERMER", + [nameof(CancelButton)] = "ANNULER", + // Dialog messages + [nameof(UkraineSupportTitle)] = "Merci de soutenir l'Ukraine !", + [nameof(UkraineSupportMessage)] = """ + Alors que la Russie mène une guerre génocidaire contre mon pays, je suis reconnaissant envers tous ceux qui continuent à soutenir l'Ukraine dans notre lutte pour la liberté. + + Cliquez sur EN SAVOIR PLUS pour trouver des moyens d'aider. + """, + [nameof(LearnMoreButton)] = "EN SAVOIR PLUS", + [nameof(UnstableBuildTitle)] = "Avertissement : version instable", + [nameof(UnstableBuildMessage)] = """ + Vous utilisez une version de développement de {0}. Ces versions ne sont pas rigoureusement testées et peuvent contenir des bugs. + + Les mises à jour automatiques sont désactivées pour les versions de développement. + + Cliquez sur VOIR LES VERSIONS pour télécharger une version stable. + """, + [nameof(SeeReleasesButton)] = "VOIR LES VERSIONS", + [nameof(UpdateDownloadingMessage)] = "Téléchargement de la mise à jour vers {0} v{1}...", + [nameof(UpdateReadyMessage)] = + "La mise à jour a été téléchargée et sera installée à la fermeture", + [nameof(UpdateInstallNowButton)] = "INSTALLER MAINTENANT", + [nameof(UpdateFailedMessage)] = "Échec de la mise à jour de l'application", + [nameof(ErrorPullingGuildsTitle)] = "Erreur lors du chargement des serveurs", + [nameof(ErrorPullingChannelsTitle)] = "Erreur lors du chargement des canaux", + [nameof(ErrorExportingTitle)] = "Erreur lors de l'exportation des canaux", + [nameof(SuccessfulExportMessage)] = "{0} canal(-aux) exporté(s) avec succès", + }; +} diff --git a/DiscordChatExporter.Gui/Localization/LocalizationManager.German.cs b/DiscordChatExporter.Gui/Localization/LocalizationManager.German.cs new file mode 100644 index 00000000..6ca87c78 --- /dev/null +++ b/DiscordChatExporter.Gui/Localization/LocalizationManager.German.cs @@ -0,0 +1,162 @@ +using System.Collections.Generic; + +namespace DiscordChatExporter.Gui.Localization; + +public partial class LocalizationManager +{ + private static readonly IReadOnlyDictionary GermanLocalization = new Dictionary< + string, + string + > + { + // Dashboard + [nameof(PullGuildsTooltip)] = "Verfügbare Server und Kanäle laden (Enter)", + [nameof(SettingsTooltip)] = "Einstellungen", + [nameof(LastMessageSentTooltip)] = "Letzte Nachricht gesendet:", + [nameof(TokenPlaceholderText)] = "Token", + // Token instructions (personal account) + [nameof(TokenPersonalHeader)] = "Token für Ihr persönliches Konto abrufen:", + [nameof(TokenPersonalTosWarning)] = + "* Das Automatisieren von Benutzerkonten verstößt technisch gegen die AGB — **auf eigene Gefahr**!", + [nameof(TokenPersonalInstructions)] = """ + 1. Öffnen Sie Discord in Ihrem Webbrowser und melden Sie sich an + 2. Öffnen Sie einen Server oder einen direkten Nachrichtenkanal + 3. Drücken Sie **Ctrl+Shift+I**, um die Entwicklertools anzuzeigen + 4. Navigieren Sie zum Reiter **Network** + 5. Drücken Sie **Ctrl+R** zum Neuladen + 6. Wechseln Sie zwischen Kanälen, um Netzwerkanfragen auszulösen + 7. Suchen Sie nach einer Anfrage, die mit **messages** beginnt + 8. Wählen Sie den Reiter **Headers** auf der rechten Seite + 9. Scrollen Sie nach unten zum Abschnitt **Request Headers** + 10. Kopieren Sie den Wert des Headers **authorization** + """, + // Token instructions (bot) + [nameof(TokenBotHeader)] = "Token für Ihren Bot abrufen:", + [nameof(TokenBotInstructions)] = """ + Der Token wird bei der Bot-Erstellung generiert. Falls er verloren gegangen ist, generieren Sie einen neuen: + + 1. Öffnen Sie Discord [Entwicklerportal](https://discord.com/developers/applications) + 2. Öffnen Sie die Einstellungen Ihrer Anwendung + 3. Navigieren Sie zum Abschnitt **Bot** auf der linken Seite + 4. Klicken Sie unter **Token** auf **Reset Token** + 5. Klicken Sie auf **Yes, do it!** und bestätigen Sie + * Integrationen, die den alten Token verwenden, hören auf zu funktionieren, bis sie aktualisiert werden + * Ihr Bot benötigt die aktivierte **Message Content Intent**, um Nachrichten zu lesen + """, + [nameof(TokenHelpText)] = + "Bei Fragen oder Problemen lesen Sie die [Dokumentation](https://github.com/Tyrrrz/DiscordChatExporter/tree/prime/.docs)", + // Settings + [nameof(SettingsTitle)] = "Einstellungen", + [nameof(ThemeLabel)] = "Design", + [nameof(ThemeTooltip)] = "Bevorzugtes Oberflächendesign", + [nameof(LanguageLabel)] = "Sprache", + [nameof(LanguageTooltip)] = "Bevorzugte Sprache der Benutzeroberfläche", + [nameof(AutoUpdateLabel)] = "Automatische Updates", + [nameof(AutoUpdateTooltip)] = "Automatische Updates bei jedem Start durchführen", + [nameof(PersistTokenLabel)] = "Token speichern", + [nameof(PersistTokenTooltip)] = """ + Den zuletzt verwendeten Token in einer Datei speichern, damit er zwischen Sitzungen erhalten bleibt. + **Warnung**: Der Token wird mit Verschlüsselung gespeichert, kann aber dennoch von einem Angreifer mit Zugriff auf Ihr System wiederhergestellt werden. + """, + [nameof(RateLimitPreferenceLabel)] = "Ratenlimit-Einstellung", + [nameof(RateLimitPreferenceTooltip)] = + "Ob empfohlene Ratenlimits eingehalten werden sollen. Wenn deaktiviert, werden nur harte Ratenlimits (d. h. 429-Antworten) eingehalten.", + [nameof(ShowThreadsLabel)] = "Threads anzeigen", + [nameof(ShowThreadsTooltip)] = "Welche Thread-Typen in der Kanalliste angezeigt werden", + [nameof(LocaleLabel)] = "Gebietsschema", + [nameof(LocaleTooltip)] = "Gebietsschema für die Formatierung von Daten und Zahlen", + [nameof(NormalizeToUtcLabel)] = "Auf UTC normalisieren", + [nameof(NormalizeToUtcTooltip)] = "Alle Zeitstempel auf UTC+0 normalisieren", + [nameof(ParallelLimitLabel)] = "Paralleles Limit", + [nameof(ParallelLimitTooltip)] = "Wie viele Kanäle gleichzeitig exportiert werden können", + // Export Setup + [nameof(ChannelsSelectedText)] = "Kanäle ausgewählt", + [nameof(OutputPathLabel)] = "Ausgabepfad", + [nameof(OutputPathTooltip)] = """ + Ausgabedatei- oder Verzeichnispfad. + + Wenn ein Verzeichnis angegeben wird, werden Dateinamen automatisch basierend auf den Kanalnamen und Exportparametern generiert. + + Verzeichnispfade müssen mit einem Schrägstrich enden, um Mehrdeutigkeiten zu vermeiden. + + Verfügbare Vorlagen-Token: + **%g** — Server-ID + **%G** — Servername + **%t** — Kategorie-ID + **%T** — Kategoriename + **%c** — Kanal-ID + **%C** — Kanalname + **%p** — Kanalposition + **%P** — Kategorieposition + **%a** — Datum ab + **%b** — Datum bis + **%d** — aktuelles Datum + """, + [nameof(FormatLabel)] = "Format", + [nameof(FormatTooltip)] = "Exportformat", + [nameof(AfterDateLabel)] = "Nach (Datum)", + [nameof(AfterDateTooltip)] = + "Nur Nachrichten einschließen, die nach diesem Datum gesendet wurden", + [nameof(BeforeDateLabel)] = "Vor (Datum)", + [nameof(BeforeDateTooltip)] = + "Nur Nachrichten einschließen, die vor diesem Datum gesendet wurden", + [nameof(AfterTimeLabel)] = "Nach (Uhrzeit)", + [nameof(AfterTimeTooltip)] = + "Nur Nachrichten einschließen, die nach dieser Uhrzeit gesendet wurden", + [nameof(BeforeTimeLabel)] = "Vor (Uhrzeit)", + [nameof(BeforeTimeTooltip)] = + "Nur Nachrichten einschließen, die vor dieser Uhrzeit gesendet wurden", + [nameof(PartitionLimitLabel)] = "Partitionslimit", + [nameof(PartitionLimitTooltip)] = + "Die Ausgabe in Partitionen aufteilen, jede begrenzt auf die angegebene Anzahl von Nachrichten (z. B. '100') oder Dateigröße (z. B. '10mb')", + [nameof(MessageFilterLabel)] = "Nachrichtenfilter", + [nameof(MessageFilterTooltip)] = + "Nur Nachrichten einschließen, die diesem Filter entsprechen (z. B. 'from:foo#1234' oder 'has:image'). Weitere Informationen finden Sie in der Dokumentation.", + [nameof(ReverseMessageOrderLabel)] = "Nachrichtenreihenfolge umkehren", + [nameof(ReverseMessageOrderTooltip)] = + "Nachrichten in umgekehrter chronologischer Reihenfolge exportieren (neueste zuerst)", + [nameof(FormatMarkdownLabel)] = "Markdown formatieren", + [nameof(FormatMarkdownTooltip)] = + "Markdown, Erwähnungen und andere spezielle Token verarbeiten", + [nameof(DownloadAssetsLabel)] = "Assets herunterladen", + [nameof(DownloadAssetsTooltip)] = + "Vom Export referenzierte Assets herunterladen (Benutzeravatare, angehängte Dateien, eingebettete Bilder usw.)", + [nameof(ReuseAssetsLabel)] = "Assets wiederverwenden", + [nameof(ReuseAssetsTooltip)] = + "Zuvor heruntergeladene Assets wiederverwenden, um redundante Anfragen zu vermeiden", + [nameof(AssetsDirPathLabel)] = "Asset-Verzeichnispfad", + [nameof(AssetsDirPathTooltip)] = + "Assets in dieses Verzeichnis herunterladen. Wenn nicht angegeben, wird der Asset-Verzeichnispfad vom Ausgabepfad abgeleitet.", + [nameof(AdvancedOptionsTooltip)] = "Erweiterte Optionen umschalten", + [nameof(ExportButton)] = "EXPORTIEREN", + // Common buttons + [nameof(CloseButton)] = "SCHLIESSEN", + [nameof(CancelButton)] = "ABBRECHEN", + // Dialog messages + [nameof(UkraineSupportTitle)] = "Danke für Ihre Unterstützung der Ukraine!", + [nameof(UkraineSupportMessage)] = """ + Während Russland einen Vernichtungskrieg gegen mein Land führt, bin ich jedem dankbar, der weiterhin an der Seite der Ukraine in unserem Kampf für die Freiheit steht. + + Klicken Sie auf MEHR ERFAHREN, um Möglichkeiten der Unterstützung zu finden. + """, + [nameof(LearnMoreButton)] = "MEHR ERFAHREN", + [nameof(UnstableBuildTitle)] = "Warnung: Instabile Version", + [nameof(UnstableBuildMessage)] = """ + Sie verwenden eine Entwicklungsversion von {0}. Diese Versionen wurden nicht gründlich getestet und können Fehler enthalten. + + Automatische Updates sind für Entwicklungsversionen deaktiviert. + + Klicken Sie auf RELEASES ANZEIGEN, wenn Sie stattdessen eine stabile Version herunterladen möchten. + """, + [nameof(SeeReleasesButton)] = "RELEASES ANZEIGEN", + [nameof(UpdateDownloadingMessage)] = "Update auf {0} v{1} wird heruntergeladen...", + [nameof(UpdateReadyMessage)] = + "Update wurde heruntergeladen und wird beim Beenden installiert", + [nameof(UpdateInstallNowButton)] = "JETZT INSTALLIEREN", + [nameof(UpdateFailedMessage)] = "Anwendungsupdate konnte nicht durchgeführt werden", + [nameof(ErrorPullingGuildsTitle)] = "Fehler beim Laden der Server", + [nameof(ErrorPullingChannelsTitle)] = "Fehler beim Laden der Kanäle", + [nameof(ErrorExportingTitle)] = "Fehler beim Exportieren der Kanäle", + [nameof(SuccessfulExportMessage)] = "{0} Kanal/-äle erfolgreich exportiert", + }; +} diff --git a/DiscordChatExporter.Gui/Localization/LocalizationManager.Spanish.cs b/DiscordChatExporter.Gui/Localization/LocalizationManager.Spanish.cs new file mode 100644 index 00000000..622ef5c2 --- /dev/null +++ b/DiscordChatExporter.Gui/Localization/LocalizationManager.Spanish.cs @@ -0,0 +1,156 @@ +using System.Collections.Generic; + +namespace DiscordChatExporter.Gui.Localization; + +public partial class LocalizationManager +{ + private static readonly IReadOnlyDictionary SpanishLocalization = + new Dictionary + { + // Dashboard + [nameof(PullGuildsTooltip)] = "Cargar servidores y canales disponibles (Enter)", + [nameof(SettingsTooltip)] = "Ajustes", + [nameof(LastMessageSentTooltip)] = "Último mensaje enviado:", + [nameof(TokenPlaceholderText)] = "Token", + // Token instructions (personal account) + [nameof(TokenPersonalHeader)] = "Cómo obtener el token para tu cuenta personal:", + [nameof(TokenPersonalTosWarning)] = + "* Automatizar cuentas de usuario técnicamente va en contra de los ToS — **bajo tu propio riesgo**!", + [nameof(TokenPersonalInstructions)] = """ + 1. Abre Discord en tu navegador web e inicia sesión + 2. Abre cualquier servidor o canal de mensaje directo + 3. Presiona **Ctrl+Shift+I** para mostrar las herramientas de desarrollo + 4. Navega a la pestaña **Network** + 5. Presiona **Ctrl+R** para recargar + 6. Cambia entre canales para activar solicitudes de red + 7. Busca una solicitud que comience con **messages** + 8. Selecciona la pestaña **Headers** a la derecha + 9. Desplázate hasta la sección **Request Headers** + 10. Copia el valor del encabezado **authorization** + """, + // Token instructions (bot) + [nameof(TokenBotHeader)] = "Cómo obtener el token para tu bot:", + [nameof(TokenBotInstructions)] = """ + El token se genera al crear el bot. Si lo perdiste, genera uno nuevo: + + 1. Abre Discord [portal de desarrolladores](https://discord.com/developers/applications) + 2. Abre la configuración de tu aplicación + 3. Navega a la sección **Bot** en el lado izquierdo + 4. En **Token**, haz clic en **Reset Token** + 5. Haz clic en **Yes, do it!** y autentica para confirmar + * Las integraciones que usen el token anterior dejarán de funcionar hasta que se actualicen + * Tu bot necesita tener habilitado **Message Content Intent** para leer mensajes + """, + [nameof(TokenHelpText)] = + "Si tienes preguntas o problemas, consulta la [documentación](https://github.com/Tyrrrz/DiscordChatExporter/tree/prime/.docs)", + // Settings + [nameof(SettingsTitle)] = "Ajustes", + [nameof(ThemeLabel)] = "Tema", + [nameof(ThemeTooltip)] = "Tema de interfaz preferido", + [nameof(LanguageLabel)] = "Idioma", + [nameof(LanguageTooltip)] = "Idioma de interfaz preferido", + [nameof(AutoUpdateLabel)] = "Actualización automática", + [nameof(AutoUpdateTooltip)] = "Realizar actualizaciones automáticas en cada inicio", + [nameof(PersistTokenLabel)] = "Guardar token", + [nameof(PersistTokenTooltip)] = """ + Guardar el último token utilizado en un archivo para conservarlo entre sesiones. + **Advertencia**: aunque el token se almacena con cifrado, aún puede ser recuperado por un atacante con acceso a tu sistema. + """, + [nameof(RateLimitPreferenceLabel)] = "Preferencia de límite de velocidad", + [nameof(RateLimitPreferenceTooltip)] = + "Si se deben respetar los límites de velocidad recomendados. Si está desactivado, solo se respetarán los límites estrictos (respuestas 429).", + [nameof(ShowThreadsLabel)] = "Mostrar hilos", + [nameof(ShowThreadsTooltip)] = "Qué tipos de hilos mostrar en la lista de canales", + [nameof(LocaleLabel)] = "Configuración regional", + [nameof(LocaleTooltip)] = "Configuración regional para el formato de fechas y números", + [nameof(NormalizeToUtcLabel)] = "Normalizar a UTC", + [nameof(NormalizeToUtcTooltip)] = "Normalizar todas las marcas de tiempo a UTC+0", + [nameof(ParallelLimitLabel)] = "Límite paralelo", + [nameof(ParallelLimitTooltip)] = "Cuántos canales pueden exportarse al mismo tiempo", + // Export Setup + [nameof(ChannelsSelectedText)] = "canales seleccionados", + [nameof(OutputPathLabel)] = "Ruta de salida", + [nameof(OutputPathTooltip)] = """ + Ruta del archivo o directorio de salida. + + Si se especifica un directorio, los nombres de archivo se generarán automáticamente según los nombres de los canales y los parámetros de exportación. + + Las rutas de directorio deben terminar con una barra diagonal para evitar ambigüedades. + + Tokens de plantilla disponibles: + **%g** — ID del servidor + **%G** — nombre del servidor + **%t** — ID de categoría + **%T** — nombre de categoría + **%c** — ID del canal + **%C** — nombre del canal + **%p** — posición del canal + **%P** — posición de la categoría + **%a** — fecha desde + **%b** — fecha hasta + **%d** — fecha actual + """, + [nameof(FormatLabel)] = "Formato", + [nameof(FormatTooltip)] = "Formato de exportación", + [nameof(AfterDateLabel)] = "Después (fecha)", + [nameof(AfterDateTooltip)] = "Solo incluir mensajes enviados después de esta fecha", + [nameof(BeforeDateLabel)] = "Antes (fecha)", + [nameof(BeforeDateTooltip)] = "Solo incluir mensajes enviados antes de esta fecha", + [nameof(AfterTimeLabel)] = "Después (hora)", + [nameof(AfterTimeTooltip)] = "Solo incluir mensajes enviados después de esta hora", + [nameof(BeforeTimeLabel)] = "Antes (hora)", + [nameof(BeforeTimeTooltip)] = "Solo incluir mensajes enviados antes de esta hora", + [nameof(PartitionLimitLabel)] = "Límite de partición", + [nameof(PartitionLimitTooltip)] = + "Dividir la salida en particiones, cada una limitada al número de mensajes especificado (p. ej. '100') o tamaño de archivo (p. ej. '10mb')", + [nameof(MessageFilterLabel)] = "Filtro de mensajes", + [nameof(MessageFilterTooltip)] = + "Solo incluir mensajes que satisfagan este filtro (p. ej. 'from:foo#1234' o 'has:image'). Consulte la documentación para más información.", + [nameof(ReverseMessageOrderLabel)] = "Invertir orden de mensajes", + [nameof(ReverseMessageOrderTooltip)] = + "Exportar mensajes en orden cronológico inverso (los más recientes primero)", + [nameof(FormatMarkdownLabel)] = "Formatear markdown", + [nameof(FormatMarkdownTooltip)] = + "Procesar markdown, menciones y otros tokens especiales", + [nameof(DownloadAssetsLabel)] = "Descargar recursos", + [nameof(DownloadAssetsTooltip)] = + "Descargar los recursos referenciados por la exportación (avatares, archivos adjuntos, imágenes incrustadas, etc.)", + [nameof(ReuseAssetsLabel)] = "Reutilizar recursos", + [nameof(ReuseAssetsTooltip)] = + "Reutilizar recursos previamente descargados para evitar solicitudes redundantes", + [nameof(AssetsDirPathLabel)] = "Ruta del directorio de recursos", + [nameof(AssetsDirPathTooltip)] = + "Descargar recursos en este directorio. Si no se especifica, la ruta se derivará de la ruta de salida.", + [nameof(AdvancedOptionsTooltip)] = "Alternar opciones avanzadas", + [nameof(ExportButton)] = "EXPORTAR", + // Common buttons + [nameof(CloseButton)] = "CERRAR", + [nameof(CancelButton)] = "CANCELAR", + // Dialog messages + [nameof(UkraineSupportTitle)] = "¡Gracias por apoyar a Ucrania!", + [nameof(UkraineSupportMessage)] = """ + Mientras Rusia libra una guerra genocida contra mi país, estoy agradecido con todos los que continúan apoyando a Ucrania en nuestra lucha por la libertad. + + Haga clic en MÁS INFORMACIÓN para encontrar formas de ayudar. + """, + [nameof(LearnMoreButton)] = "MÁS INFORMACIÓN", + [nameof(UnstableBuildTitle)] = "Advertencia de versión inestable", + [nameof(UnstableBuildMessage)] = """ + Está usando una versión de desarrollo de {0}. Estas versiones no han sido probadas exhaustivamente y pueden contener errores. + + Las actualizaciones automáticas están desactivadas para las versiones de desarrollo. + + Haga clic en VER VERSIONES si desea descargar una versión estable. + """, + [nameof(SeeReleasesButton)] = "VER VERSIONES", + [nameof(UpdateDownloadingMessage)] = "Descargando actualización a {0} v{1}...", + [nameof(UpdateReadyMessage)] = + "La actualización se ha descargado y se instalará al salir", + [nameof(UpdateInstallNowButton)] = "INSTALAR AHORA", + [nameof(UpdateFailedMessage)] = "Error al realizar la actualización de la aplicación", + [nameof(ErrorPullingGuildsTitle)] = "Error al cargar servidores", + [nameof(ErrorPullingChannelsTitle)] = "Error al cargar canales", + [nameof(ErrorExportingTitle)] = "Error al exportar canal(es)", + [nameof(SuccessfulExportMessage)] = "{0} canal(es) exportado(s) con éxito", + }; +} diff --git a/DiscordChatExporter.Gui/Localization/LocalizationManager.Ukrainian.cs b/DiscordChatExporter.Gui/Localization/LocalizationManager.Ukrainian.cs new file mode 100644 index 00000000..6b2b1cbb --- /dev/null +++ b/DiscordChatExporter.Gui/Localization/LocalizationManager.Ukrainian.cs @@ -0,0 +1,155 @@ +using System.Collections.Generic; + +namespace DiscordChatExporter.Gui.Localization; + +public partial class LocalizationManager +{ + private static readonly IReadOnlyDictionary UkrainianLocalization = + new Dictionary + { + // Dashboard + [nameof(PullGuildsTooltip)] = "Завантажити доступні сервери та канали (Enter)", + [nameof(SettingsTooltip)] = "Налаштування", + [nameof(LastMessageSentTooltip)] = "Останнє повідомлення:", + [nameof(TokenPlaceholderText)] = "Токен", + // Token instructions (personal account) + [nameof(TokenPersonalHeader)] = "Як отримати токен для персонального акаунту:", + [nameof(TokenPersonalTosWarning)] = + "* Автоматизація облікових записів технічно порушує Умови обслуговування — **на власний ризик**!", + [nameof(TokenPersonalInstructions)] = """ + 1. Відкрийте Discord у вашому веб-браузері та увійдіть + 2. Відкрийте будь-який сервер або канал особистих повідомлень + 3. Натисніть **Ctrl+Shift+I**, щоб відкрити інструменти розробника + 4. Перейдіть на вкладку **Network** + 5. Натисніть **Ctrl+R** для перезавантаження + 6. Перемикайтеся між каналами, щоб викликати мережеві запити + 7. Знайдіть запит, що починається з **messages** + 8. Виберіть вкладку **Headers** праворуч + 9. Прокрутіть до розділу **Request Headers** + 10. Скопіюйте значення заголовка **authorization** + """, + // Token instructions (bot) + [nameof(TokenBotHeader)] = "Як отримати токен для бота:", + [nameof(TokenBotInstructions)] = """ + Токен генерується під час створення бота. Якщо ви його втратили, згенеруйте новий: + + 1. Відкрийте Discord [портал розробника](https://discord.com/developers/applications) + 2. Відкрийте налаштування вашого застосунку + 3. Перейдіть до розділу **Bot** ліворуч + 4. В розділі **Token** натисніть **Reset Token** + 5. Натисніть **Yes, do it!** та підтвердьте + * Інтеграції, що використовують попередній токен, перестануть працювати + * Ваш бот повинен мати включений **Message Content Intent** для читання повідомлень + """, + [nameof(TokenHelpText)] = + "Якщо у вас є запитання або проблеми, зверніться до [документації](https://github.com/Tyrrrz/DiscordChatExporter/tree/prime/.docs)", + // Settings + [nameof(SettingsTitle)] = "Налаштування", + [nameof(ThemeLabel)] = "Тема", + [nameof(ThemeTooltip)] = "Бажана тема інтерфейсу", + [nameof(LanguageLabel)] = "Мова", + [nameof(LanguageTooltip)] = "Бажана мова інтерфейсу", + [nameof(AutoUpdateLabel)] = "Авто-оновлення", + [nameof(AutoUpdateTooltip)] = "Виконувати автоматичні оновлення при кожному запуску", + [nameof(PersistTokenLabel)] = "Зберігати токен", + [nameof(PersistTokenTooltip)] = """ + Зберігати останній використаний токен у файлі для збереження між сеансами. + **Увага**: хоча токен зберігається із шифруванням, він може бути відновлений зловмисником, який має доступ до вашої системи. + """, + [nameof(RateLimitPreferenceLabel)] = "Ліміт запитів", + [nameof(RateLimitPreferenceTooltip)] = + "Чи дотримуватись рекомендованих лімітів запитів. Якщо вимкнено, будуть дотримуватись лише жорсткі ліміти (тобто відповіді 429).", + [nameof(ShowThreadsLabel)] = "Показувати гілки", + [nameof(ShowThreadsTooltip)] = "Які типи гілок показувати у списку каналів", + [nameof(LocaleLabel)] = "Локаль", + [nameof(LocaleTooltip)] = "Локаль для форматування дат та чисел", + [nameof(NormalizeToUtcLabel)] = "Нормалізувати до UTC", + [nameof(NormalizeToUtcTooltip)] = "Нормалізувати всі часові мітки до UTC+0", + [nameof(ParallelLimitLabel)] = "Ліміт паралелізації", + [nameof(ParallelLimitTooltip)] = "Скільки каналів може експортуватись одночасно", + // Export Setup + [nameof(ChannelsSelectedText)] = "каналів вибрано", + [nameof(OutputPathLabel)] = "Шлях збереження", + [nameof(OutputPathTooltip)] = """ + Шлях до файлу або директорії виводу. + + Якщо вказано директорію, імена файлів генеруватимуться автоматично на основі назв каналів та параметрів експорту. + + Шляхи до директорій повинні закінчуватись слешем для уникнення неоднозначності. + + Доступні шаблонні токени: + **%g** — ID сервера + **%G** — назва сервера + **%t** — ID категорії + **%T** — назва категорії + **%c** — ID каналу + **%C** — назва каналу + **%p** — позиція каналу + **%P** — позиція категорії + **%a** — дата після + **%b** — дата до + **%d** — поточна дата + """, + [nameof(FormatLabel)] = "Формат", + [nameof(FormatTooltip)] = "Формат експорту", + [nameof(AfterDateLabel)] = "Після (дата)", + [nameof(AfterDateTooltip)] = "Включати лише повідомлення, надіслані після цієї дати", + [nameof(BeforeDateLabel)] = "До (дата)", + [nameof(BeforeDateTooltip)] = "Включати лише повідомлення, надіслані до цієї дати", + [nameof(AfterTimeLabel)] = "Після (час)", + [nameof(AfterTimeTooltip)] = "Включати лише повідомлення, надіслані після цього часу", + [nameof(BeforeTimeLabel)] = "До (час)", + [nameof(BeforeTimeTooltip)] = "Включати лише повідомлення, надіслані до цього часу", + [nameof(PartitionLimitLabel)] = "Розділяти експорт", + [nameof(PartitionLimitTooltip)] = + "Розділити вивід на частини, кожна обмежена вказаною кількістю повідомлень (напр. '100') або розміром файлу (напр. '10mb')", + [nameof(MessageFilterLabel)] = "Фільтр повідомлень", + [nameof(MessageFilterTooltip)] = + "Включати лише повідомлення, що відповідають цьому фільтру (напр. 'from:foo#1234' або 'has:image'). Дивіться документацію для більш детальної інформації.", + [nameof(ReverseMessageOrderLabel)] = "Зворотній порядок повідомлень", + [nameof(ReverseMessageOrderTooltip)] = + "Експортувати повідомлення у зворотному хронологічному порядку (найновіші спочатку)", + [nameof(FormatMarkdownLabel)] = "Форматувати markdown", + [nameof(FormatMarkdownTooltip)] = + "Обробляти markdown, згадки та інші спеціальні токени", + [nameof(DownloadAssetsLabel)] = "Завантажувати ресурси", + [nameof(DownloadAssetsTooltip)] = + "Завантажувати ресурси, на які посилається експорт (аватари, вкладені файли, вбудовані зображення тощо)", + [nameof(ReuseAssetsLabel)] = "Повторно використовувати ресурси", + [nameof(ReuseAssetsTooltip)] = + "Повторно використовувати раніше завантажені ресурси, щоб уникнути зайвих запитів", + [nameof(AssetsDirPathLabel)] = "Шлях до директорії ресурсів", + [nameof(AssetsDirPathTooltip)] = + "Завантажувати ресурси до цієї директорії. Якщо не вказано, шлях до директорії ресурсів буде визначено з шляху збереження.", + [nameof(AdvancedOptionsTooltip)] = "Перемкнути розширені параметри", + [nameof(ExportButton)] = "ЕКСПОРТУВАТИ", + // Common buttons + [nameof(CloseButton)] = "ЗАКРИТИ", + [nameof(CancelButton)] = "СКАСУВАТИ", + // Dialog messages + [nameof(UkraineSupportTitle)] = "Дякуємо за підтримку України!", + [nameof(UkraineSupportMessage)] = """ + Поки Росія веде геноцидну війну проти моєї країни, я вдячний кожному, хто продовжує підтримувати Україну у нашій боротьбі за свободу. + + Натисніть ДІЗНАТИСЬ БІЛЬШЕ, щоб знайти способи допомогти. + """, + [nameof(LearnMoreButton)] = "ДІЗНАТИСЬ БІЛЬШЕ", + [nameof(UnstableBuildTitle)] = "Попередження про нестабільну збірку", + [nameof(UnstableBuildMessage)] = """ + Ви використовуєте збірку розробки {0}. Ці збірки не пройшли ретельного тестування та можуть містити помилки. + + Авто-оновлення вимкнено для збірок розробки. + + Натисніть ПЕРЕГЛЯНУТИ РЕЛІЗИ, щоб завантажити стабільний реліз. + """, + [nameof(SeeReleasesButton)] = "ПЕРЕГЛЯНУТИ РЕЛІЗИ", + [nameof(UpdateDownloadingMessage)] = "Завантаження оновлення {0} v{1}...", + [nameof(UpdateReadyMessage)] = "Оновлення завантажено та буде встановлено після виходу", + [nameof(UpdateInstallNowButton)] = "ВСТАНОВИТИ ЗАРАЗ", + [nameof(UpdateFailedMessage)] = "Не вдалося виконати оновлення програми", + [nameof(ErrorPullingGuildsTitle)] = "Помилка завантаження серверів", + [nameof(ErrorPullingChannelsTitle)] = "Помилка завантаження каналів", + [nameof(ErrorExportingTitle)] = "Помилка експорту каналу(-ів)", + [nameof(SuccessfulExportMessage)] = "Успішно експортовано {0} канал(-ів)", + }; +} diff --git a/DiscordChatExporter.Gui/Localization/LocalizationManager.cs b/DiscordChatExporter.Gui/Localization/LocalizationManager.cs new file mode 100644 index 00000000..8a386eae --- /dev/null +++ b/DiscordChatExporter.Gui/Localization/LocalizationManager.cs @@ -0,0 +1,165 @@ +using System; +using System.Globalization; +using System.Runtime.CompilerServices; +using CommunityToolkit.Mvvm.ComponentModel; +using DiscordChatExporter.Gui.Services; +using PowerKit; +using PowerKit.Extensions; + +namespace DiscordChatExporter.Gui.Localization; + +public partial class LocalizationManager : ObservableObject, IDisposable +{ + private readonly IDisposable _eventSubscription; + + public LocalizationManager(SettingsService settingsService) + { + _eventSubscription = Disposable.Merge( + settingsService.WatchProperty(o => o.Language, v => Language = v, true), + this.WatchProperty( + o => o.Language, + _ => + { + foreach (var propertyName in EnglishLocalization.Keys) + OnPropertyChanged(propertyName); + } + ) + ); + } + + [ObservableProperty] + public partial Language Language { get; set; } = Language.System; + + private string Get([CallerMemberName] string? key = null) + { + if (string.IsNullOrWhiteSpace(key)) + return string.Empty; + + var localization = Language switch + { + Language.System => + CultureInfo.CurrentUICulture.ThreeLetterISOLanguageName.ToLowerInvariant() switch + { + "ukr" => UkrainianLocalization, + "deu" => GermanLocalization, + "fra" => FrenchLocalization, + "spa" => SpanishLocalization, + _ => EnglishLocalization, + }, + Language.Ukrainian => UkrainianLocalization, + Language.German => GermanLocalization, + Language.French => FrenchLocalization, + Language.Spanish => SpanishLocalization, + _ => EnglishLocalization, + }; + + if ( + localization.TryGetValue(key, out var value) + // English is used as a fallback + || EnglishLocalization.TryGetValue(key, out value) + ) + { + return value; + } + + return $"Missing localization for '{key}'"; + } + + public void Dispose() => _eventSubscription.Dispose(); +} + +public partial class LocalizationManager +{ + // ---- Dashboard ---- + + public string PullGuildsTooltip => Get(); + public string SettingsTooltip => Get(); + public string LastMessageSentTooltip => Get(); + public string TokenPlaceholderText => Get(); + + // Token instructions (personal account) + public string TokenPersonalHeader => Get(); + public string TokenPersonalTosWarning => Get(); + public string TokenPersonalInstructions => Get(); + + // Token instructions (bot) + public string TokenBotHeader => Get(); + public string TokenBotInstructions => Get(); + public string TokenHelpText => Get(); + + // ---- Settings ---- + + public string SettingsTitle => Get(); + public string ThemeLabel => Get(); + public string ThemeTooltip => Get(); + public string LanguageLabel => Get(); + public string LanguageTooltip => Get(); + public string AutoUpdateLabel => Get(); + public string AutoUpdateTooltip => Get(); + public string PersistTokenLabel => Get(); + public string PersistTokenTooltip => Get(); + public string RateLimitPreferenceLabel => Get(); + public string RateLimitPreferenceTooltip => Get(); + public string ShowThreadsLabel => Get(); + public string ShowThreadsTooltip => Get(); + public string LocaleLabel => Get(); + public string LocaleTooltip => Get(); + public string NormalizeToUtcLabel => Get(); + public string NormalizeToUtcTooltip => Get(); + public string ParallelLimitLabel => Get(); + public string ParallelLimitTooltip => Get(); + + // ---- Export Setup ---- + + public string ChannelsSelectedText => Get(); + public string OutputPathLabel => Get(); + public string OutputPathTooltip => Get(); + public string FormatLabel => Get(); + public string FormatTooltip => Get(); + public string AfterDateLabel => Get(); + public string AfterDateTooltip => Get(); + public string BeforeDateLabel => Get(); + public string BeforeDateTooltip => Get(); + public string AfterTimeLabel => Get(); + public string AfterTimeTooltip => Get(); + public string BeforeTimeLabel => Get(); + public string BeforeTimeTooltip => Get(); + public string PartitionLimitLabel => Get(); + public string PartitionLimitTooltip => Get(); + public string MessageFilterLabel => Get(); + public string MessageFilterTooltip => Get(); + public string ReverseMessageOrderLabel => Get(); + public string ReverseMessageOrderTooltip => Get(); + public string FormatMarkdownLabel => Get(); + public string FormatMarkdownTooltip => Get(); + public string DownloadAssetsLabel => Get(); + public string DownloadAssetsTooltip => Get(); + public string ReuseAssetsLabel => Get(); + public string ReuseAssetsTooltip => Get(); + public string AssetsDirPathLabel => Get(); + public string AssetsDirPathTooltip => Get(); + public string AdvancedOptionsTooltip => Get(); + public string ExportButton => Get(); + + // ---- Common buttons ---- + + public string CloseButton => Get(); + public string CancelButton => Get(); + + // ---- Dialog messages ---- + + public string UkraineSupportTitle => Get(); + public string UkraineSupportMessage => Get(); + public string LearnMoreButton => Get(); + public string UnstableBuildTitle => Get(); + public string UnstableBuildMessage => Get(); + public string SeeReleasesButton => Get(); + public string UpdateDownloadingMessage => Get(); + public string UpdateReadyMessage => Get(); + public string UpdateInstallNowButton => Get(); + public string UpdateFailedMessage => Get(); + public string ErrorPullingGuildsTitle => Get(); + public string ErrorPullingChannelsTitle => Get(); + public string ErrorExportingTitle => Get(); + public string SuccessfulExportMessage => Get(); +} diff --git a/DiscordChatExporter.Gui/Models/ThreadInclusionMode.cs b/DiscordChatExporter.Gui/Models/ThreadInclusionMode.cs index 41e21628..4a7677a3 100644 --- a/DiscordChatExporter.Gui/Models/ThreadInclusionMode.cs +++ b/DiscordChatExporter.Gui/Models/ThreadInclusionMode.cs @@ -1,8 +1,8 @@ -namespace DiscordChatExporter.Gui.Models; +namespace DiscordChatExporter.Gui.Models; public enum ThreadInclusionMode { None, Active, - All + All, } diff --git a/DiscordChatExporter.Gui/Program.cs b/DiscordChatExporter.Gui/Program.cs new file mode 100644 index 00000000..c484ae5f --- /dev/null +++ b/DiscordChatExporter.Gui/Program.cs @@ -0,0 +1,53 @@ +using System; +using System.Reflection; +using Avalonia; +using DiscordChatExporter.Gui.Utils; + +namespace DiscordChatExporter.Gui; + +public static class Program +{ + private static Assembly Assembly { get; } = Assembly.GetExecutingAssembly(); + + public static string Name { get; } = Assembly.GetName().Name ?? "DiscordChatExporter"; + + public static Version Version { get; } = Assembly.GetName().Version ?? new Version(0, 0, 0); + + public static string VersionString { get; } = Version.ToString(3); + + public static bool IsDevelopmentBuild { get; } = Version.Major is <= 0 or >= 999; + + public static string ProjectUrl { get; } = "https://github.com/Tyrrrz/DiscordChatExporter"; + + public static string ProjectReleasesUrl { get; } = $"{ProjectUrl}/releases"; + + public static string ProjectDocumentationUrl { get; } = ProjectUrl + "/tree/prime/.docs"; + + public static AppBuilder BuildAvaloniaApp() => + AppBuilder.Configure().UsePlatformDetect().LogToTrace(); + + [STAThread] + public static int Main(string[] args) + { + // Build and run the app + var builder = BuildAvaloniaApp(); + + try + { + return builder.StartWithClassicDesktopLifetime(args); + } + catch (Exception ex) + { + if (OperatingSystem.IsWindows()) + _ = NativeMethods.Windows.MessageBox(0, ex.ToString(), "Fatal Error", 0x10); + + throw; + } + finally + { + // Clean up after application shutdown + if (builder.Instance is IDisposable disposableApp) + disposableApp.Dispose(); + } + } +} diff --git a/DiscordChatExporter.Gui/PublishMacOSBundle.csx b/DiscordChatExporter.Gui/PublishMacOSBundle.csx new file mode 100755 index 00000000..46b0e426 --- /dev/null +++ b/DiscordChatExporter.Gui/PublishMacOSBundle.csx @@ -0,0 +1,123 @@ +#!/usr/bin/dotnet -- +#:package CliFx + +using CliFx; +using CliFx.Binding; +using CliFx.Infrastructure; + +[Command(Description = "Publishes the GUI app as a macOS .app bundle.")] +public partial class PublishMacOSBundleCommand : ICommand +{ + private const string BundleName = "DiscordChatExporter.app"; + private const string AppName = "DiscordChatExporter"; + private const string AppCopyright = "© Oleksii Holub"; + private const string AppIdentifier = "me.Tyrrrz.DiscordChatExporter"; + private const string AppSpokenName = "Discord Chat Exporter"; + private const string AppIconName = "AppIcon"; + + [CommandOption("publish-dir", Description = "Path to the publish output directory.")] + public required string PublishDirPath { get; set; } + + [CommandOption("icons-file", Description = "Path to the .icns icons file.")] + public required string IconsFilePath { get; set; } + + [CommandOption("full-version", Description = "Full version string (e.g. '1.2.3.4').")] + public required string FullVersion { get; set; } + + [CommandOption("short-version", Description = "Short version string (e.g. '1.2.3').")] + public required string ShortVersion { get; set; } + + public async ValueTask ExecuteAsync(IConsole console) + { + // Set up paths + var publishDirPath = Path.GetFullPath(PublishDirPath); + var tempDirPath = Path.GetFullPath( + Path.Combine(publishDirPath, "../publish-macos-app-temp") + ); + + // Ensure the temporary directory is clean before use in case a previous run crashed + if (Directory.Exists(tempDirPath)) + Directory.Delete(tempDirPath, true); + + var bundleDirPath = Path.Combine(tempDirPath, BundleName); + var contentsDirPath = Path.Combine(bundleDirPath, "Contents"); + + try + { + // Copy icons into the .app's Resources folder + Directory.CreateDirectory(Path.Combine(contentsDirPath, "Resources")); + File.Copy( + IconsFilePath, + Path.Combine(contentsDirPath, "Resources", "AppIcon.icns"), + true + ); + + // Generate the Info.plist metadata file with the app information + // lang=xml + var plistContent = $""" + + + + + CFBundleDisplayName + {AppName} + CFBundleName + {AppName} + CFBundleExecutable + {AppName} + NSHumanReadableCopyright + {AppCopyright} + CFBundleIdentifier + {AppIdentifier} + CFBundleSpokenName + {AppSpokenName} + CFBundleIconFile + {AppIconName} + CFBundleIconName + {AppIconName} + CFBundleVersion + {FullVersion} + CFBundleShortVersionString + {ShortVersion} + NSHighResolutionCapable + + CFBundlePackageType + APPL + + + """; + + await File.WriteAllTextAsync(Path.Combine(contentsDirPath, "Info.plist"), plistContent); + + // Delete the previous bundle if it exists + var existingBundlePath = Path.Combine(publishDirPath, BundleName); + if (Directory.Exists(existingBundlePath)) + Directory.Delete(existingBundlePath, true); + + // Move all files from the publish directory into the MacOS directory + Directory.CreateDirectory(Path.Combine(contentsDirPath, "MacOS")); + foreach (var entryPath in Directory.GetFileSystemEntries(publishDirPath)) + { + var destinationPath = Path.Combine( + contentsDirPath, + "MacOS", + Path.GetFileName(entryPath) + ); + + if (Directory.Exists(entryPath)) + Directory.Move(entryPath, destinationPath); + else + File.Move(entryPath, destinationPath); + } + + // Move the final bundle into the publish directory for upload + Directory.Move(bundleDirPath, Path.Combine(publishDirPath, BundleName)); + } + finally + { + // Clean up the temporary directory + if (Directory.Exists(tempDirPath)) + Directory.Delete(tempDirPath, true); + } + } +} diff --git a/DiscordChatExporter.Gui/Services/SettingsService.TokenEncryptionConverter.cs b/DiscordChatExporter.Gui/Services/SettingsService.TokenEncryptionConverter.cs new file mode 100644 index 00000000..979538c2 --- /dev/null +++ b/DiscordChatExporter.Gui/Services/SettingsService.TokenEncryptionConverter.cs @@ -0,0 +1,102 @@ +using System; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using DiscordChatExporter.Gui.Utils.Extensions; + +namespace DiscordChatExporter.Gui.Services; + +public partial class SettingsService +{ + private class TokenEncryptionConverter : JsonConverter + { + private const string Prefix = "enc:"; + + private static readonly Lazy Key = new(() => + Rfc2898DeriveBytes.Pbkdf2( + Encoding.UTF8.GetBytes(Environment.TryGetMachineId() ?? string.Empty), + Encoding.UTF8.GetBytes(ThisAssembly.Project.EncryptionSalt), + 600_000, + HashAlgorithmName.SHA256, + 16 + ) + ); + + public override string? Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options + ) + { + var value = reader.GetString(); + + // No prefix means the token is stored as plain text, which was the case for older + // versions of the application. Load it as is and encrypt it on next save. + if ( + string.IsNullOrWhiteSpace(value) + || !value.StartsWith(Prefix, StringComparison.Ordinal) + ) + { + return value; + } + + try + { + var encryptedData = Convert.FromHexString(value[Prefix.Length..]); + var tokenData = new byte[encryptedData.AsSpan(28).Length]; + + // Layout: nonce (12 bytes) | tag (16 bytes) | cipher + using var aes = new AesGcm(Key.Value, 16); + aes.Decrypt( + encryptedData.AsSpan(0, 12), + encryptedData.AsSpan(28), + encryptedData.AsSpan(12, 16), + tokenData + ); + + return Encoding.UTF8.GetString(tokenData); + } + catch (Exception ex) + when (ex + is FormatException + or CryptographicException + or ArgumentException + or IndexOutOfRangeException + ) + { + return null; + } + } + + public override void Write( + Utf8JsonWriter writer, + string? value, + JsonSerializerOptions options + ) + { + if (string.IsNullOrWhiteSpace(value)) + { + writer.WriteNullValue(); + return; + } + + var tokenData = Encoding.UTF8.GetBytes(value); + var encryptedData = new byte[28 + tokenData.Length]; + + // Nonce + RandomNumberGenerator.Fill(encryptedData.AsSpan(0, 12)); + + // Layout: nonce (12 bytes) | tag (16 bytes) | cipher + using var aes = new AesGcm(Key.Value, 16); + aes.Encrypt( + encryptedData.AsSpan(0, 12), + tokenData, + encryptedData.AsSpan(28), + encryptedData.AsSpan(12, 16) + ); + + writer.WriteStringValue(Prefix + Convert.ToHexStringLower(encryptedData)); + } + } +} diff --git a/DiscordChatExporter.Gui/Services/SettingsService.cs b/DiscordChatExporter.Gui/Services/SettingsService.cs index 58310ef1..61240b54 100644 --- a/DiscordChatExporter.Gui/Services/SettingsService.cs +++ b/DiscordChatExporter.Gui/Services/SettingsService.cs @@ -1,48 +1,76 @@ -using System; -using System.IO; +using System.Text.Json.Serialization; using Cogwheel; +using CommunityToolkit.Mvvm.ComponentModel; +using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Exporting; +using DiscordChatExporter.Gui.Framework; +using DiscordChatExporter.Gui.Localization; using DiscordChatExporter.Gui.Models; -using Microsoft.Win32; namespace DiscordChatExporter.Gui.Services; +[ObservableObject] public partial class SettingsService() - : SettingsBase(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Settings.dat")) + : SettingsBase(StartOptions.Current.SettingsPath, SerializerContext.Default) { - public bool IsUkraineSupportMessageEnabled { get; set; } = true; + [ObservableProperty] + public partial bool IsUkraineSupportMessageEnabled { get; set; } = true; - public bool IsAutoUpdateEnabled { get; set; } = true; + [ObservableProperty] + public partial ThemeVariant Theme { get; set; } - public bool IsDarkModeEnabled { get; set; } = IsDarkModeEnabledByDefault(); + [ObservableProperty] + public partial Language Language { get; set; } - public bool IsTokenPersisted { get; set; } = true; + [ObservableProperty] + public partial bool IsAutoUpdateEnabled { get; set; } = true; - public ThreadInclusionMode ThreadInclusionMode { get; set; } = ThreadInclusionMode.None; + [ObservableProperty] + public partial bool IsTokenPersisted { get; set; } = true; - public string? Locale { get; set; } + [ObservableProperty] + public partial RateLimitPreference RateLimitPreference { get; set; } = + RateLimitPreference.RespectAll; - public bool IsUtcNormalizationEnabled { get; set; } + [ObservableProperty] + public partial ThreadInclusionMode ThreadInclusionMode { get; set; } - public int ParallelLimit { get; set; } = 1; + [ObservableProperty] + public partial string? Locale { get; set; } - public Version? LastAppVersion { get; set; } + [ObservableProperty] + public partial bool IsUtcNormalizationEnabled { get; set; } - public string? LastToken { get; set; } + [ObservableProperty] + public partial int ParallelLimit { get; set; } = 1; - public ExportFormat LastExportFormat { get; set; } = ExportFormat.HtmlDark; + [ObservableProperty] + [JsonConverter(typeof(TokenEncryptionConverter))] + public partial string? LastToken { get; set; } - public string? LastPartitionLimitValue { get; set; } + [ObservableProperty] + public partial ExportFormat LastExportFormat { get; set; } = ExportFormat.HtmlDark; - public string? LastMessageFilterValue { get; set; } + [ObservableProperty] + public partial string? LastPartitionLimitValue { get; set; } - public bool LastShouldFormatMarkdown { get; set; } = true; + [ObservableProperty] + public partial string? LastMessageFilterValue { get; set; } - public bool LastShouldDownloadAssets { get; set; } + [ObservableProperty] + public partial bool LastIsReverseMessageOrder { get; set; } - public bool LastShouldReuseAssets { get; set; } + [ObservableProperty] + public partial bool LastShouldFormatMarkdown { get; set; } = true; - public string? LastAssetsDirPath { get; set; } + [ObservableProperty] + public partial bool LastShouldDownloadAssets { get; set; } + + [ObservableProperty] + public partial bool LastShouldReuseAssets { get; set; } + + [ObservableProperty] + public partial string? LastAssetsDirPath { get; set; } public override void Save() { @@ -59,21 +87,6 @@ public partial class SettingsService() public partial class SettingsService { - private static bool IsDarkModeEnabledByDefault() - { - try - { - return Registry - .CurrentUser.OpenSubKey( - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", - false - ) - ?.GetValue("AppsUseLightTheme") - is 0; - } - catch - { - return false; - } - } + [JsonSerializable(typeof(SettingsService))] + private partial class SerializerContext : JsonSerializerContext; } diff --git a/DiscordChatExporter.Gui/Services/UpdateService.cs b/DiscordChatExporter.Gui/Services/UpdateService.cs index 738ba216..12857d72 100644 --- a/DiscordChatExporter.Gui/Services/UpdateService.cs +++ b/DiscordChatExporter.Gui/Services/UpdateService.cs @@ -1,4 +1,5 @@ -using System; +using System; +using System.Runtime.InteropServices; using System.Threading.Tasks; using Onova; using Onova.Exceptions; @@ -8,17 +9,31 @@ namespace DiscordChatExporter.Gui.Services; public class UpdateService(SettingsService settingsService) : IDisposable { - private readonly IUpdateManager _updateManager = new UpdateManager( - new GithubPackageResolver("Tyrrrz", "DiscordChatExporter", "DiscordChatExporter.zip"), - new ZipPackageExtractor() - ); + private readonly IUpdateManager? _updateManager = + OperatingSystem.IsWindows() && StartOptions.Current.IsAutoUpdateAllowed + ? new UpdateManager( + new GithubPackageResolver( + "Tyrrrz", + "DiscordChatExporter", + // Examples: + // DiscordChatExporter.win-arm64.zip + // DiscordChatExporter.win-x64.zip + // DiscordChatExporter.linux-x64.zip + $"DiscordChatExporter.{RuntimeInformation.RuntimeIdentifier}.zip" + ), + new ZipPackageExtractor() + ) + : null; private Version? _updateVersion; - private bool _updatePrepared; - private bool _updaterLaunched; + private bool _isUpdatePrepared; + private bool _isUpdaterLaunched; public async ValueTask CheckForUpdatesAsync() { + if (_updateManager is null) + return null; + if (!settingsService.IsAutoUpdateEnabled) return null; @@ -28,13 +43,16 @@ public class UpdateService(SettingsService settingsService) : IDisposable public async ValueTask PrepareUpdateAsync(Version version) { + if (_updateManager is null) + return; + if (!settingsService.IsAutoUpdateEnabled) return; try { await _updateManager.PrepareUpdateAsync(_updateVersion = version); - _updatePrepared = true; + _isUpdatePrepared = true; } catch (UpdaterAlreadyLaunchedException) { @@ -48,16 +66,19 @@ public class UpdateService(SettingsService settingsService) : IDisposable public void FinalizeUpdate(bool needRestart) { + if (_updateManager is null) + return; + if (!settingsService.IsAutoUpdateEnabled) return; - if (_updateVersion is null || !_updatePrepared || _updaterLaunched) + if (_updateVersion is null || !_isUpdatePrepared || _isUpdaterLaunched) return; try { _updateManager.LaunchUpdater(_updateVersion, needRestart); - _updaterLaunched = true; + _isUpdaterLaunched = true; } catch (UpdaterAlreadyLaunchedException) { @@ -69,5 +90,5 @@ public class UpdateService(SettingsService settingsService) : IDisposable } } - public void Dispose() => _updateManager.Dispose(); + public void Dispose() => _updateManager?.Dispose(); } diff --git a/DiscordChatExporter.Gui/StartOptions.cs b/DiscordChatExporter.Gui/StartOptions.cs new file mode 100644 index 00000000..d17752a8 --- /dev/null +++ b/DiscordChatExporter.Gui/StartOptions.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; + +namespace DiscordChatExporter.Gui; + +public partial class StartOptions +{ + public required string SettingsPath { get; init; } + + public required bool IsAutoUpdateAllowed { get; init; } +} + +public partial class StartOptions +{ + public static StartOptions Current { get; } = + new() + { + SettingsPath = + Environment.GetEnvironmentVariable("DISCORDCHATEXPORTER_SETTINGS_PATH") is { } path + && !string.IsNullOrWhiteSpace(path) + ? Path.EndsInDirectorySeparator(path) || Directory.Exists(path) + ? Path.Combine(path, "Settings.dat") + : path + : Path.Combine(AppContext.BaseDirectory, "Settings.dat"), + IsAutoUpdateAllowed = !( + Environment.GetEnvironmentVariable("DISCORDCHATEXPORTER_ALLOW_AUTO_UPDATE") + is { } env + && env.Equals("false", StringComparison.OrdinalIgnoreCase) + ), + }; +} diff --git a/DiscordChatExporter.Gui/Utils/Extensions/AvaloniaExtensions.cs b/DiscordChatExporter.Gui/Utils/Extensions/AvaloniaExtensions.cs new file mode 100644 index 00000000..f89ffd63 --- /dev/null +++ b/DiscordChatExporter.Gui/Utils/Extensions/AvaloniaExtensions.cs @@ -0,0 +1,40 @@ +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using PowerKit.Extensions; + +namespace DiscordChatExporter.Gui.Utils.Extensions; + +internal static class AvaloniaExtensions +{ + extension(IApplicationLifetime lifetime) + { + public Control? TryGetMainView() => + lifetime switch + { + IClassicDesktopStyleApplicationLifetime desktopLifetime => + desktopLifetime.MainWindow, + + ISingleViewApplicationLifetime singleViewLifetime => singleViewLifetime.MainView, + + _ => null, + }; + + public TopLevel? TryGetTopLevel() => lifetime.TryGetMainView()?.Pipe(TopLevel.GetTopLevel); + + public bool TryShutdown(int exitCode = 0) + { + if (lifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime) + { + return desktopLifetime.TryShutdown(exitCode); + } + + if (lifetime is IControlledApplicationLifetime controlledLifetime) + { + controlledLifetime.Shutdown(exitCode); + return true; + } + + return false; + } + } +} diff --git a/DiscordChatExporter.Gui/Utils/Extensions/EnvironmentExtensions.cs b/DiscordChatExporter.Gui/Utils/Extensions/EnvironmentExtensions.cs new file mode 100644 index 00000000..8390309c --- /dev/null +++ b/DiscordChatExporter.Gui/Utils/Extensions/EnvironmentExtensions.cs @@ -0,0 +1,54 @@ +using System; +using System.IO; + +namespace DiscordChatExporter.Gui.Utils.Extensions; + +internal static class EnvironmentExtensions +{ + extension(Environment) + { + public static string? TryGetMachineId() + { + // Windows: stable GUID written during OS installation + if (OperatingSystem.IsWindows()) + { + try + { + using var regKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey( + @"SOFTWARE\Microsoft\Cryptography" + ); + if ( + regKey?.GetValue("MachineGuid") is string guid + && !string.IsNullOrWhiteSpace(guid) + ) + return guid; + } + catch { } + } + else + { + // Unix: /etc/machine-id (set once by systemd at first boot) + foreach (var path in new[] { "/etc/machine-id", "/var/lib/dbus/machine-id" }) + { + try + { + var id = File.ReadAllText(path).Trim(); + if (!string.IsNullOrWhiteSpace(id)) + return id; + } + catch { } + } + } + + // Last-resort fallback + try + { + return Environment.MachineName; + } + catch + { + return null; + } + } + } +} diff --git a/DiscordChatExporter.Gui/Utils/Extensions/MarkdigExtensions.cs b/DiscordChatExporter.Gui/Utils/Extensions/MarkdigExtensions.cs new file mode 100644 index 00000000..33d40adf --- /dev/null +++ b/DiscordChatExporter.Gui/Utils/Extensions/MarkdigExtensions.cs @@ -0,0 +1,18 @@ +using System.Linq; +using Markdig.Syntax.Inlines; + +namespace DiscordChatExporter.Gui.Utils.Extensions; + +internal static class MarkdigExtensions +{ + extension(Inline inline) + { + public string GetInnerText() => + inline switch + { + LiteralInline literal => literal.Content.ToString(), + ContainerInline container => string.Concat(container.Select(c => c.GetInnerText())), + _ => string.Empty, + }; + } +} diff --git a/DiscordChatExporter.Gui/Utils/Internationalization.cs b/DiscordChatExporter.Gui/Utils/Internationalization.cs index 7567c3d7..910ac183 100644 --- a/DiscordChatExporter.Gui/Utils/Internationalization.cs +++ b/DiscordChatExporter.Gui/Utils/Internationalization.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; namespace DiscordChatExporter.Gui.Utils; @@ -7,4 +7,6 @@ internal static class Internationalization public static bool Is24Hours => string.IsNullOrWhiteSpace(CultureInfo.CurrentCulture.DateTimeFormat.AMDesignator) && string.IsNullOrWhiteSpace(CultureInfo.CurrentCulture.DateTimeFormat.PMDesignator); + + public static string AvaloniaClockIdentifier => Is24Hours ? "24HourClock" : "12HourClock"; } diff --git a/DiscordChatExporter.Gui/Utils/MediaColor.cs b/DiscordChatExporter.Gui/Utils/MediaColor.cs deleted file mode 100644 index 07fb8acb..00000000 --- a/DiscordChatExporter.Gui/Utils/MediaColor.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Windows.Media; - -namespace DiscordChatExporter.Gui.Utils; - -internal static class MediaColor -{ - public static Color FromHex(string hex) => (Color)ColorConverter.ConvertFromString(hex); -} diff --git a/DiscordChatExporter.Gui/Utils/NativeMethods.cs b/DiscordChatExporter.Gui/Utils/NativeMethods.cs new file mode 100644 index 00000000..b2b3697b --- /dev/null +++ b/DiscordChatExporter.Gui/Utils/NativeMethods.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace DiscordChatExporter.Gui.Utils; + +internal static class NativeMethods +{ + public static class Windows + { + [DllImport("user32.dll", SetLastError = true)] + public static extern int MessageBox(nint hWnd, string text, string caption, uint type); + } +} diff --git a/DiscordChatExporter.Gui/Utils/ProcessEx.cs b/DiscordChatExporter.Gui/Utils/ProcessEx.cs deleted file mode 100644 index 100550bc..00000000 --- a/DiscordChatExporter.Gui/Utils/ProcessEx.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Diagnostics; - -namespace DiscordChatExporter.Gui.Utils; - -internal static class ProcessEx -{ - public static void StartShellExecute(string path) - { - using var process = new Process - { - StartInfo = new ProcessStartInfo { FileName = path, UseShellExecute = true } - }; - - process.Start(); - } -} diff --git a/DiscordChatExporter.Gui/ViewModels/Components/DashboardViewModel.cs b/DiscordChatExporter.Gui/ViewModels/Components/DashboardViewModel.cs index cf075d3f..542c9526 100644 --- a/DiscordChatExporter.Gui/ViewModels/Components/DashboardViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/Components/DashboardViewModel.cs @@ -1,104 +1,112 @@ -using System; +using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Threading; using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Exceptions; using DiscordChatExporter.Core.Exporting; -using DiscordChatExporter.Core.Utils.Extensions; +using DiscordChatExporter.Gui.Framework; +using DiscordChatExporter.Gui.Localization; using DiscordChatExporter.Gui.Models; using DiscordChatExporter.Gui.Services; -using DiscordChatExporter.Gui.Utils; -using DiscordChatExporter.Gui.ViewModels.Dialogs; -using DiscordChatExporter.Gui.ViewModels.Framework; -using DiscordChatExporter.Gui.ViewModels.Messages; using Gress; using Gress.Completable; -using Stylet; +using PowerKit; +using PowerKit.Extensions; namespace DiscordChatExporter.Gui.ViewModels.Components; -public class DashboardViewModel : PropertyChangedBase +public partial class DashboardViewModel : ViewModelBase { - private readonly IViewModelFactory _viewModelFactory; - private readonly IEventAggregator _eventAggregator; + private readonly ViewModelManager _viewModelManager; + private readonly SnackbarManager _snackbarManager; private readonly DialogManager _dialogManager; private readonly SettingsService _settingsService; + private readonly IDisposable _eventSubscription; private readonly AutoResetProgressMuxer _progressMuxer; private DiscordClient? _discord; - public bool IsBusy { get; private set; } + public DashboardViewModel( + ViewModelManager viewModelManager, + DialogManager dialogManager, + SnackbarManager snackbarManager, + SettingsService settingsService, + LocalizationManager localizationManager + ) + { + _viewModelManager = viewModelManager; + _dialogManager = dialogManager; + _snackbarManager = snackbarManager; + _settingsService = settingsService; + LocalizationManager = localizationManager; + + _progressMuxer = Progress.CreateMuxer().WithAutoReset(); + + _eventSubscription = Disposable.Merge( + Progress.WatchProperty( + o => o.Current, + _ => OnPropertyChanged(nameof(IsProgressIndeterminate)) + ), + SelectedChannels.WatchProperty( + o => o.Count, + _ => ExportCommand.NotifyCanExecuteChanged() + ) + ); + } + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(IsProgressIndeterminate))] + [NotifyCanExecuteChangedFor(nameof(PullGuildsCommand))] + [NotifyCanExecuteChangedFor(nameof(PullChannelsCommand))] + [NotifyCanExecuteChangedFor(nameof(ExportCommand))] + public partial bool IsBusy { get; set; } + + public LocalizationManager LocalizationManager { get; } public ProgressContainer Progress { get; } = new(); public bool IsProgressIndeterminate => IsBusy && Progress.Current.Fraction is <= 0 or >= 1; - public string? Token { get; set; } + [ObservableProperty] + [NotifyCanExecuteChangedFor(nameof(PullGuildsCommand))] + public partial string? Token { get; set; } - public IReadOnlyList? AvailableGuilds { get; private set; } + [ObservableProperty] + public partial IReadOnlyList? AvailableGuilds { get; set; } - public Guild? SelectedGuild { get; set; } + [ObservableProperty] + [NotifyCanExecuteChangedFor(nameof(PullChannelsCommand))] + [NotifyCanExecuteChangedFor(nameof(ExportCommand))] + public partial Guild? SelectedGuild { get; set; } - public IReadOnlyList? AvailableChannels { get; private set; } + [ObservableProperty] + public partial IReadOnlyList? AvailableChannels { get; set; } - public IReadOnlyList? SelectedChannels { get; set; } + public ObservableCollection SelectedChannels { get; } = []; - public DashboardViewModel( - IViewModelFactory viewModelFactory, - IEventAggregator eventAggregator, - DialogManager dialogManager, - SettingsService settingsService - ) - { - _viewModelFactory = viewModelFactory; - _eventAggregator = eventAggregator; - _dialogManager = dialogManager; - _settingsService = settingsService; - - _progressMuxer = Progress.CreateMuxer().WithAutoReset(); - - this.Bind(o => o.IsBusy, (_, _) => NotifyOfPropertyChange(() => IsProgressIndeterminate)); - - Progress.Bind( - o => o.Current, - (_, _) => NotifyOfPropertyChange(() => IsProgressIndeterminate) - ); - - this.Bind( - o => o.SelectedGuild, - (_, _) => - { - // Reset channels when the selected guild changes, to avoid jitter - // due to the channels being asynchronously loaded. - AvailableChannels = null; - SelectedChannels = null; - - // Pull channels for the selected guild - // (ideally this should be called inside `PullGuilds()`, - // but Stylet doesn't support async commands) - PullChannels(); - } - ); - } - - public void OnViewLoaded() + public override Task InitializeAsync() { if (!string.IsNullOrWhiteSpace(_settingsService.LastToken)) Token = _settingsService.LastToken; + + return Task.CompletedTask; } - public async void ShowSettings() => - await _dialogManager.ShowDialogAsync(_viewModelFactory.CreateSettingsViewModel()); + [RelayCommand] + private async Task ShowSettingsAsync() => + await _dialogManager.ShowDialogAsync(_viewModelManager.GetSettingsViewModel()); - public void ShowHelp() => ProcessEx.StartShellExecute(App.DocumentationUrl); + private bool CanPullGuilds() => !IsBusy && !string.IsNullOrWhiteSpace(Token); - public bool CanPullGuilds => !IsBusy && !string.IsNullOrWhiteSpace(Token); - - public async void PullGuilds() + [RelayCommand(CanExecute = nameof(CanPullGuilds))] + private async Task PullGuildsAsync() { IsBusy = true; var progress = _progressMuxer.CreateInput(); @@ -112,24 +120,26 @@ public class DashboardViewModel : PropertyChangedBase AvailableGuilds = null; SelectedGuild = null; AvailableChannels = null; - SelectedChannels = null; + SelectedChannels.Clear(); - _discord = new DiscordClient(token); + _discord = new DiscordClient(token, _settingsService.RateLimitPreference); _settingsService.LastToken = token; var guilds = await _discord.GetUserGuildsAsync(); AvailableGuilds = guilds; SelectedGuild = guilds.FirstOrDefault(); + + await PullChannelsAsync(); } catch (DiscordChatExporterException ex) when (!ex.IsFatal) { - _eventAggregator.Publish(new NotificationMessage(ex.Message.TrimEnd('.'))); + _snackbarManager.Notify(ex.Message.TrimEnd('.')); } catch (Exception ex) { - var dialog = _viewModelFactory.CreateMessageBoxViewModel( - "Error pulling guilds", + var dialog = _viewModelManager.GetMessageBoxViewModel( + LocalizationManager.ErrorPullingGuildsTitle, ex.ToString() ); @@ -142,9 +152,10 @@ public class DashboardViewModel : PropertyChangedBase } } - public bool CanPullChannels => !IsBusy && _discord is not null && SelectedGuild is not null; + private bool CanPullChannels() => !IsBusy && _discord is not null && SelectedGuild is not null; - public async void PullChannels() + [RelayCommand(CanExecute = nameof(CanPullChannels))] + private async Task PullChannelsAsync() { IsBusy = true; var progress = _progressMuxer.CreateInput(); @@ -155,18 +166,13 @@ public class DashboardViewModel : PropertyChangedBase return; AvailableChannels = null; - SelectedChannels = null; + SelectedChannels.Clear(); var channels = new List(); // Regular channels await foreach (var channel in _discord.GetGuildChannelsAsync(SelectedGuild.Id)) - { - if (channel.IsCategory) - continue; - channels.Add(channel); - } // Threads if (_settingsService.ThreadInclusionMode != ThreadInclusionMode.None) @@ -182,17 +188,25 @@ public class DashboardViewModel : PropertyChangedBase } } - AvailableChannels = channels; - SelectedChannels = null; + // Build a hierarchy of channels + var channelTree = ChannelConnection.BuildTree( + channels + .OrderByDescending(c => c.IsDirect ? c.LastMessageId : null) + .ThenBy(c => c.Position) + .ToArray() + ); + + AvailableChannels = channelTree; + SelectedChannels.Clear(); } catch (DiscordChatExporterException ex) when (!ex.IsFatal) { - _eventAggregator.Publish(new NotificationMessage(ex.Message.TrimEnd('.'))); + _snackbarManager.Notify(ex.Message.TrimEnd('.')); } catch (Exception ex) { - var dialog = _viewModelFactory.CreateMessageBoxViewModel( - "Error pulling channels", + var dialog = _viewModelManager.GetMessageBoxViewModel( + LocalizationManager.ErrorPullingChannelsTitle, ex.ToString() ); @@ -205,30 +219,24 @@ public class DashboardViewModel : PropertyChangedBase } } - public bool CanExport => - !IsBusy - && _discord is not null - && SelectedGuild is not null - && SelectedChannels?.Any() is true; + private bool CanExport() => + !IsBusy && _discord is not null && SelectedGuild is not null && SelectedChannels.Any(); - public async void Export() + [RelayCommand(CanExecute = nameof(CanExport))] + private async Task ExportAsync() { IsBusy = true; try { - if ( - _discord is null - || SelectedGuild is null - || SelectedChannels is null - || !SelectedChannels.Any() - ) + if (_discord is null || SelectedGuild is null || !SelectedChannels.Any()) return; - var dialog = _viewModelFactory.CreateExportSetupViewModel( + var dialog = _viewModelManager.GetExportSetupViewModel( SelectedGuild, - SelectedChannels + SelectedChannels.Select(c => c.Channel).ToArray() ); + if (await _dialogManager.ShowDialogAsync(dialog) != true) return; @@ -244,7 +252,7 @@ public class DashboardViewModel : PropertyChangedBase channelProgressPairs, new ParallelOptions { - MaxDegreeOfParallelism = Math.Max(1, _settingsService.ParallelLimit) + MaxDegreeOfParallelism = Math.Max(1, _settingsService.ParallelLimit), }, async (pair, cancellationToken) => { @@ -263,6 +271,7 @@ public class DashboardViewModel : PropertyChangedBase dialog.Before?.Pipe(Snowflake.FromDate), dialog.PartitionLimit, dialog.MessageFilter, + dialog.IsReverseMessageOrder, dialog.ShouldFormatMarkdown, dialog.ShouldDownloadAssets, dialog.ShouldReuseAssets, @@ -274,9 +283,13 @@ public class DashboardViewModel : PropertyChangedBase Interlocked.Increment(ref successfulExportCount); } + catch (ChannelEmptyException ex) + { + _snackbarManager.Notify(ex.Message.TrimEnd('.')); + } catch (DiscordChatExporterException ex) when (!ex.IsFatal) { - _eventAggregator.Publish(new NotificationMessage(ex.Message.TrimEnd('.'))); + _snackbarManager.Notify(ex.Message.TrimEnd('.')); } finally { @@ -288,17 +301,18 @@ public class DashboardViewModel : PropertyChangedBase // Notify of the overall completion if (successfulExportCount > 0) { - _eventAggregator.Publish( - new NotificationMessage( - $"Successfully exported {successfulExportCount} channel(s)" + _snackbarManager.Notify( + string.Format( + LocalizationManager.SuccessfulExportMessage, + successfulExportCount ) ); } } catch (Exception ex) { - var dialog = _viewModelFactory.CreateMessageBoxViewModel( - "Error exporting channel(s)", + var dialog = _viewModelManager.GetMessageBoxViewModel( + LocalizationManager.ErrorExportingTitle, ex.ToString() ); @@ -310,8 +324,13 @@ public class DashboardViewModel : PropertyChangedBase } } - public void OpenDiscord() => ProcessEx.StartShellExecute("https://discord.com/app"); + protected override void Dispose(bool disposing) + { + if (disposing) + { + _eventSubscription.Dispose(); + } - public void OpenDiscordDeveloperPortal() => - ProcessEx.StartShellExecute("https://discord.com/developers/applications"); + base.Dispose(disposing); + } } diff --git a/DiscordChatExporter.Gui/ViewModels/Dialogs/ExportSetupViewModel.cs b/DiscordChatExporter.Gui/ViewModels/Dialogs/ExportSetupViewModel.cs index 0522e8bf..9f1aed31 100644 --- a/DiscordChatExporter.Gui/ViewModels/Dialogs/ExportSetupViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/Dialogs/ExportSetupViewModel.cs @@ -1,89 +1,118 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; +using Avalonia.Platform.Storage; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Exporting; using DiscordChatExporter.Core.Exporting.Filtering; using DiscordChatExporter.Core.Exporting.Partitioning; -using DiscordChatExporter.Core.Utils.Extensions; +using DiscordChatExporter.Gui.Framework; +using DiscordChatExporter.Gui.Localization; using DiscordChatExporter.Gui.Services; -using DiscordChatExporter.Gui.ViewModels.Framework; +using PowerKit.Extensions; namespace DiscordChatExporter.Gui.ViewModels.Dialogs; -public class ExportSetupViewModel : DialogScreen +public partial class ExportSetupViewModel( + DialogManager dialogManager, + SettingsService settingsService, + LocalizationManager localizationManager +) : DialogViewModelBase { - private readonly DialogManager _dialogManager; - private readonly SettingsService _settingsService; + public LocalizationManager LocalizationManager { get; } = localizationManager; - public Guild? Guild { get; set; } + [ObservableProperty] + public partial Guild? Guild { get; set; } - public IReadOnlyList? Channels { get; set; } + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(IsSingleChannel))] + public partial IReadOnlyList? Channels { get; set; } + + [ObservableProperty] + public partial string? OutputPath { get; set; } + + [ObservableProperty] + public partial ExportFormat SelectedFormat { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(IsAfterDateSet))] + [NotifyPropertyChangedFor(nameof(After))] + public partial DateTimeOffset? AfterDate { get; set; } + + [ObservableProperty] + public partial TimeSpan? AfterTime { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(IsBeforeDateSet))] + [NotifyPropertyChangedFor(nameof(Before))] + public partial DateTimeOffset? BeforeDate { get; set; } + + [ObservableProperty] + public partial TimeSpan? BeforeTime { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(PartitionLimit))] + public partial string? PartitionLimitValue { get; set; } + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(MessageFilter))] + public partial string? MessageFilterValue { get; set; } + + [ObservableProperty] + public partial bool IsReverseMessageOrder { get; set; } + + [ObservableProperty] + public partial bool ShouldFormatMarkdown { get; set; } + + [ObservableProperty] + public partial bool ShouldDownloadAssets { get; set; } + + [ObservableProperty] + public partial bool ShouldReuseAssets { get; set; } + + [ObservableProperty] + public partial string? AssetsDirPath { get; set; } + + [ObservableProperty] + public partial bool IsAdvancedSectionDisplayed { get; set; } public bool IsSingleChannel => Channels?.Count == 1; - public string? OutputPath { get; set; } - public IReadOnlyList AvailableFormats { get; } = Enum.GetValues(); - public ExportFormat SelectedFormat { get; set; } - - // This date/time abomination is required because we use separate controls to set these - - public DateTimeOffset? AfterDate { get; set; } - public bool IsAfterDateSet => AfterDate is not null; - public TimeSpan? AfterTime { get; set; } - public DateTimeOffset? After => AfterDate?.Add(AfterTime ?? TimeSpan.Zero); - public DateTimeOffset? BeforeDate { get; set; } - public bool IsBeforeDateSet => BeforeDate is not null; - public TimeSpan? BeforeTime { get; set; } - public DateTimeOffset? Before => BeforeDate?.Add(BeforeTime ?? TimeSpan.Zero); - public string? PartitionLimitValue { get; set; } - public PartitionLimit PartitionLimit => !string.IsNullOrWhiteSpace(PartitionLimitValue) ? PartitionLimit.Parse(PartitionLimitValue) : PartitionLimit.Null; - public string? MessageFilterValue { get; set; } - public MessageFilter MessageFilter => !string.IsNullOrWhiteSpace(MessageFilterValue) ? MessageFilter.Parse(MessageFilterValue) : MessageFilter.Null; - public bool ShouldFormatMarkdown { get; set; } - - public bool ShouldDownloadAssets { get; set; } - - public bool ShouldReuseAssets { get; set; } - - public string? AssetsDirPath { get; set; } - - public bool IsAdvancedSectionDisplayed { get; set; } - - public ExportSetupViewModel(DialogManager dialogManager, SettingsService settingsService) + public override Task InitializeAsync() { - _dialogManager = dialogManager; - _settingsService = settingsService; - // Persist preferences - SelectedFormat = _settingsService.LastExportFormat; - PartitionLimitValue = _settingsService.LastPartitionLimitValue; - MessageFilterValue = _settingsService.LastMessageFilterValue; - ShouldFormatMarkdown = _settingsService.LastShouldFormatMarkdown; - ShouldDownloadAssets = _settingsService.LastShouldDownloadAssets; - ShouldReuseAssets = _settingsService.LastShouldReuseAssets; - AssetsDirPath = _settingsService.LastAssetsDirPath; + SelectedFormat = settingsService.LastExportFormat; + PartitionLimitValue = settingsService.LastPartitionLimitValue; + MessageFilterValue = settingsService.LastMessageFilterValue; + IsReverseMessageOrder = settingsService.LastIsReverseMessageOrder; + ShouldFormatMarkdown = settingsService.LastShouldFormatMarkdown; + ShouldDownloadAssets = settingsService.LastShouldDownloadAssets; + ShouldReuseAssets = settingsService.LastShouldReuseAssets; + AssetsDirPath = settingsService.LastAssetsDirPath; // Show the "advanced options" section by default if any // of the advanced options are set to non-default values. @@ -94,12 +123,14 @@ public class ExportSetupViewModel : DialogScreen || !string.IsNullOrWhiteSpace(MessageFilterValue) || ShouldDownloadAssets || ShouldReuseAssets - || !string.IsNullOrWhiteSpace(AssetsDirPath); + || !string.IsNullOrWhiteSpace(AssetsDirPath) + || IsReverseMessageOrder; + + return Task.CompletedTask; } - public void ToggleAdvancedSection() => IsAdvancedSectionDisplayed = !IsAdvancedSectionDisplayed; - - public void ShowOutputPathPrompt() + [RelayCommand] + private async Task ShowOutputPathPromptAsync() { if (IsSingleChannel) { @@ -112,33 +143,43 @@ public class ExportSetupViewModel : DialogScreen ); var extension = SelectedFormat.GetFileExtension(); - var filter = $"{extension.ToUpperInvariant()} files|*.{extension}"; - var path = _dialogManager.PromptSaveFilePath(filter, defaultFileName); + var path = await dialogManager.PromptSaveFilePathAsync( + [ + new FilePickerFileType($"{extension.ToUpperInvariant()} file") + { + Patterns = [$"*.{extension}"], + }, + ], + defaultFileName + ); + if (!string.IsNullOrWhiteSpace(path)) OutputPath = path; } else { - var path = _dialogManager.PromptDirectoryPath(); + var path = await dialogManager.PromptDirectoryPathAsync(); if (!string.IsNullOrWhiteSpace(path)) OutputPath = path; } } - public void ShowAssetsDirPathPrompt() + [RelayCommand] + private async Task ShowAssetsDirPathPromptAsync() { - var path = _dialogManager.PromptDirectoryPath(); + var path = await dialogManager.PromptDirectoryPathAsync(); if (!string.IsNullOrWhiteSpace(path)) AssetsDirPath = path; } - public void Confirm() + [RelayCommand] + private async Task ConfirmAsync() { - // Prompt the output path if it's not set yet + // Prompt the output path if it hasn't been set yet if (string.IsNullOrWhiteSpace(OutputPath)) { - ShowOutputPathPrompt(); + await ShowOutputPathPromptAsync(); // If the output path is still not set, cancel the export if (string.IsNullOrWhiteSpace(OutputPath)) @@ -146,31 +187,15 @@ public class ExportSetupViewModel : DialogScreen } // Persist preferences - _settingsService.LastExportFormat = SelectedFormat; - _settingsService.LastPartitionLimitValue = PartitionLimitValue; - _settingsService.LastMessageFilterValue = MessageFilterValue; - _settingsService.LastShouldFormatMarkdown = ShouldFormatMarkdown; - _settingsService.LastShouldDownloadAssets = ShouldDownloadAssets; - _settingsService.LastShouldReuseAssets = ShouldReuseAssets; - _settingsService.LastAssetsDirPath = AssetsDirPath; + settingsService.LastExportFormat = SelectedFormat; + settingsService.LastPartitionLimitValue = PartitionLimitValue; + settingsService.LastMessageFilterValue = MessageFilterValue; + settingsService.LastIsReverseMessageOrder = IsReverseMessageOrder; + settingsService.LastShouldFormatMarkdown = ShouldFormatMarkdown; + settingsService.LastShouldDownloadAssets = ShouldDownloadAssets; + settingsService.LastShouldReuseAssets = ShouldReuseAssets; + settingsService.LastAssetsDirPath = AssetsDirPath; Close(true); } } - -public static class ExportSetupViewModelExtensions -{ - public static ExportSetupViewModel CreateExportSetupViewModel( - this IViewModelFactory factory, - Guild guild, - IReadOnlyList channels - ) - { - var viewModel = factory.CreateExportSetupViewModel(); - - viewModel.Guild = guild; - viewModel.Channels = channels; - - return viewModel; - } -} diff --git a/DiscordChatExporter.Gui/ViewModels/Dialogs/MessageBoxViewModel.cs b/DiscordChatExporter.Gui/ViewModels/Dialogs/MessageBoxViewModel.cs index 270ed217..75c78f2e 100644 --- a/DiscordChatExporter.Gui/ViewModels/Dialogs/MessageBoxViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/Dialogs/MessageBoxViewModel.cs @@ -1,49 +1,29 @@ -using DiscordChatExporter.Gui.ViewModels.Framework; +using CommunityToolkit.Mvvm.ComponentModel; +using DiscordChatExporter.Gui.Framework; namespace DiscordChatExporter.Gui.ViewModels.Dialogs; -public class MessageBoxViewModel : DialogScreen +public partial class MessageBoxViewModel : DialogViewModelBase { - public string? Title { get; set; } + [ObservableProperty] + public partial string? Title { get; set; } - public string? Message { get; set; } + [ObservableProperty] + public partial string? Message { get; set; } - public bool IsOkButtonVisible { get; set; } = true; + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(IsDefaultButtonVisible))] + [NotifyPropertyChangedFor(nameof(ButtonsCount))] + public partial string? DefaultButtonText { get; set; } - public string? OkButtonText { get; set; } + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(IsCancelButtonVisible))] + [NotifyPropertyChangedFor(nameof(ButtonsCount))] + public partial string? CancelButtonText { get; set; } - public bool IsCancelButtonVisible { get; set; } + public bool IsDefaultButtonVisible => !string.IsNullOrWhiteSpace(DefaultButtonText); - public string? CancelButtonText { get; set; } + public bool IsCancelButtonVisible => !string.IsNullOrWhiteSpace(CancelButtonText); - public int ButtonsCount => (IsOkButtonVisible ? 1 : 0) + (IsCancelButtonVisible ? 1 : 0); -} - -public static class MessageBoxViewModelExtensions -{ - public static MessageBoxViewModel CreateMessageBoxViewModel( - this IViewModelFactory factory, - string title, - string message, - string? okButtonText, - string? cancelButtonText - ) - { - var viewModel = factory.CreateMessageBoxViewModel(); - - viewModel.Title = title; - viewModel.Message = message; - viewModel.IsOkButtonVisible = !string.IsNullOrWhiteSpace(okButtonText); - viewModel.OkButtonText = okButtonText; - viewModel.IsCancelButtonVisible = !string.IsNullOrWhiteSpace(cancelButtonText); - viewModel.CancelButtonText = cancelButtonText; - - return viewModel; - } - - public static MessageBoxViewModel CreateMessageBoxViewModel( - this IViewModelFactory factory, - string title, - string message - ) => factory.CreateMessageBoxViewModel(title, message, "CLOSE", null); + public int ButtonsCount => (IsDefaultButtonVisible ? 1 : 0) + (IsCancelButtonVisible ? 1 : 0); } diff --git a/DiscordChatExporter.Gui/ViewModels/Dialogs/SettingsViewModel.cs b/DiscordChatExporter.Gui/ViewModels/Dialogs/SettingsViewModel.cs index d15e2176..cb3998cd 100644 --- a/DiscordChatExporter.Gui/ViewModels/Dialogs/SettingsViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/Dialogs/SettingsViewModel.cs @@ -1,96 +1,149 @@ -using System; +using System; using System.Collections.Generic; -using System.Linq; -using DiscordChatExporter.Core.Utils.Extensions; +using DiscordChatExporter.Core.Discord; +using DiscordChatExporter.Gui.Framework; +using DiscordChatExporter.Gui.Localization; using DiscordChatExporter.Gui.Models; using DiscordChatExporter.Gui.Services; -using DiscordChatExporter.Gui.ViewModels.Framework; +using PowerKit; +using PowerKit.Extensions; namespace DiscordChatExporter.Gui.ViewModels.Dialogs; -public class SettingsViewModel(SettingsService settingsService) : DialogScreen +public class SettingsViewModel : DialogViewModelBase { - public bool IsAutoUpdateEnabled + private readonly SettingsService _settingsService; + + private readonly IDisposable _eventSubscription; + + public SettingsViewModel( + SettingsService settingsService, + LocalizationManager localizationManager + ) { - get => settingsService.IsAutoUpdateEnabled; - set => settingsService.IsAutoUpdateEnabled = value; + _settingsService = settingsService; + LocalizationManager = localizationManager; + + _eventSubscription = Disposable.Merge( + _settingsService.WatchAllProperties(OnAllPropertiesChanged) + ); } - public bool IsDarkModeEnabled + public LocalizationManager LocalizationManager { get; } + + public IReadOnlyList AvailableThemes { get; } = Enum.GetValues(); + + public ThemeVariant Theme { - get => settingsService.IsDarkModeEnabled; - set => settingsService.IsDarkModeEnabled = value; + get => _settingsService.Theme; + set => _settingsService.Theme = value; + } + + public IReadOnlyList AvailableLanguages { get; } = Enum.GetValues(); + + public Language Language + { + get => _settingsService.Language; + set => _settingsService.Language = value; + } + + public bool IsAutoUpdateAvailable { get; } = + OperatingSystem.IsWindows() && StartOptions.Current.IsAutoUpdateAllowed; + + public bool IsAutoUpdateEnabled + { + get => _settingsService.IsAutoUpdateEnabled; + set => _settingsService.IsAutoUpdateEnabled = value; } public bool IsTokenPersisted { - get => settingsService.IsTokenPersisted; - set => settingsService.IsTokenPersisted = value; + get => _settingsService.IsTokenPersisted; + set => _settingsService.IsTokenPersisted = value; } - public IReadOnlyList AvailableThreadInclusions { get; } = + public IReadOnlyList AvailableRateLimitPreferences { get; } = + Enum.GetValues(); + + public RateLimitPreference RateLimitPreference + { + get => _settingsService.RateLimitPreference; + set => _settingsService.RateLimitPreference = value; + } + + public IReadOnlyList AvailableThreadInclusionModes { get; } = Enum.GetValues(); public ThreadInclusionMode ThreadInclusionMode { - get => settingsService.ThreadInclusionMode; - set => settingsService.ThreadInclusionMode = value; + get => _settingsService.ThreadInclusionMode; + set => _settingsService.ThreadInclusionMode = value; } - // These items have to be non-nullable because WPF ComboBox doesn't allow a null value to be selected - public IReadOnlyList AvailableLocales { get; } = new[] - { - // Current locale (maps to null downstream) - "", - // Locales supported by the Discord app - "da-DK", - "de-DE", - "en-GB", - "en-US", - "es-ES", - "fr-FR", - "hr-HR", - "it-IT", - "lt-LT", - "hu-HU", - "nl-NL", - "no-NO", - "pl-PL", - "pt-BR", - "ro-RO", - "fi-FI", - "sv-SE", - "vi-VN", - "tr-TR", - "cs-CZ", - "el-GR", - "bg-BG", - "ru-RU", - "uk-UA", - "th-TH", - "zh-CN", - "ja-JP", - "zh-TW", - "ko-KR" - }.Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); + // These items have to be non-nullable because Avalonia ComboBox doesn't allow a null value to be selected + public IReadOnlyList AvailableLocales { get; } = + [ + // Current locale (maps to null downstream) + "", + // Locales supported by the Discord app + "da-DK", + "de-DE", + "en-GB", + "en-US", + "es-ES", + "fr-FR", + "hr-HR", + "it-IT", + "lt-LT", + "hu-HU", + "nl-NL", + "no-NO", + "pl-PL", + "pt-BR", + "ro-RO", + "fi-FI", + "sv-SE", + "vi-VN", + "tr-TR", + "cs-CZ", + "el-GR", + "bg-BG", + "ru-RU", + "uk-UA", + "th-TH", + "zh-CN", + "ja-JP", + "zh-TW", + "ko-KR", + ]; - // This has to be non-nullable because WPF ComboBox doesn't allow a null value to be selected + // This has to be non-nullable because Avalonia ComboBox doesn't allow a null value to be selected public string Locale { - get => settingsService.Locale ?? ""; + get => _settingsService.Locale ?? ""; // Important to reduce empty strings to nulls, because empty strings don't correspond to valid cultures - set => settingsService.Locale = value.NullIfWhiteSpace(); + set => _settingsService.Locale = value.NullIfWhiteSpace(); } public bool IsUtcNormalizationEnabled { - get => settingsService.IsUtcNormalizationEnabled; - set => settingsService.IsUtcNormalizationEnabled = value; + get => _settingsService.IsUtcNormalizationEnabled; + set => _settingsService.IsUtcNormalizationEnabled = value; } public int ParallelLimit { - get => settingsService.ParallelLimit; - set => settingsService.ParallelLimit = Math.Clamp(value, 1, 10); + get => _settingsService.ParallelLimit; + set => _settingsService.ParallelLimit = Math.Clamp(value, 1, 10); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _eventSubscription.Dispose(); + } + + base.Dispose(disposing); } } diff --git a/DiscordChatExporter.Gui/ViewModels/Framework/DialogManager.cs b/DiscordChatExporter.Gui/ViewModels/Framework/DialogManager.cs deleted file mode 100644 index 15958817..00000000 --- a/DiscordChatExporter.Gui/ViewModels/Framework/DialogManager.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using MaterialDesignThemes.Wpf; -using Microsoft.Win32; -using Stylet; - -namespace DiscordChatExporter.Gui.ViewModels.Framework; - -public class DialogManager(IViewManager viewManager) : IDisposable -{ - private readonly SemaphoreSlim _dialogLock = new(1, 1); - - public async ValueTask ShowDialogAsync(DialogScreen dialogScreen) - { - var view = viewManager.CreateAndBindViewForModelIfNecessary(dialogScreen); - - void OnDialogOpened(object? openSender, DialogOpenedEventArgs openArgs) - { - void OnScreenClosed(object? closeSender, EventArgs closeArgs) - { - try - { - openArgs.Session.Close(); - } - catch (InvalidOperationException) - { - // Race condition: dialog is already being closed - } - - dialogScreen.Closed -= OnScreenClosed; - } - dialogScreen.Closed += OnScreenClosed; - } - - await _dialogLock.WaitAsync(); - try - { - await DialogHost.Show(view, OnDialogOpened); - return dialogScreen.DialogResult; - } - finally - { - _dialogLock.Release(); - } - } - - public string? PromptSaveFilePath(string filter = "All files|*.*", string defaultFilePath = "") - { - var dialog = new SaveFileDialog - { - Filter = filter, - AddExtension = true, - FileName = defaultFilePath, - DefaultExt = Path.GetExtension(defaultFilePath) - }; - - return dialog.ShowDialog() == true ? dialog.FileName : null; - } - - public string? PromptDirectoryPath(string defaultDirPath = "") - { - var dialog = new OpenFolderDialog { InitialDirectory = defaultDirPath }; - return dialog.ShowDialog() == true ? dialog.FolderName : null; - } - - public void Dispose() - { - _dialogLock.Dispose(); - } -} diff --git a/DiscordChatExporter.Gui/ViewModels/Framework/DialogScreen.cs b/DiscordChatExporter.Gui/ViewModels/Framework/DialogScreen.cs deleted file mode 100644 index e6a74360..00000000 --- a/DiscordChatExporter.Gui/ViewModels/Framework/DialogScreen.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using Stylet; - -namespace DiscordChatExporter.Gui.ViewModels.Framework; - -public abstract class DialogScreen : PropertyChangedBase -{ - public T? DialogResult { get; private set; } - - public event EventHandler? Closed; - - public void Close(T dialogResult) - { - DialogResult = dialogResult; - Closed?.Invoke(this, EventArgs.Empty); - } -} - -public abstract class DialogScreen : DialogScreen; diff --git a/DiscordChatExporter.Gui/ViewModels/Framework/IViewModelFactory.cs b/DiscordChatExporter.Gui/ViewModels/Framework/IViewModelFactory.cs deleted file mode 100644 index f1e5e58c..00000000 --- a/DiscordChatExporter.Gui/ViewModels/Framework/IViewModelFactory.cs +++ /dev/null @@ -1,16 +0,0 @@ -using DiscordChatExporter.Gui.ViewModels.Components; -using DiscordChatExporter.Gui.ViewModels.Dialogs; - -namespace DiscordChatExporter.Gui.ViewModels.Framework; - -// Used to instantiate new view models while making use of dependency injection -public interface IViewModelFactory -{ - DashboardViewModel CreateDashboardViewModel(); - - ExportSetupViewModel CreateExportSetupViewModel(); - - MessageBoxViewModel CreateMessageBoxViewModel(); - - SettingsViewModel CreateSettingsViewModel(); -} diff --git a/DiscordChatExporter.Gui/ViewModels/MainViewModel.cs b/DiscordChatExporter.Gui/ViewModels/MainViewModel.cs new file mode 100644 index 00000000..169d50ce --- /dev/null +++ b/DiscordChatExporter.Gui/ViewModels/MainViewModel.cs @@ -0,0 +1,119 @@ +using System.Diagnostics; +using System.Threading.Tasks; +using DiscordChatExporter.Gui.Framework; +using DiscordChatExporter.Gui.Localization; +using DiscordChatExporter.Gui.Services; +using DiscordChatExporter.Gui.ViewModels.Components; +using PowerKit.Extensions; + +namespace DiscordChatExporter.Gui.ViewModels; + +public partial class MainViewModel( + ViewModelManager viewModelManager, + DialogManager dialogManager, + SnackbarManager snackbarManager, + SettingsService settingsService, + UpdateService updateService, + LocalizationManager localizationManager +) : ViewModelBase +{ + public string Title { get; } = $"{Program.Name} v{Program.VersionString}"; + + public DashboardViewModel Dashboard { get; } = viewModelManager.GetDashboardViewModel(); + + private async Task ShowUkraineSupportMessageAsync() + { + if (!settingsService.IsUkraineSupportMessageEnabled) + return; + + var dialog = viewModelManager.GetMessageBoxViewModel( + localizationManager.UkraineSupportTitle, + localizationManager.UkraineSupportMessage, + localizationManager.LearnMoreButton, + localizationManager.CloseButton + ); + + // Disable this message in the future + settingsService.IsUkraineSupportMessageEnabled = false; + settingsService.Save(); + + if (await dialogManager.ShowDialogAsync(dialog) == true) + Process.StartShellExecute("https://tyrrrz.me/ukraine?source=discordchatexporter"); + } + + private async Task ShowDevelopmentBuildMessageAsync() + { + if (!Program.IsDevelopmentBuild) + return; + + // If debugging, the user is likely a developer + if (Debugger.IsAttached) + return; + + var dialog = viewModelManager.GetMessageBoxViewModel( + localizationManager.UnstableBuildTitle, + string.Format(localizationManager.UnstableBuildMessage, Program.Name), + localizationManager.SeeReleasesButton, + localizationManager.CloseButton + ); + + if (await dialogManager.ShowDialogAsync(dialog) == true) + Process.StartShellExecute(Program.ProjectReleasesUrl); + } + + private async Task CheckForUpdatesAsync() + { + try + { + var updateVersion = await updateService.CheckForUpdatesAsync(); + if (updateVersion is null) + return; + + snackbarManager.Notify( + string.Format( + localizationManager.UpdateDownloadingMessage, + Program.Name, + updateVersion + ) + ); + await updateService.PrepareUpdateAsync(updateVersion); + + snackbarManager.Notify( + localizationManager.UpdateReadyMessage, + localizationManager.UpdateInstallNowButton, + () => + { + updateService.FinalizeUpdate(true); + + App.Shutdown(2); + } + ); + } + catch + { + // Failure to update shouldn't crash the application + snackbarManager.Notify(localizationManager.UpdateFailedMessage); + } + } + + public override async Task InitializeAsync() + { + await ShowUkraineSupportMessageAsync(); + await ShowDevelopmentBuildMessageAsync(); + await CheckForUpdatesAsync(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + // Save settings + settingsService.Save(); + + // Finalize pending updates + updateService.FinalizeUpdate(false); + } + + base.Dispose(disposing); + } +} diff --git a/DiscordChatExporter.Gui/ViewModels/Messages/NotificationMessage.cs b/DiscordChatExporter.Gui/ViewModels/Messages/NotificationMessage.cs deleted file mode 100644 index 54c2ae80..00000000 --- a/DiscordChatExporter.Gui/ViewModels/Messages/NotificationMessage.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace DiscordChatExporter.Gui.ViewModels.Messages; - -public record NotificationMessage(string Text); diff --git a/DiscordChatExporter.Gui/ViewModels/RootViewModel.cs b/DiscordChatExporter.Gui/ViewModels/RootViewModel.cs deleted file mode 100644 index 4e100cd0..00000000 --- a/DiscordChatExporter.Gui/ViewModels/RootViewModel.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System; -using System.Threading.Tasks; -using DiscordChatExporter.Gui.Services; -using DiscordChatExporter.Gui.Utils; -using DiscordChatExporter.Gui.ViewModels.Components; -using DiscordChatExporter.Gui.ViewModels.Dialogs; -using DiscordChatExporter.Gui.ViewModels.Framework; -using DiscordChatExporter.Gui.ViewModels.Messages; -using MaterialDesignThemes.Wpf; -using Stylet; - -namespace DiscordChatExporter.Gui.ViewModels; - -public class RootViewModel : Screen, IHandle, IDisposable -{ - private readonly IViewModelFactory _viewModelFactory; - private readonly DialogManager _dialogManager; - private readonly SettingsService _settingsService; - private readonly UpdateService _updateService; - - public SnackbarMessageQueue Notifications { get; } = new(TimeSpan.FromSeconds(5)); - - public DashboardViewModel Dashboard { get; } - - public RootViewModel( - IViewModelFactory viewModelFactory, - IEventAggregator eventAggregator, - DialogManager dialogManager, - SettingsService settingsService, - UpdateService updateService - ) - { - _viewModelFactory = viewModelFactory; - _dialogManager = dialogManager; - _settingsService = settingsService; - _updateService = updateService; - - eventAggregator.Subscribe(this); - - Dashboard = _viewModelFactory.CreateDashboardViewModel(); - - DisplayName = $"{App.Name} v{App.VersionString}"; - } - - private async Task ShowUkraineSupportMessageAsync() - { - if (!_settingsService.IsUkraineSupportMessageEnabled) - return; - - var dialog = _viewModelFactory.CreateMessageBoxViewModel( - "Thank you for supporting Ukraine!", - """ - As Russia wages a genocidal war against my country, I'm grateful to everyone who continues to stand with Ukraine in our fight for freedom. - - Click LEARN MORE to find ways that you can help. - """, - "LEARN MORE", - "CLOSE" - ); - - // Disable this message in the future - _settingsService.IsUkraineSupportMessageEnabled = false; - _settingsService.Save(); - - if (await _dialogManager.ShowDialogAsync(dialog) == true) - ProcessEx.StartShellExecute("https://tyrrrz.me/ukraine?source=discordchatexporter"); - } - - private async ValueTask CheckForUpdatesAsync() - { - try - { - var updateVersion = await _updateService.CheckForUpdatesAsync(); - if (updateVersion is null) - return; - - Notifications.Enqueue($"Downloading update to {App.Name} v{updateVersion}..."); - await _updateService.PrepareUpdateAsync(updateVersion); - - Notifications.Enqueue( - "Update has been downloaded and will be installed when you exit", - "INSTALL NOW", - () => - { - _updateService.FinalizeUpdate(true); - RequestClose(); - } - ); - } - catch - { - // Failure to update shouldn't crash the application - Notifications.Enqueue("Failed to perform application update"); - } - } - - public async void OnViewFullyLoaded() - { - await ShowUkraineSupportMessageAsync(); - await CheckForUpdatesAsync(); - } - - protected override void OnViewLoaded() - { - base.OnViewLoaded(); - - _settingsService.Load(); - - // Sync the theme with settings - if (_settingsService.IsDarkModeEnabled) - { - App.SetDarkTheme(); - } - else - { - App.SetLightTheme(); - } - - // App has just been updated, display the changelog - if ( - _settingsService.LastAppVersion is not null - && _settingsService.LastAppVersion != App.Version - ) - { - Notifications.Enqueue( - $"Successfully updated to {App.Name} v{App.VersionString}", - "WHAT'S NEW", - () => ProcessEx.StartShellExecute(App.LatestReleaseUrl) - ); - - _settingsService.LastAppVersion = App.Version; - _settingsService.Save(); - } - } - - protected override void OnClose() - { - base.OnClose(); - - _settingsService.Save(); - _updateService.FinalizeUpdate(false); - } - - public void Handle(NotificationMessage message) => Notifications.Enqueue(message.Text); - - public void Dispose() => Notifications.Dispose(); -} diff --git a/DiscordChatExporter.Gui/Views/Components/DashboardView.axaml b/DiscordChatExporter.Gui/Views/Components/DashboardView.axaml new file mode 100644 index 00000000..cd3ea111 --- /dev/null +++ b/DiscordChatExporter.Gui/Views/Components/DashboardView.axaml @@ -0,0 +1,307 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DiscordChatExporter.Gui/Views/Components/DashboardView.axaml.cs b/DiscordChatExporter.Gui/Views/Components/DashboardView.axaml.cs new file mode 100644 index 00000000..ec1c27e9 --- /dev/null +++ b/DiscordChatExporter.Gui/Views/Components/DashboardView.axaml.cs @@ -0,0 +1,46 @@ +using System.Linq; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Interactivity; +using DiscordChatExporter.Core.Discord.Data; +using DiscordChatExporter.Gui.Framework; +using DiscordChatExporter.Gui.ViewModels.Components; +using PowerKit.Extensions; + +namespace DiscordChatExporter.Gui.Views.Components; + +public partial class DashboardView : UserControl +{ + public DashboardView() => InitializeComponent(); + + private void UserControl_OnLoaded(object? sender, RoutedEventArgs args) => + TokenValueTextBox.Focus(); + + private void AvailableGuildsListBox_OnSelectionChanged( + object? sender, + SelectionChangedEventArgs args + ) => DataContext.PullChannelsCommand.ExecuteIfCan(null); + + private void AvailableChannelsTreeView_OnSelectionChanged( + object? sender, + SelectionChangedEventArgs args + ) + { + // Hack: unselect categories because they cannot be exported + foreach ( + var item in args.AddedItems.OfType().Where(x => x.Channel.IsCategory) + ) + { + if (AvailableChannelsTreeView.TreeContainerFromItem(item) is TreeViewItem container) + container.IsSelected = false; + } + } + + private void ChannelGrid_OnDoubleTapped(object? sender, TappedEventArgs args) + { + if (DataContext.SelectedChannels.Count != 1) + return; + + DataContext.ExportCommand.ExecuteIfCan(null); + } +} diff --git a/DiscordChatExporter.Gui/Views/Components/DashboardView.xaml b/DiscordChatExporter.Gui/Views/Components/DashboardView.xaml deleted file mode 100644 index 4d7d031f..00000000 --- a/DiscordChatExporter.Gui/Views/Components/DashboardView.xaml +++ /dev/null @@ -1,440 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/DiscordChatExporter.Gui/Views/Components/DashboardView.xaml.cs b/DiscordChatExporter.Gui/Views/Components/DashboardView.xaml.cs deleted file mode 100644 index 281bc6f0..00000000 --- a/DiscordChatExporter.Gui/Views/Components/DashboardView.xaml.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace DiscordChatExporter.Gui.Views.Components; - -public partial class DashboardView -{ - public DashboardView() - { - InitializeComponent(); - } -} diff --git a/DiscordChatExporter.Gui/Views/Controls/HyperLink.axaml b/DiscordChatExporter.Gui/Views/Controls/HyperLink.axaml new file mode 100644 index 00000000..454d8161 --- /dev/null +++ b/DiscordChatExporter.Gui/Views/Controls/HyperLink.axaml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/DiscordChatExporter.Gui/Views/Controls/HyperLink.axaml.cs b/DiscordChatExporter.Gui/Views/Controls/HyperLink.axaml.cs new file mode 100644 index 00000000..a50dd2e7 --- /dev/null +++ b/DiscordChatExporter.Gui/Views/Controls/HyperLink.axaml.cs @@ -0,0 +1,64 @@ +using System.Diagnostics; +using System.Windows.Input; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using PowerKit.Extensions; + +namespace DiscordChatExporter.Gui.Views.Controls; + +public partial class HyperLink : UserControl +{ + public static readonly StyledProperty TextProperty = + TextBlock.TextProperty.AddOwner(); + + public static readonly StyledProperty CommandProperty = + Button.CommandProperty.AddOwner(); + + public static readonly StyledProperty CommandParameterProperty = + Button.CommandParameterProperty.AddOwner(); + + // If Url is set and Command is not set, clicking will open this URL in the default browser. + public static readonly StyledProperty UrlProperty = AvaloniaProperty.Register< + HyperLink, + string? + >(nameof(Url)); + + public HyperLink() => InitializeComponent(); + + public string? Text + { + get => GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + public ICommand? Command + { + get => GetValue(CommandProperty); + set => SetValue(CommandProperty, value); + } + + public object? CommandParameter + { + get => GetValue(CommandParameterProperty); + set => SetValue(CommandParameterProperty, value); + } + + public string? Url + { + get => GetValue(UrlProperty); + set => SetValue(UrlProperty, value); + } + + private void TextBlock_OnPointerReleased(object? sender, PointerReleasedEventArgs args) + { + if (Command is not null) + { + Command.ExecuteIfCan(CommandParameter); + } + else if (!string.IsNullOrWhiteSpace(Url)) + { + Process.StartShellExecute(Url); + } + } +} diff --git a/DiscordChatExporter.Gui/Views/Controls/RevealablePasswordBox.xaml b/DiscordChatExporter.Gui/Views/Controls/RevealablePasswordBox.xaml deleted file mode 100644 index f9ca6ad6..00000000 --- a/DiscordChatExporter.Gui/Views/Controls/RevealablePasswordBox.xaml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/DiscordChatExporter.Gui/Views/Controls/RevealablePasswordBox.xaml.cs b/DiscordChatExporter.Gui/Views/Controls/RevealablePasswordBox.xaml.cs deleted file mode 100644 index 23239e12..00000000 --- a/DiscordChatExporter.Gui/Views/Controls/RevealablePasswordBox.xaml.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Windows; - -namespace DiscordChatExporter.Gui.Views.Controls; - -public partial class RevealablePasswordBox -{ - public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register( - nameof(Password), - typeof(string), - typeof(RevealablePasswordBox), - new FrameworkPropertyMetadata( - string.Empty, - FrameworkPropertyMetadataOptions.BindsTwoWayByDefault - ) - ); - - public static readonly DependencyProperty IsRevealedProperty = DependencyProperty.Register( - nameof(IsRevealed), - typeof(bool), - typeof(RevealablePasswordBox), - new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None) - ); - - public string Password - { - get => (string)GetValue(PasswordProperty); - set => SetValue(PasswordProperty, value); - } - - public bool IsRevealed - { - get => (bool)GetValue(IsRevealedProperty); - set => SetValue(IsRevealedProperty, value); - } - - public RevealablePasswordBox() - { - InitializeComponent(); - } -} diff --git a/DiscordChatExporter.Gui/Views/Dialogs/ExportSetupView.axaml b/DiscordChatExporter.Gui/Views/Dialogs/ExportSetupView.axaml new file mode 100644 index 00000000..b7f2e1bb --- /dev/null +++ b/DiscordChatExporter.Gui/Views/Dialogs/ExportSetupView.axaml @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + \ No newline at end of file diff --git a/DiscordChatExporter.Gui/Views/Dialogs/MessageBoxView.axaml.cs b/DiscordChatExporter.Gui/Views/Dialogs/MessageBoxView.axaml.cs new file mode 100644 index 00000000..997cc2d4 --- /dev/null +++ b/DiscordChatExporter.Gui/Views/Dialogs/MessageBoxView.axaml.cs @@ -0,0 +1,9 @@ +using DiscordChatExporter.Gui.Framework; +using DiscordChatExporter.Gui.ViewModels.Dialogs; + +namespace DiscordChatExporter.Gui.Views.Dialogs; + +public partial class MessageBoxView : UserControl +{ + public MessageBoxView() => InitializeComponent(); +} diff --git a/DiscordChatExporter.Gui/Views/Dialogs/MessageBoxView.xaml b/DiscordChatExporter.Gui/Views/Dialogs/MessageBoxView.xaml deleted file mode 100644 index e96789c3..00000000 --- a/DiscordChatExporter.Gui/Views/Dialogs/MessageBoxView.xaml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - -