From d821e76589f844e951811e099af8d718f5f16f34 Mon Sep 17 00:00:00 2001 From: Timmy Time Date: Fri, 20 Mar 2026 11:57:53 -0400 Subject: [PATCH] [loop-cycle-1234] refactor: break up _generate_avatar_image (#563) (#589) --- src/timmy/mcp_tools.py | 84 +++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/src/timmy/mcp_tools.py b/src/timmy/mcp_tools.py index 120b4ab..62b8b9a 100644 --- a/src/timmy/mcp_tools.py +++ b/src/timmy/mcp_tools.py @@ -270,20 +270,8 @@ async def create_gitea_issue_via_mcp(title: str, body: str = "", labels: str = " return f"Failed to create issue via MCP: {exc}" -def _generate_avatar_image() -> bytes: - """Generate a Timmy-themed avatar image using Pillow. - - Creates a 512x512 wizard-themed avatar with emerald/purple/gold palette. - Returns raw PNG bytes. Falls back to a minimal solid-color image if - Pillow drawing primitives fail. - """ - from PIL import Image, ImageDraw - - size = 512 - img = Image.new("RGB", (size, size), (15, 25, 20)) - draw = ImageDraw.Draw(img) - - # Background gradient effect — concentric circles +def _draw_background(draw: "ImageDraw.ImageDraw", size: int) -> None: + """Draw radial gradient background with concentric circles.""" for i in range(size // 2, 0, -4): g = int(25 + (i / (size // 2)) * 30) draw.ellipse( @@ -291,33 +279,45 @@ def _generate_avatar_image() -> bytes: fill=(10, g, 20), ) - # Wizard hat (triangle) + +def _draw_wizard(draw: "ImageDraw.ImageDraw") -> None: + """Draw wizard hat, face, eyes, smile, monogram, and robe.""" hat_color = (100, 50, 160) # purple - draw.polygon( - [(256, 40), (160, 220), (352, 220)], - fill=hat_color, - outline=(180, 130, 255), - ) + hat_outline = (180, 130, 255) + gold = (220, 190, 50) + pupil = (30, 30, 60) - # Hat brim - draw.ellipse([140, 200, 372, 250], fill=hat_color, outline=(180, 130, 255)) + # Hat + brim + draw.polygon([(256, 40), (160, 220), (352, 220)], fill=hat_color, outline=hat_outline) + draw.ellipse([140, 200, 372, 250], fill=hat_color, outline=hat_outline) - # Face circle + # Face draw.ellipse([190, 220, 322, 370], fill=(60, 180, 100), outline=(80, 220, 120)) - # Eyes + # Eyes (whites + pupils) draw.ellipse([220, 275, 248, 310], fill=(255, 255, 255)) draw.ellipse([264, 275, 292, 310], fill=(255, 255, 255)) - draw.ellipse([228, 285, 242, 300], fill=(30, 30, 60)) - draw.ellipse([272, 285, 286, 300], fill=(30, 30, 60)) + draw.ellipse([228, 285, 242, 300], fill=pupil) + draw.ellipse([272, 285, 286, 300], fill=pupil) # Smile - draw.arc([225, 300, 287, 355], start=10, end=170, fill=(30, 30, 60), width=3) + draw.arc([225, 300, 287, 355], start=10, end=170, fill=pupil, width=3) - # Stars around the hat + # "T" monogram on hat + draw.text((243, 100), "T", fill=gold) + + # Robe + draw.polygon( + [(180, 370), (140, 500), (372, 500), (332, 370)], + fill=(40, 100, 70), + outline=(60, 160, 100), + ) + + +def _draw_stars(draw: "ImageDraw.ImageDraw") -> None: + """Draw decorative gold stars around the wizard hat.""" gold = (220, 190, 50) - star_positions = [(120, 100), (380, 120), (100, 300), (400, 280), (256, 10)] - for sx, sy in star_positions: + for sx, sy in [(120, 100), (380, 120), (100, 300), (400, 280), (256, 10)]: r = 8 draw.polygon( [ @@ -333,18 +333,26 @@ def _generate_avatar_image() -> bytes: fill=gold, ) - # "T" monogram on the hat - draw.text((243, 100), "T", fill=gold) - # Robe / body - draw.polygon( - [(180, 370), (140, 500), (372, 500), (332, 370)], - fill=(40, 100, 70), - outline=(60, 160, 100), - ) +def _generate_avatar_image() -> bytes: + """Generate a Timmy-themed avatar image using Pillow. + Creates a 512x512 wizard-themed avatar with emerald/purple/gold palette. + Returns raw PNG bytes. Falls back to a minimal solid-color image if + Pillow drawing primitives fail. + """ import io + from PIL import Image, ImageDraw + + size = 512 + img = Image.new("RGB", (size, size), (15, 25, 20)) + draw = ImageDraw.Draw(img) + + _draw_background(draw, size) + _draw_wizard(draw) + _draw_stars(draw) + buf = io.BytesIO() img.save(buf, format="PNG") return buf.getvalue()