Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .commitlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": ["@commitlint/config-conventional"],
"rules": {
"type-enum": [2, "always", [
"feat", "fix", "refactor", "build", "deps",
"chore", "docs", "test", "style", "ci", "perf"
]],
"scope-enum": [1, "always", [
"core", "helpers", "deps", "release", "ci"
]],
"subject-max-length": [1, "always", 100]
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ tests/exhaustive/
scripts/
data/
gameData.json
.pythonrc.py

# Unit test / coverage reports
htmlcov/
Expand Down
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ repos:
hooks:
- id: ruff
args: [--fix]
exclude: ^examples/
- id: ruff-format

# ── Mypy type checking (mirrors CI type-check job) ────────────────────
Expand Down
50 changes: 50 additions & 0 deletions docs/api/helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,56 @@ Functions for querying omicron skill data from game data collections.

---

## Localization Helpers

Utilities for working with the SWGOH client's BBCode-style markup that appears
throughout localization bundles (ability descriptions, mod descriptions, event
banners, etc.).

### parse_swgoh_string

Parse a raw localization string and convert it to plain text, ANSI-colored
terminal output, Discord markdown, or HTML. The parser follows
`NGUIText.ParseSymbol()` semantics, so it handles the same tag family the game
engine itself supports.

```python
from swgoh_comlink.helpers import parse_swgoh_string

raw = "[c][FF0000][b]Boss[/b][-] deals [u]2x[/u] damage[/c]"

parse_swgoh_string(raw) # 'Boss deals 2x damage'
parse_swgoh_string(raw, output="discord") # '**Boss** deals __2x__ damage'
parse_swgoh_string(raw, output="web") # HTML with <b>, <u>, <span style=...>
parse_swgoh_string(raw, output="terminal") # ANSI truecolor escapes
```

Supported markup:

| Tag(s) | Purpose |
|--------|---------|
| `[c] [/c] [-c]` | Optional color block wrapper |
| `[-]` | Reset the active color |
| `[RGB]` / `[RGBA]` / `[RRGGBB]` / `[RRGGBBAA]` | Hex color literal (short forms duplicate each nibble) |
| `[A]` | 1-digit hex alpha (reuses the previous RGB or white) |
| `[b] [/b]` / `[i] [/i]` | Bold / italic |
| `[u] [/u]` / `[s] [/s]` | Underline / strikethrough |
| `[t] [/t]` | Sprite color marker (stripped in text output) |
| `[sub] [sub=X] [/sub]` / `[sup] [sup=X] [/sup]` | Subscript / superscript with optional scale |
| `[y=X] [/y]` | Font scaling (web output uses inline `font-size`) |
| `\n` | Literal backslash-n escape -> newline |

The `[c]...[/c]` wrapper is optional — bare `[FF0000]` takes effect on its
own, and `[-]` clears the active color whether or not you're inside a `[c]`
block.

::: swgoh_comlink.helpers._localization.parse_swgoh_string
options:
show_root_heading: true
show_root_full_path: false

---

## Decorators

### func_timer
Expand Down
10 changes: 5 additions & 5 deletions examples/Async/get_location_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ async def main():
# Get the language bundle using the latest game data language version
# By default, Comlink compresses the data and encodes it into a BASE64 string for smaller payload and
# faster delivery. The result is a string value that must be decoded before using.
location_bundle = await comlink.get_localization_bundle(id=game_data_versions['language'])
location_bundle = await comlink.get_localization_bundle(localization_id=game_data_versions["language"])

# Decode the Base64 result
loc_bundle_decoded = base64.b64decode(location_bundle['localizationBundle'])
loc_bundle_decoded = base64.b64decode(location_bundle["localizationBundle"])

# Create a zipfile object to access the compressed content
zip_obj = zipfile.ZipFile(io.BytesIO(loc_bundle_decoded))
Expand All @@ -48,10 +48,10 @@ async def main():
# it as a string. You could also use the various other zipfile methods to extract all files to disk
# (see https://docs.python.org/3/library/zipfile.html for more details), or loop through the namelist()
# output and select only specific languages you are interested in.
eng_obj = zip_obj.read('Loc_ENG_US.txt')
eng_obj = zip_obj.read("Loc_ENG_US.txt")

# Decode to string then split into individual lines
eng_obj_decoded = eng_obj.decode('utf-8')
eng_obj_decoded = eng_obj.decode("utf-8")
eng_obj_lines = eng_obj_decoded.splitlines()

"""
Expand All @@ -60,7 +60,7 @@ async def main():
above.
"""
location_bundle_unzipped = await comlink.get_localization_bundle(
id=game_data_versions['language'], unzip=True
localization_id=game_data_versions["language"], unzip=True
)

"""
Expand Down
26 changes: 14 additions & 12 deletions examples/Sync/get_location_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
get_location_bundle.py
Script to illustrate the basic usage of the swgoh_comlink wrapper library
"""

# import the SwgohComlink class from the swgoh_comlink module
from swgoh_comlink import SwgohComlink
import base64
import zipfile
import io
import zipfile

from swgoh_comlink import SwgohComlink

# create an instance of a SwgohComlink object
comlink = SwgohComlink()
Expand All @@ -20,10 +22,10 @@
# Get the language bundle using the latest game data language version
# By default, Comlink compresses the data and encodes it into a BASE64 string for smaller payload and faster delivery.
# The result is a string value that must be decoded before using.
location_bundle = comlink.get_localization_bundle(localization_id=game_data_versions['language'])
location_bundle = comlink.get_localization_bundle(localization_id=game_data_versions["language"])

# Decode the Base64 result
loc_bundle_decoded = base64.b64decode(location_bundle['localizationBundle'])
loc_bundle_decoded = base64.b64decode(location_bundle["localizationBundle"])

# Create a zipfile object to access the compressed content
zip_obj = zipfile.ZipFile(io.BytesIO(loc_bundle_decoded))
Expand All @@ -32,8 +34,8 @@
"""
Sample output:

['Loc_CHS_CN.txt', 'Loc_CHT_CN.txt', 'Loc_ENG_US.txt', 'Loc_FRE_FR.txt', 'Loc_GER_DE.txt', 'Loc_IND_ID.txt',
'Loc_ITA_IT.txt', 'Loc_JPN_JP.txt', 'Loc_Key_Mapping.txt', 'Loc_KOR_KR.txt', 'Loc_POR_BR.txt', 'Loc_RUS_RU.txt',
['Loc_CHS_CN.txt', 'Loc_CHT_CN.txt', 'Loc_ENG_US.txt', 'Loc_FRE_FR.txt', 'Loc_GER_DE.txt', 'Loc_IND_ID.txt',
'Loc_ITA_IT.txt', 'Loc_JPN_JP.txt', 'Loc_Key_Mapping.txt', 'Loc_KOR_KR.txt', 'Loc_POR_BR.txt', 'Loc_RUS_RU.txt',
'Loc_SPA_XM.txt', 'Loc_THA_TH.txt', 'Loc_TUR_TR.txt']

"""
Expand All @@ -43,24 +45,24 @@
# You could also use the various other zipfile methods to extra all files to disk
# (see https://docs.python.org/3/library/zipfile.html for more details), or loop through the namelist()
# output and select only specific languages you are interested in.
eng_obj = zip_obj.read('Loc_ENG_US.txt')
eng_obj = zip_obj.read("Loc_ENG_US.txt")

# Decode to string then split into individual lines
eng_obj_decoded = eng_obj.decode('utf-8')
eng_obj_decoded = eng_obj.decode("utf-8")
eng_obj_lines = eng_obj_decoded.splitlines()

"""
Alternatively, if you elected to have Comlink send an unzipped response, the result is a dictionary containing keys
Alternatively, if you elected to have Comlink send an unzipped response, the result is a dictionary containing keys
for all of the language files (similar to the namelist() output from the zipfile method above.
"""
location_bundle_unzipped = comlink.get_localization_bundle(id=game_data_versions['language'], unzip=True)
location_bundle_unzipped = comlink.get_localization_bundle(localization_id=game_data_versions["language"], unzip=True)

"""
Each key of the result dictionary is a string that can be split into individual lines, or written to files.

>>> location_bundle_unzipped.keys()
dict_keys(['Loc_CHS_CN.txt', 'Loc_CHT_CN.txt', 'Loc_ENG_US.txt', 'Loc_FRE_FR.txt', 'Loc_GER_DE.txt', 'Loc_IND_ID.txt',
'Loc_ITA_IT.txt', 'Loc_JPN_JP.txt', 'Loc_Key_Mapping.txt', 'Loc_KOR_KR.txt', 'Loc_POR_BR.txt', 'Loc_RUS_RU.txt',
dict_keys(['Loc_CHS_CN.txt', 'Loc_CHT_CN.txt', 'Loc_ENG_US.txt', 'Loc_FRE_FR.txt', 'Loc_GER_DE.txt', 'Loc_IND_ID.txt',
'Loc_ITA_IT.txt', 'Loc_JPN_JP.txt', 'Loc_Key_Mapping.txt', 'Loc_KOR_KR.txt', 'Loc_POR_BR.txt', 'Loc_RUS_RU.txt',
'Loc_SPA_XM.txt', 'Loc_THA_TH.txt', 'Loc_TUR_TR.txt'])

Depending on the speed of your connection and other resource factors, the time needed to retrieve the localization
Expand Down
46 changes: 24 additions & 22 deletions examples/Sync/get_location_bundle_adv.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
Script to illustrate a more advanced and efficient usage of the swgoh_comlink
library to collect and parse the localization bundle.
"""

import base64
import zipfile
import io
import os
import json
import os
import zipfile
from pathlib import Path

from swgoh_comlink import SwgohComlink
Expand All @@ -30,11 +31,11 @@


def parse_loc_zip(
zf: zipfile.ZipFile,
output_dir: str | Path = ".",
delimiter: str = "|",
encoding: str = "utf-8",
) -> None:
zf: zipfile.ZipFile,
output_dir: str | Path = ".",
delimiter: str = "|",
encoding: str = "utf-8",
) -> None:
"""
Parses the content of a zip file containing delimited text files, transforms the data into a
key-value JSON structure, and saves the result to specified output files.
Expand Down Expand Up @@ -65,18 +66,19 @@ def parse_loc_zip(
result = {}

# Process the zip file entry as a stream to conserve memory and extract the delimited text
with zf.open(info) as raw:
with io.TextIOWrapper(raw, encoding=encoding) as stream:
for line in stream:
if line.startswith("#"):
continue
# Use partition() instead of split() to split the line into key and value
key, sep, value = line.rstrip("\r\n").partition(delimiter)
# Only store the key-value pair if the delimiter is present
if sep:
# Note: many of the "values" contain BBCode style formatting directives
# that could be stripped out before storing them in the JSON
result[key] = value
with zf.open(info) as raw, io.TextIOWrapper(raw, encoding=encoding) as stream:
for line in stream:
if line.startswith("#"):
continue
# Use partition() instead of split() to split the line into key and value
key, sep, value = line.rstrip("\r\n").partition(delimiter)
# Only store the key-value pair if the delimiter is present
if sep:
# Note: many of the "values" contain BBCode style formatting directives
# that can be stripped or converted before storing them. See
# `swgoh_comlink.helpers.parse_swgoh_string` for a parser that
# emits plain text, Discord, terminal ANSI, or HTML output.
result[key] = value

with open(output_path, "w", encoding="utf-8") as f:
json.dump(result, f, ensure_ascii=False)
Expand All @@ -89,10 +91,10 @@ def parse_loc_zip(

print("Fetching localization bundle (id=%s)", remote_lang)
location_bundle = comlink.get_localization_bundle(
localization_id=remote_lang,
)
localization_id=remote_lang,
)

loc_bundle_decoded = base64.b64decode(location_bundle['localizationBundle'])
loc_bundle_decoded = base64.b64decode(location_bundle["localizationBundle"])

parse_loc_zip(zipfile.ZipFile(io.BytesIO(loc_bundle_decoded)), _LANG_DIR)
print("Localization bundle parsed to %s", _LANG_DIR)
Loading
Loading