dialog-forge 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dialog_forge-0.1.0/PKG-INFO +126 -0
- dialog_forge-0.1.0/README.md +103 -0
- dialog_forge-0.1.0/pyproject.toml +35 -0
- dialog_forge-0.1.0/setup.cfg +4 -0
- dialog_forge-0.1.0/src/dialog_forge/__init__.py +12 -0
- dialog_forge-0.1.0/src/dialog_forge/api.py +78 -0
- dialog_forge-0.1.0/src/dialog_forge/exporters.py +109 -0
- dialog_forge-0.1.0/src/dialog_forge/models.py +115 -0
- dialog_forge-0.1.0/src/dialog_forge/platforms/__init__.py +16 -0
- dialog_forge-0.1.0/src/dialog_forge/platforms/facebook.py +419 -0
- dialog_forge-0.1.0/src/dialog_forge/platforms/instagram.py +324 -0
- dialog_forge-0.1.0/src/dialog_forge/platforms/whatsapp.py +288 -0
- dialog_forge-0.1.0/src/dialog_forge/renderer.py +78 -0
- dialog_forge-0.1.0/src/dialog_forge.egg-info/PKG-INFO +126 -0
- dialog_forge-0.1.0/src/dialog_forge.egg-info/SOURCES.txt +19 -0
- dialog_forge-0.1.0/src/dialog_forge.egg-info/dependency_links.txt +1 -0
- dialog_forge-0.1.0/src/dialog_forge.egg-info/requires.txt +7 -0
- dialog_forge-0.1.0/src/dialog_forge.egg-info/top_level.txt +1 -0
- dialog_forge-0.1.0/tests/test_api.py +59 -0
- dialog_forge-0.1.0/tests/test_models.py +96 -0
- dialog_forge-0.1.0/tests/test_renderers.py +100 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dialog-forge
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Generate realistic chat conversation mocks from dict/JSON input
|
|
5
|
+
Author: Dialog Forge Contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: chat,mock,conversation,whatsapp,instagram,facebook
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Provides-Extra: export
|
|
19
|
+
Requires-Dist: playwright>=1.40; extra == "export"
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
22
|
+
Requires-Dist: playwright>=1.40; extra == "dev"
|
|
23
|
+
|
|
24
|
+
# Dialog Forge
|
|
25
|
+
|
|
26
|
+
Generate realistic chat conversation mocks from dict/JSON input with platform-specific styling.
|
|
27
|
+
|
|
28
|
+
## Supported Platforms
|
|
29
|
+
|
|
30
|
+
- **WhatsApp** — green bubbles, double-check marks, timestamps
|
|
31
|
+
- **Instagram** — gradient bubbles, circular avatars, "Seen" labels
|
|
32
|
+
- **Facebook Messenger** — blue bubbles, rounded UI, delivery indicators
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install dialog-forge
|
|
38
|
+
|
|
39
|
+
# With image export support (PNG/JPG via Playwright)
|
|
40
|
+
pip install dialog-forge[export]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from dialog_forge import render_chat
|
|
47
|
+
|
|
48
|
+
conversation = {
|
|
49
|
+
"platform": "whatsapp",
|
|
50
|
+
"title": "Family Group",
|
|
51
|
+
"participants": {
|
|
52
|
+
"me": {"name": "Alice", "avatar": None},
|
|
53
|
+
"friend": {"name": "Bob", "avatar": None},
|
|
54
|
+
},
|
|
55
|
+
"messages": [
|
|
56
|
+
{
|
|
57
|
+
"sender": "friend",
|
|
58
|
+
"text": "Hey! Are you coming tonight?",
|
|
59
|
+
"timestamp": "18:30",
|
|
60
|
+
"status": "read",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"sender": "me",
|
|
64
|
+
"text": "Yes! On my way 🚗",
|
|
65
|
+
"timestamp": "18:32",
|
|
66
|
+
"status": "delivered",
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# Get HTML string
|
|
72
|
+
html = render_chat(conversation)
|
|
73
|
+
|
|
74
|
+
# Get PNG bytes
|
|
75
|
+
png_bytes = render_chat(conversation, format="png")
|
|
76
|
+
|
|
77
|
+
# Debug mode — prints the intermediate HTML path
|
|
78
|
+
html_debug = render_chat(conversation, format="png", debug=True)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## API
|
|
82
|
+
|
|
83
|
+
### `render_chat(data, format="html", debug=False)`
|
|
84
|
+
|
|
85
|
+
| Parameter | Type | Default | Description |
|
|
86
|
+
|-----------|--------|---------|-------------|
|
|
87
|
+
| `data` | `dict` | — | Conversation data (see schema below) |
|
|
88
|
+
| `format` | `str` | `"html"` | Output format: `"html"`, `"png"`, or `"jpg"` |
|
|
89
|
+
| `debug` | `bool` | `False` | When `True`, saves intermediate HTML to a temp file and prints its path |
|
|
90
|
+
|
|
91
|
+
**Returns:** `str` (HTML) or `bytes` (image data)
|
|
92
|
+
|
|
93
|
+
### Conversation Schema
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"platform": "whatsapp | instagram | facebook",
|
|
98
|
+
"title": "Chat Title",
|
|
99
|
+
"participants": {
|
|
100
|
+
"sender_id": {
|
|
101
|
+
"name": "Display Name",
|
|
102
|
+
"avatar": "https://url-to-avatar.png (optional)",
|
|
103
|
+
"gender": "male | female (optional)"
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"messages": [
|
|
107
|
+
{
|
|
108
|
+
"sender": "sender_id",
|
|
109
|
+
"text": "Message content",
|
|
110
|
+
"timestamp": "HH:MM",
|
|
111
|
+
"status": "sent | delivered | read",
|
|
112
|
+
"type": "text"
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Adding a New Platform
|
|
119
|
+
|
|
120
|
+
1. Create `src/dialog_forge/platforms/your_platform.py`
|
|
121
|
+
2. Subclass `BaseRenderer` and implement `render(conversation) -> str`
|
|
122
|
+
3. Register it in `src/dialog_forge/platforms/__init__.py`
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Dialog Forge
|
|
2
|
+
|
|
3
|
+
Generate realistic chat conversation mocks from dict/JSON input with platform-specific styling.
|
|
4
|
+
|
|
5
|
+
## Supported Platforms
|
|
6
|
+
|
|
7
|
+
- **WhatsApp** — green bubbles, double-check marks, timestamps
|
|
8
|
+
- **Instagram** — gradient bubbles, circular avatars, "Seen" labels
|
|
9
|
+
- **Facebook Messenger** — blue bubbles, rounded UI, delivery indicators
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install dialog-forge
|
|
15
|
+
|
|
16
|
+
# With image export support (PNG/JPG via Playwright)
|
|
17
|
+
pip install dialog-forge[export]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from dialog_forge import render_chat
|
|
24
|
+
|
|
25
|
+
conversation = {
|
|
26
|
+
"platform": "whatsapp",
|
|
27
|
+
"title": "Family Group",
|
|
28
|
+
"participants": {
|
|
29
|
+
"me": {"name": "Alice", "avatar": None},
|
|
30
|
+
"friend": {"name": "Bob", "avatar": None},
|
|
31
|
+
},
|
|
32
|
+
"messages": [
|
|
33
|
+
{
|
|
34
|
+
"sender": "friend",
|
|
35
|
+
"text": "Hey! Are you coming tonight?",
|
|
36
|
+
"timestamp": "18:30",
|
|
37
|
+
"status": "read",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"sender": "me",
|
|
41
|
+
"text": "Yes! On my way 🚗",
|
|
42
|
+
"timestamp": "18:32",
|
|
43
|
+
"status": "delivered",
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Get HTML string
|
|
49
|
+
html = render_chat(conversation)
|
|
50
|
+
|
|
51
|
+
# Get PNG bytes
|
|
52
|
+
png_bytes = render_chat(conversation, format="png")
|
|
53
|
+
|
|
54
|
+
# Debug mode — prints the intermediate HTML path
|
|
55
|
+
html_debug = render_chat(conversation, format="png", debug=True)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## API
|
|
59
|
+
|
|
60
|
+
### `render_chat(data, format="html", debug=False)`
|
|
61
|
+
|
|
62
|
+
| Parameter | Type | Default | Description |
|
|
63
|
+
|-----------|--------|---------|-------------|
|
|
64
|
+
| `data` | `dict` | — | Conversation data (see schema below) |
|
|
65
|
+
| `format` | `str` | `"html"` | Output format: `"html"`, `"png"`, or `"jpg"` |
|
|
66
|
+
| `debug` | `bool` | `False` | When `True`, saves intermediate HTML to a temp file and prints its path |
|
|
67
|
+
|
|
68
|
+
**Returns:** `str` (HTML) or `bytes` (image data)
|
|
69
|
+
|
|
70
|
+
### Conversation Schema
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"platform": "whatsapp | instagram | facebook",
|
|
75
|
+
"title": "Chat Title",
|
|
76
|
+
"participants": {
|
|
77
|
+
"sender_id": {
|
|
78
|
+
"name": "Display Name",
|
|
79
|
+
"avatar": "https://url-to-avatar.png (optional)",
|
|
80
|
+
"gender": "male | female (optional)"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"messages": [
|
|
84
|
+
{
|
|
85
|
+
"sender": "sender_id",
|
|
86
|
+
"text": "Message content",
|
|
87
|
+
"timestamp": "HH:MM",
|
|
88
|
+
"status": "sent | delivered | read",
|
|
89
|
+
"type": "text"
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Adding a New Platform
|
|
96
|
+
|
|
97
|
+
1. Create `src/dialog_forge/platforms/your_platform.py`
|
|
98
|
+
2. Subclass `BaseRenderer` and implement `render(conversation) -> str`
|
|
99
|
+
3. Register it in `src/dialog_forge/platforms/__init__.py`
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
MIT
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "dialog-forge"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Generate realistic chat conversation mocks from dict/JSON input"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Dialog Forge Contributors"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["chat", "mock", "conversation", "whatsapp", "instagram", "facebook"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Topic :: Software Development :: Libraries",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
export = ["playwright>=1.40"]
|
|
29
|
+
dev = ["pytest>=7.0", "playwright>=1.40"]
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["src"]
|
|
33
|
+
|
|
34
|
+
[tool.pytest.ini_options]
|
|
35
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dialog Forge — Generate realistic chat conversation mocks.
|
|
3
|
+
|
|
4
|
+
Public API:
|
|
5
|
+
render_chat(data, format="html", debug=False) -> str | bytes
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dialog_forge.api import render_chat
|
|
9
|
+
from dialog_forge.models import Conversation, Message, Sender
|
|
10
|
+
|
|
11
|
+
__all__ = ["render_chat", "Conversation", "Message", "Sender"]
|
|
12
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Public API for dialog_forge.
|
|
3
|
+
|
|
4
|
+
Provides the main entry point: render_chat()
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import tempfile
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
from dialog_forge.models import Conversation
|
|
13
|
+
from dialog_forge.renderer import RendererRegistry
|
|
14
|
+
|
|
15
|
+
# Ensure built-in platforms are registered on first import
|
|
16
|
+
import dialog_forge.platforms # noqa: F401
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def render_chat(
|
|
20
|
+
data: dict,
|
|
21
|
+
format: str = "html",
|
|
22
|
+
debug: bool = False,
|
|
23
|
+
) -> str | bytes:
|
|
24
|
+
"""
|
|
25
|
+
Render a chat conversation mock from a dict/JSON input.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
data: Dictionary describing the conversation (see README for schema).
|
|
29
|
+
format: Output format — "html", "png", or "jpg".
|
|
30
|
+
debug: When True and format is an image, saves the intermediate HTML
|
|
31
|
+
to a temp file and prints its path for visual tuning.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
str: The HTML document (when format="html").
|
|
35
|
+
bytes: Image data (when format="png" or "jpg").
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
ValueError: If the platform is not recognized or format is invalid.
|
|
39
|
+
ImportError: If image export is requested but Playwright is not installed.
|
|
40
|
+
"""
|
|
41
|
+
fmt = format.lower()
|
|
42
|
+
if fmt not in ("html", "png", "jpg", "jpeg"):
|
|
43
|
+
raise ValueError(
|
|
44
|
+
f"Unsupported format '{format}'. Use 'html', 'png', or 'jpg'."
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Parse input into a Conversation model
|
|
48
|
+
conversation = Conversation.from_dict(data)
|
|
49
|
+
|
|
50
|
+
# Select the renderer for the given platform
|
|
51
|
+
renderer = RendererRegistry.get(conversation.platform)
|
|
52
|
+
|
|
53
|
+
# Render to HTML
|
|
54
|
+
html = renderer.render(conversation)
|
|
55
|
+
|
|
56
|
+
# HTML output mode
|
|
57
|
+
if fmt == "html":
|
|
58
|
+
if debug:
|
|
59
|
+
path = _save_debug_html(html)
|
|
60
|
+
print(f"[dialog_forge debug] HTML saved to: {path}")
|
|
61
|
+
return html
|
|
62
|
+
|
|
63
|
+
# Image output mode
|
|
64
|
+
if debug:
|
|
65
|
+
path = _save_debug_html(html)
|
|
66
|
+
print(f"[dialog_forge debug] Intermediate HTML saved to: {path}")
|
|
67
|
+
|
|
68
|
+
from dialog_forge.exporters import export_to_image
|
|
69
|
+
|
|
70
|
+
return export_to_image(html, fmt=fmt)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _save_debug_html(html: str) -> str:
|
|
74
|
+
"""Save HTML to a temporary file and return its path."""
|
|
75
|
+
fd, path = tempfile.mkstemp(suffix=".html", prefix="dialog_forge_debug_")
|
|
76
|
+
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
|
77
|
+
f.write(html)
|
|
78
|
+
return path
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Image export functionality for dialog_forge.
|
|
3
|
+
|
|
4
|
+
Uses Playwright (headless Chromium) to render HTML to PNG or JPG.
|
|
5
|
+
Playwright is an optional dependency — install with:
|
|
6
|
+
pip install dialog-forge[export]
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import asyncio
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _check_playwright_available() -> None:
|
|
16
|
+
"""Raise a helpful error if Playwright is not installed."""
|
|
17
|
+
try:
|
|
18
|
+
import playwright # noqa: F401
|
|
19
|
+
except ImportError:
|
|
20
|
+
raise ImportError(
|
|
21
|
+
"Image export requires Playwright. "
|
|
22
|
+
"Install it with: pip install dialog-forge[export]\n"
|
|
23
|
+
"Then run: python -m playwright install chromium"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
async def _render_html_to_image(
|
|
28
|
+
html: str,
|
|
29
|
+
fmt: str = "png",
|
|
30
|
+
width: int = 420,
|
|
31
|
+
) -> bytes:
|
|
32
|
+
"""
|
|
33
|
+
Render an HTML string to image bytes using Playwright.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
html: Complete HTML document string.
|
|
37
|
+
fmt: Image format — "png" or "jpg"/"jpeg".
|
|
38
|
+
width: Viewport width in pixels (height auto-adjusts to content).
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Image bytes in the requested format.
|
|
42
|
+
"""
|
|
43
|
+
from playwright.async_api import async_playwright
|
|
44
|
+
|
|
45
|
+
fmt_lower = fmt.lower()
|
|
46
|
+
if fmt_lower in ("jpg", "jpeg"):
|
|
47
|
+
screenshot_type = "jpeg"
|
|
48
|
+
else:
|
|
49
|
+
screenshot_type = "png"
|
|
50
|
+
|
|
51
|
+
async with async_playwright() as p:
|
|
52
|
+
browser = await p.chromium.launch(headless=True)
|
|
53
|
+
page = await browser.new_page(viewport={"width": width, "height": 800})
|
|
54
|
+
|
|
55
|
+
await page.set_content(html, wait_until="networkidle")
|
|
56
|
+
|
|
57
|
+
# Auto-size the viewport height to fit the content
|
|
58
|
+
content_height = await page.evaluate(
|
|
59
|
+
"() => document.documentElement.scrollHeight"
|
|
60
|
+
)
|
|
61
|
+
await page.set_viewport_size({"width": width, "height": content_height})
|
|
62
|
+
|
|
63
|
+
screenshot_bytes = await page.screenshot(
|
|
64
|
+
type=screenshot_type,
|
|
65
|
+
full_page=True,
|
|
66
|
+
quality=95 if screenshot_type == "jpeg" else None,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
await browser.close()
|
|
70
|
+
|
|
71
|
+
return screenshot_bytes
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def export_to_image(
|
|
75
|
+
html: str,
|
|
76
|
+
fmt: str = "png",
|
|
77
|
+
width: int = 420,
|
|
78
|
+
) -> bytes:
|
|
79
|
+
"""
|
|
80
|
+
Synchronous wrapper to export HTML to an image.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
html: Complete HTML document string.
|
|
84
|
+
fmt: Image format — "png" or "jpg"/"jpeg".
|
|
85
|
+
width: Viewport width in pixels.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Image file bytes.
|
|
89
|
+
"""
|
|
90
|
+
_check_playwright_available()
|
|
91
|
+
|
|
92
|
+
# Handle event loop — if one is already running, use it; otherwise create one
|
|
93
|
+
try:
|
|
94
|
+
loop = asyncio.get_running_loop()
|
|
95
|
+
except RuntimeError:
|
|
96
|
+
loop = None
|
|
97
|
+
|
|
98
|
+
if loop and loop.is_running():
|
|
99
|
+
# We're inside an async context; run in a new thread to avoid deadlock
|
|
100
|
+
import concurrent.futures
|
|
101
|
+
|
|
102
|
+
with concurrent.futures.ThreadPoolExecutor() as pool:
|
|
103
|
+
future = pool.submit(
|
|
104
|
+
asyncio.run,
|
|
105
|
+
_render_html_to_image(html, fmt, width),
|
|
106
|
+
)
|
|
107
|
+
return future.result()
|
|
108
|
+
else:
|
|
109
|
+
return asyncio.run(_render_html_to_image(html, fmt, width))
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data models for dialog_forge.
|
|
3
|
+
|
|
4
|
+
Defines the core dataclasses used to represent a chat conversation:
|
|
5
|
+
- Sender: a participant in the conversation
|
|
6
|
+
- Message: a single chat message
|
|
7
|
+
- Conversation: the full conversation container
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class Sender:
|
|
18
|
+
"""Represents a chat participant."""
|
|
19
|
+
|
|
20
|
+
name: str
|
|
21
|
+
avatar: Optional[str] = None # URL or base64 data URI
|
|
22
|
+
gender: Optional[str] = None # "male", "female", or None
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def from_dict(cls, data: dict) -> Sender:
|
|
26
|
+
"""Create a Sender from a raw dictionary."""
|
|
27
|
+
return cls(
|
|
28
|
+
name=data["name"],
|
|
29
|
+
avatar=data.get("avatar"),
|
|
30
|
+
gender=data.get("gender"),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class Message:
|
|
36
|
+
"""Represents a single chat message."""
|
|
37
|
+
|
|
38
|
+
sender: str # Key referencing a participant in the conversation
|
|
39
|
+
text: str
|
|
40
|
+
timestamp: str = "" # Display string like "18:30"
|
|
41
|
+
status: str = "sent" # "sent", "delivered", "read"
|
|
42
|
+
type: str = "text" # "text" (extensible for future types)
|
|
43
|
+
|
|
44
|
+
# Resolved at render time — not expected from user input
|
|
45
|
+
_sender_obj: Optional[Sender] = field(default=None, repr=False)
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def from_dict(cls, data: dict) -> Message:
|
|
49
|
+
"""Create a Message from a raw dictionary."""
|
|
50
|
+
return cls(
|
|
51
|
+
sender=data["sender"],
|
|
52
|
+
text=data["text"],
|
|
53
|
+
timestamp=data.get("timestamp", ""),
|
|
54
|
+
status=data.get("status", "sent"),
|
|
55
|
+
type=data.get("type", "text"),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass
|
|
60
|
+
class Conversation:
|
|
61
|
+
"""
|
|
62
|
+
Represents a full chat conversation.
|
|
63
|
+
|
|
64
|
+
Attributes:
|
|
65
|
+
platform: Target platform for styling ("whatsapp", "instagram", "facebook").
|
|
66
|
+
title: Display title of the chat (e.g. contact name or group name).
|
|
67
|
+
participants: Mapping of participant IDs to Sender objects.
|
|
68
|
+
messages: Ordered list of Message objects.
|
|
69
|
+
me: The participant ID representing the local/owner user. Defaults to "me".
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
platform: str
|
|
73
|
+
title: str
|
|
74
|
+
participants: dict[str, Sender]
|
|
75
|
+
messages: list[Message]
|
|
76
|
+
me: str = "me" # Which participant ID is "us" (outgoing messages)
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def from_dict(cls, data: dict) -> Conversation:
|
|
80
|
+
"""
|
|
81
|
+
Parse a raw dictionary (or deserialized JSON) into a Conversation.
|
|
82
|
+
|
|
83
|
+
Expected schema:
|
|
84
|
+
{
|
|
85
|
+
"platform": "whatsapp",
|
|
86
|
+
"title": "Chat Title",
|
|
87
|
+
"me": "alice", # optional, defaults to "me"
|
|
88
|
+
"participants": {
|
|
89
|
+
"alice": {"name": "Alice", "avatar": null},
|
|
90
|
+
"bob": {"name": "Bob", "avatar": null},
|
|
91
|
+
},
|
|
92
|
+
"messages": [
|
|
93
|
+
{"sender": "bob", "text": "Hello!", "timestamp": "18:30", "status": "read"},
|
|
94
|
+
...
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
"""
|
|
98
|
+
participants = {
|
|
99
|
+
pid: Sender.from_dict(pdata)
|
|
100
|
+
for pid, pdata in data.get("participants", {}).items()
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
messages = [Message.from_dict(m) for m in data.get("messages", [])]
|
|
104
|
+
|
|
105
|
+
# Resolve sender objects on each message for convenience
|
|
106
|
+
for msg in messages:
|
|
107
|
+
msg._sender_obj = participants.get(msg.sender)
|
|
108
|
+
|
|
109
|
+
return cls(
|
|
110
|
+
platform=data["platform"],
|
|
111
|
+
title=data.get("title", "Chat"),
|
|
112
|
+
participants=participants,
|
|
113
|
+
messages=messages,
|
|
114
|
+
me=data.get("me", "me"),
|
|
115
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Platform renderer registration.
|
|
3
|
+
|
|
4
|
+
Importing this module registers all built-in platform renderers
|
|
5
|
+
with the global RendererRegistry.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dialog_forge.renderer import RendererRegistry
|
|
9
|
+
from dialog_forge.platforms.whatsapp import WhatsAppRenderer
|
|
10
|
+
from dialog_forge.platforms.instagram import InstagramRenderer
|
|
11
|
+
from dialog_forge.platforms.facebook import FacebookRenderer
|
|
12
|
+
|
|
13
|
+
# Register all built-in renderers
|
|
14
|
+
RendererRegistry.register("whatsapp", WhatsAppRenderer)
|
|
15
|
+
RendererRegistry.register("instagram", InstagramRenderer)
|
|
16
|
+
RendererRegistry.register("facebook", FacebookRenderer)
|