CLAUDE: Add smart text splitting for Discord embed fields
App Version: 2.0.4 Created reusable text_utils module with split_text_for_fields() function to handle rare play results that exceed Discord's 1024 character field limit. Changes: - New utils/text_utils.py with intelligent text splitting on delimiters - Updated commands/dice/rolls.py to split long rare play results into multiple fields - Automatic part indicators for multi-chunk results (e.g., "Part 1/2") - Handles outfield rare plays (~1135 chars) by splitting into 2 fields - Maintains single field for shorter infield/pitcher rare plays Benefits: - Fixes Discord field limit issue for outfield rare plays - Reusable utility for any future long-text scenarios - Clean breaks on semantic boundaries (between result types) - No multiple embeds needed - everything in single embed 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
3cc52169b1
commit
7937e51c6d
@ -17,6 +17,7 @@ from utils import team_utils
|
||||
from utils.logging import get_contextual_logger
|
||||
from utils.decorators import logged_command
|
||||
from utils.team_utils import get_user_major_league_team
|
||||
from utils.text_utils import split_text_for_fields
|
||||
from views.embeds import EmbedColors, EmbedTemplate
|
||||
|
||||
|
||||
@ -493,18 +494,31 @@ class DiceRollCommands(commands.Cog):
|
||||
inline=False
|
||||
)
|
||||
|
||||
# Add rare play
|
||||
if d100_result >= 1:
|
||||
# Add rare play or error result
|
||||
if d100_result == 1:
|
||||
error_result = self._get_rare_play(position, d20_result)
|
||||
base_field_name = "Rare Play Result"
|
||||
else:
|
||||
# Add error result
|
||||
error_result = self._get_error_result(position, d6_total)
|
||||
base_field_name = "Error Result"
|
||||
|
||||
if error_result:
|
||||
embed.add_field(
|
||||
name="Error Result",
|
||||
value=error_result,
|
||||
inline=False
|
||||
)
|
||||
# Split text if it exceeds Discord's field limit
|
||||
result_chunks = split_text_for_fields(error_result, max_length=1024)
|
||||
|
||||
# Add each chunk as a separate field
|
||||
for i, chunk in enumerate(result_chunks):
|
||||
field_name = base_field_name
|
||||
# Add part indicator if multiple chunks
|
||||
if len(result_chunks) > 1:
|
||||
field_name += f" (Part {i+1}/{len(result_chunks)})"
|
||||
|
||||
embed.add_field(
|
||||
name=field_name,
|
||||
value=chunk,
|
||||
inline=False
|
||||
)
|
||||
|
||||
# Add help commands
|
||||
embed.add_field(
|
||||
|
||||
92
utils/text_utils.py
Normal file
92
utils/text_utils.py
Normal file
@ -0,0 +1,92 @@
|
||||
"""
|
||||
Text Utility Functions
|
||||
|
||||
Provides text manipulation and formatting utilities for Discord bot operations.
|
||||
"""
|
||||
|
||||
|
||||
def split_text_for_fields(text: str, max_length: int = 1024, split_on: str = '\n') -> list[str]:
|
||||
"""
|
||||
Split text into chunks that fit Discord field value limits.
|
||||
|
||||
Discord embeds have a field value limit of 1024 characters. This function intelligently
|
||||
splits longer text into multiple chunks while preserving readability by splitting on
|
||||
semantic boundaries (default: newlines).
|
||||
|
||||
Args:
|
||||
text: Text to split into chunks
|
||||
max_length: Maximum characters per chunk (default 1024 for Discord field values)
|
||||
split_on: Character/string to split on for clean breaks (default newline)
|
||||
|
||||
Returns:
|
||||
List of text chunks, each under max_length characters
|
||||
|
||||
Examples:
|
||||
>>> short_text = "This is short"
|
||||
>>> split_text_for_fields(short_text)
|
||||
['This is short']
|
||||
|
||||
>>> long_text = "Line 1\\nLine 2\\nLine 3\\n..." * 100
|
||||
>>> chunks = split_text_for_fields(long_text, max_length=100)
|
||||
>>> all(len(chunk) <= 100 for chunk in chunks)
|
||||
True
|
||||
|
||||
>>> # Custom delimiter
|
||||
>>> text = "Part 1. Part 2. Part 3."
|
||||
>>> split_text_for_fields(text, max_length=10, split_on='. ')
|
||||
['Part 1.', 'Part 2.', 'Part 3.']
|
||||
|
||||
Notes:
|
||||
- If text is already under max_length, returns single-item list
|
||||
- Splits on boundaries to preserve formatting (no mid-line breaks)
|
||||
- Trailing delimiters are removed from final chunks
|
||||
- Empty segments are preserved if they exist in original text
|
||||
"""
|
||||
# Handle edge cases
|
||||
if not text:
|
||||
return ['']
|
||||
|
||||
if len(text) <= max_length:
|
||||
return [text]
|
||||
|
||||
chunks = []
|
||||
current_chunk = []
|
||||
current_length = 0
|
||||
|
||||
# Split on the delimiter (e.g., '\n')
|
||||
segments = text.split(split_on)
|
||||
|
||||
for i, segment in enumerate(segments):
|
||||
# Add delimiter back except for last segment
|
||||
is_last_segment = (i == len(segments) - 1)
|
||||
segment_with_delimiter = segment if is_last_segment else segment + split_on
|
||||
segment_length = len(segment_with_delimiter)
|
||||
|
||||
# If adding this segment would exceed limit, start new chunk
|
||||
if current_length + segment_length > max_length and current_chunk:
|
||||
# Save current chunk
|
||||
chunks.append(''.join(current_chunk).rstrip(split_on))
|
||||
current_chunk = []
|
||||
current_length = 0
|
||||
|
||||
# Handle edge case: single segment longer than max_length
|
||||
if segment_length > max_length:
|
||||
# Split mid-segment as last resort (shouldn't happen with reasonable max_length)
|
||||
if current_chunk:
|
||||
chunks.append(''.join(current_chunk).rstrip(split_on))
|
||||
current_chunk = []
|
||||
current_length = 0
|
||||
|
||||
# Split the long segment into character chunks
|
||||
for j in range(0, len(segment_with_delimiter), max_length):
|
||||
chunks.append(segment_with_delimiter[j:j + max_length])
|
||||
else:
|
||||
# Add segment to current chunk
|
||||
current_chunk.append(segment_with_delimiter)
|
||||
current_length += segment_length
|
||||
|
||||
# Add remaining chunk if any
|
||||
if current_chunk:
|
||||
chunks.append(''.join(current_chunk).rstrip(split_on))
|
||||
|
||||
return chunks
|
||||
Loading…
Reference in New Issue
Block a user