mirror of
https://github.com/Paillat-dev/dismoji.git
synced 2026-01-02 09:06:20 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18020f3bc8 | ||
| 7a42ead179 | |||
| 1d9d14f1ed | |||
| cc7d78a47a | |||
|
|
fe3bcffa24 | ||
|
|
b01368182f | ||
| 9fb0c776d7 | |||
| cfa8812081 | |||
| 34739a077c | |||
| 47aa6a3fc1 | |||
| ad98dd9a58 | |||
| a1d2592ee1 | |||
| 6dc46f06d6 | |||
| 8af0deea07 |
2
.github/workflows/publish.yaml
vendored
2
.github/workflows/publish.yaml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
environment: pypi
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
|
||||
8
.github/workflows/quality.yaml
vendored
8
.github/workflows/quality.yaml
vendored
@@ -9,9 +9,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Setup Copywrite
|
||||
uses: hashicorp/setup-copywrite@5e3e8a26d7b9f8a508848ad0a069dfd2f7aa5339
|
||||
uses: hashicorp/setup-copywrite@32f9f1c86f661b8a51100768976a06f1b281a035
|
||||
- name: Check Header Compliance
|
||||
run: copywrite headers --plan --config .copywrite.hcl
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
name: "Python 3.13"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
name: ${{ matrix.name }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: "Install uv"
|
||||
uses: astral-sh/setup-uv@v6
|
||||
|
||||
22
README.md
22
README.md
@@ -12,7 +12,8 @@
|
||||
|
||||
<!-- end badges -->
|
||||
|
||||
A Python library for converting Discord emoji names to their Unicode equivalents.
|
||||
A Python library for converting Discord emoji names to their Unicode equivalents and
|
||||
vice versa.
|
||||
|
||||
</div>
|
||||
|
||||
@@ -31,9 +32,9 @@ A Python library for converting Discord emoji names to their Unicode equivalents
|
||||
## Overview
|
||||
|
||||
Dismoji is a lightweight Python library that provides a simple way to convert Discord
|
||||
emoji names to their Unicode equivalents. With just a single function call, you can
|
||||
transform text containing Discord-style emoji codes (like `:smile:`) into text with
|
||||
actual Unicode emoji characters (like "😄").
|
||||
emoji names to their Unicode equivalents and vice versa. With just two function calls,
|
||||
you can transform text containing Discord-style emoji codes (like `:smile:`) into text
|
||||
with actual Unicode emoji characters (like "😄") and back again.
|
||||
|
||||
This library uses
|
||||
[Paillat-dev/discord-emojis](https://github.com/Paillat-dev/discord-emojis) as the
|
||||
@@ -56,16 +57,23 @@ import dismoji
|
||||
text = "Hello, :wave: I'm excited! :partying_face:"
|
||||
converted_text = dismoji.emojize(text)
|
||||
print(converted_text) # Output: "Hello, 👋 I'm excited! 🥳"
|
||||
|
||||
# Convert Unicode emojis back to Discord emoji names
|
||||
emoji_text = "Hello, 👋 I'm excited! 🥳"
|
||||
named_text = dismoji.demojize(emoji_text)
|
||||
print(named_text) # Output: "Hello, :wave: I'm excited! :partying_face:"
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- **Simple API**: Just one function to remember - `dismoji.emojize()`
|
||||
- **Simple API**: Just two functions to remember - `dismoji.emojize()` and
|
||||
`dismoji.demojize()`
|
||||
- **Discord Compatible**: Supports Discord's emoji naming conventions
|
||||
- **Comprehensive**: Includes all standard emojis available on Discord
|
||||
- **Type Safe**: Fully type-annotated for better IDE integration
|
||||
- **Zero Dependencies**: Lightweight with no external dependencies
|
||||
- **Fast**: Optimized for quick emoji replacement
|
||||
- **Bidirectional**: Convert between emoji names and characters in both directions
|
||||
|
||||
## Getting Help
|
||||
|
||||
@@ -94,6 +102,10 @@ If you encounter issues or have questions about dismoji:
|
||||
- **HashiCorp Copywrite**: For managing license headers
|
||||
- **basedpyright**: For type checking
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
- [`emoji`](https://pypi.org/project/emoji/) as inspiration for the API design
|
||||
|
||||
## License
|
||||
|
||||
MIT License - Copyright (c) 2025 Paillat-dev
|
||||
|
||||
@@ -120,6 +120,7 @@ exclude = [
|
||||
"src/dismoji/_version.py"
|
||||
]
|
||||
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["ALL"]
|
||||
per-file-ignores = { "examples/**/*" = ["INP001", "ARG002", "T201"], "tests/**/*" = ["S101"], "src/dismoji/_version.py" = ["I001", "Q000", "UP005", "UP006", "UP035"] }
|
||||
@@ -151,6 +152,7 @@ extend-ignore = [
|
||||
"C901",
|
||||
"ISC003" # conflicts with basedpyright reportImplicitStringConcatenation
|
||||
]
|
||||
pydocstyle.convention = "google"
|
||||
|
||||
[tool.uv.sources]
|
||||
py-cord = { git = "https://github.com/Pycord-Development/pycord", rev = "c0c0b7c58f7b489983a159f5e0eea2c0dab0b0c8" }
|
||||
|
||||
@@ -1,25 +1,18 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["config:recommended"],
|
||||
"baseBranches": ["master"],
|
||||
"labels": ["deps"],
|
||||
"ignorePaths": ["requirements.txt"],
|
||||
"commitMessagePrefix": "⬆️",
|
||||
"commitMessageAction": "Upgrade",
|
||||
"packageRules": [
|
||||
{
|
||||
"updateTypes": ["pin"],
|
||||
"commitMessagePrefix": "📌",
|
||||
"commitMessageAction": "Pin"
|
||||
"extends": [
|
||||
"local>nicebots-xyz/renovate-config",
|
||||
":semanticPrefixFixDepsChoreOthers",
|
||||
":dependencyDashboard"
|
||||
],
|
||||
"git-submodules": {
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"updateTypes": ["rollback"],
|
||||
"commitMessagePrefix": "⬇️",
|
||||
"commitMessageAction": "Downgrade"
|
||||
},
|
||||
{
|
||||
"matchDatasources": ["pypi"],
|
||||
"addLabels": ["pypi"]
|
||||
"forkProcessing": "enabled",
|
||||
"baseBranchPatterns": [
|
||||
"master"
|
||||
],
|
||||
"lockFileMaintenance": {
|
||||
"enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -12,9 +12,32 @@ EMOJIS_PATH = Path(__file__).parent / "raw" / "build" / "emojis.json"
|
||||
with EMOJIS_PATH.open("r", encoding="utf-8") as f:
|
||||
EMOJIS = json.load(f)
|
||||
|
||||
EMOJI_MAPPING: dict[str, str] = {k: EMOJIS["emojis"][v]["surrogates"] for k, v in EMOJIS["nameToEmoji"].items()}
|
||||
_VARIATION_SELECTOR = "\ufe0f" # We remove this as it is not needed by discord and causes issues with tests
|
||||
EMOJI_MAPPING: dict[str, str] = {
|
||||
k: EMOJIS["emojis"][v]["surrogates"].replace(_VARIATION_SELECTOR, "") for k, v in EMOJIS["nameToEmoji"].items()
|
||||
}
|
||||
|
||||
EMOJI_PATTERN = re.compile(r":([a-zA-Z0-9_-]+):")
|
||||
REVERSE_EMOJI_MAPPING: dict[str, str] = {}
|
||||
|
||||
for emoji_index_str, emoji_index in sorted(EMOJIS["surrogateToEmoji"].items(), key=lambda x: len(x[0]), reverse=True):
|
||||
# Get the first name in the list as the preferred name
|
||||
e = EMOJIS["emojis"][emoji_index]
|
||||
# If it has multiple diversity parents, use the last name because it is the most specific one
|
||||
# e.g. :handshake_light_skin_tone_dark_skin_tone: vs :handshake_tone1_tone5:
|
||||
REVERSE_EMOJI_MAPPING[emoji_index_str] = e["names"][-1 if e.get("hasMultiDiversityParent") else 0]
|
||||
|
||||
del EMOJIS, _VARIATION_SELECTOR # Clean up to save memory
|
||||
|
||||
EMOJI_PATTERN = re.compile(r":([\w+-]+):")
|
||||
|
||||
EMOJI_CHARS_PATTERN = re.compile("|".join(map(re.escape, REVERSE_EMOJI_MAPPING.keys())))
|
||||
|
||||
|
||||
def _replace(match: re.Match[str]) -> str:
|
||||
emoji_name = match.group(1)
|
||||
if emoji_name in EMOJI_MAPPING:
|
||||
return EMOJI_MAPPING[emoji_name]
|
||||
return match.group(0)
|
||||
|
||||
|
||||
def emojize(s: str) -> str:
|
||||
@@ -27,11 +50,30 @@ def emojize(s: str) -> str:
|
||||
str: The input string with emoji names replaced by emoji characters.
|
||||
|
||||
"""
|
||||
return EMOJI_PATTERN.sub(_replace, s)
|
||||
|
||||
def replace(match: re.Match[str]) -> str:
|
||||
emoji_name = match.group(1)
|
||||
if emoji_name in EMOJI_MAPPING:
|
||||
return EMOJI_MAPPING[emoji_name]
|
||||
return match.group(0)
|
||||
|
||||
return EMOJI_PATTERN.sub(replace, s)
|
||||
def _reverse_replace(match: re.Match[str]) -> str:
|
||||
emoji = match.group(0)
|
||||
return f":{REVERSE_EMOJI_MAPPING[emoji]}:"
|
||||
|
||||
|
||||
def demojize(s: str) -> str:
|
||||
"""Convert a string with emoji characters to a string with emoji names.
|
||||
|
||||
Args:
|
||||
s (str): The input string containing emoji characters.
|
||||
|
||||
Returns:
|
||||
str: The input string with emoji characters replaced by emoji names.
|
||||
|
||||
"""
|
||||
return EMOJI_CHARS_PATTERN.sub(_reverse_replace, s)
|
||||
|
||||
|
||||
__all__ = (
|
||||
"EMOJI_MAPPING",
|
||||
"REVERSE_EMOJI_MAPPING",
|
||||
"demojize",
|
||||
"emojize",
|
||||
)
|
||||
|
||||
Submodule src/dismoji/raw updated: f4c3abf844...78764682b8
@@ -1,7 +1,26 @@
|
||||
# Copyright (c) Paillat-dev
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from dismoji import emojize
|
||||
from dismoji import EMOJI_MAPPING, REVERSE_EMOJI_MAPPING, demojize, emojize
|
||||
|
||||
|
||||
def are_equal(a: str, b: str) -> bool:
|
||||
"""Check if two emojis are equal.
|
||||
|
||||
Allows for comparing emojis with modifiers even when they are in different orders.
|
||||
|
||||
Args:
|
||||
a (str): First emoji string.
|
||||
b (str): Second emoji string.
|
||||
|
||||
Returns:
|
||||
bool: True if the emojis are equal, False otherwise.
|
||||
"""
|
||||
if len(a) != len(b):
|
||||
return False
|
||||
if len(a) == 1:
|
||||
return a == b
|
||||
return a[0] == b[0] and set(a[1:]) == set(b[1:])
|
||||
|
||||
|
||||
def test_basic() -> None:
|
||||
@@ -68,3 +87,47 @@ def test_emoji_with_special_characters() -> None:
|
||||
]
|
||||
for input_str, expected_output in special_char_tests:
|
||||
assert emojize(input_str) == expected_output
|
||||
|
||||
|
||||
def test_emojize_all() -> None:
|
||||
for name, emoji in EMOJI_MAPPING.items():
|
||||
assert are_equal(emojize(f":{name}:"), emoji)
|
||||
|
||||
|
||||
def test_demojize_basic() -> None:
|
||||
"""Test basic functionality of demojize function."""
|
||||
assert demojize("Hello 😄") == "Hello :smile:"
|
||||
|
||||
|
||||
def test_demojize_no_match() -> None:
|
||||
"""Test demojize function with no matches."""
|
||||
assert demojize("Hello world") == "Hello world"
|
||||
|
||||
|
||||
def test_demojize_multiple_emojis() -> None:
|
||||
"""Test demojize function with multiple emojis."""
|
||||
assert demojize("😄 👋") == ":smile: :wave:"
|
||||
|
||||
|
||||
def test_demojize_complex_sentence() -> None:
|
||||
"""Test demojize function with a complex sentence."""
|
||||
assert demojize("Hello 👋, what's up? 😄 ✅ 😄") == "Hello :wave:, what's up? :smile: :white_check_mark: :smile:"
|
||||
|
||||
|
||||
def test_demojize_surrogate() -> None:
|
||||
"""Test demojize function with surrogate pairs."""
|
||||
surrogate_pairs = [
|
||||
("🫱🏻🫲🏿", ":handshake_light_skin_tone_dark_skin_tone:"),
|
||||
("🫱🏿🫲🏻", ":handshake_dark_skin_tone_light_skin_tone:"),
|
||||
("🫱🏽🫲🏻", ":handshake_medium_skin_tone_light_skin_tone:"),
|
||||
("🫱🏼🫲🏿", ":handshake_medium_light_skin_tone_dark_skin_tone:"),
|
||||
("🫱🏾🫲🏻", ":handshake_medium_dark_skin_tone_light_skin_tone:"),
|
||||
]
|
||||
|
||||
for surrogate, emoji_name in surrogate_pairs:
|
||||
assert demojize(surrogate) == emoji_name
|
||||
|
||||
|
||||
def test_demojize_all() -> None:
|
||||
for emoji, name in REVERSE_EMOJI_MAPPING.items():
|
||||
assert demojize(emoji) == f":{name}:"
|
||||
|
||||
Reference in New Issue
Block a user