Skip to content

Commit 474af2e

Browse files
committed
version 0.1
1 parent 4c3e138 commit 474af2e

18 files changed

+2121
-261
lines changed

.env.example

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,21 @@
1+
# Required settings
12
GEMINI_API_KEY="YOUR_GEMINI_API_KEY"
23
WASENDER_API_TOKEN="YOUR_WASENDER_API_TOKEN"
3-
# Optional: If you change the port in script.py, update it here too for ngrok or other services
4-
# FLASK_RUN_PORT=5000
4+
5+
# Optional settings
6+
WEBHOOK_SECRET="YOUR_WEBHOOK_SECRET" # For additional webhook verification security
7+
GEMINI_MODEL="gemini-2.0-flash" # Or another model like "gemini-1.5-pro"
8+
CONVERSATIONS_DIR="conversations" # Directory to store conversation histories
9+
PERSONA_FILE_PATH="persona.json" # Path to your persona configuration file
10+
11+
# Performance settings
12+
MAX_RETRIES=3 # Maximum number of retry attempts for WaSenderAPI calls
13+
MESSAGE_CHUNK_MAX_LINES=3 # Max lines per WhatsApp message
14+
MESSAGE_CHUNK_MAX_CHARS=100 # Max characters per line in WhatsApp messages
15+
MESSAGE_DELAY_MIN=0.55 # Minimum delay between sequential messages (seconds)
16+
MESSAGE_DELAY_MAX=1.5 # Maximum delay between sequential messages (seconds)
17+
18+
# Server settings
19+
PORT=5001 # HTTP server port
20+
FLASK_DEBUG=False # Set to True in development only
21+
# Optional: LOG_LEVEL=INFO # DEBUG, INFO, WARNING, ERROR, CRITICAL
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
[
2+
{
3+
"role": "user",
4+
"parts": [
5+
"Hello, chatbot!"
6+
]
7+
},
8+
{
9+
"role": "model",
10+
"parts": [
11+
"Test response from Gemini"
12+
]
13+
},
14+
{
15+
"role": "user",
16+
"parts": [
17+
"Generate a long response"
18+
]
19+
},
20+
{
21+
"role": "model",
22+
"parts": [
23+
"Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\nLine 8"
24+
]
25+
},
26+
{
27+
"role": "user",
28+
"parts": [
29+
"Generate a long response"
30+
]
31+
},
32+
{
33+
"role": "model",
34+
"parts": [
35+
"Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\nLine 8"
36+
]
37+
},
38+
{
39+
"role": "user",
40+
"parts": [
41+
"Generate a long response"
42+
]
43+
},
44+
{
45+
"role": "model",
46+
"parts": [
47+
"Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\nLine 8"
48+
]
49+
}
50+
]

message_splitter.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import re
2+
3+
"""
4+
message_splitter.py - Implementation of the message splitting functionality
5+
"""
6+
7+
def split_message(text, max_lines=3, max_chars_per_line=100):
8+
"""
9+
The main message splitting function, used throughout the application.
10+
This is just an alias to keep backwards compatibility.
11+
"""
12+
return split_message_impl(text, max_lines, max_chars_per_line)
13+
14+
def split_message_impl(text, max_lines=3, max_chars_per_line=100):
15+
"""
16+
Split a long message into smaller chunks for better WhatsApp readability.
17+
This improved implementation properly handles long lines without newlines.
18+
19+
Args:
20+
text: The text to split
21+
max_lines: Maximum lines per message chunk
22+
max_chars_per_line: Maximum characters per line
23+
24+
Returns:
25+
List of message chunks ready to send
26+
"""
27+
if not text:
28+
return []
29+
30+
# Convert escaped newlines and normalize line endings
31+
normalized_text = text.replace('\\n', '\n').replace('\r\n', '\n')
32+
33+
# Remove standalone backslashes using regex
34+
normalized_text = re.sub(r'\n\s*\\\s*\n', '\n', normalized_text)
35+
normalized_text = re.sub(r'^\s*\\\s*\n', '', normalized_text)
36+
normalized_text = re.sub(r'\n\s*\\\s*$', '', normalized_text)
37+
38+
# Split by existing newlines
39+
paragraphs = normalized_text.split('\n')
40+
chunks = []
41+
current_chunk = []
42+
current_line_count = 0
43+
44+
for paragraph in paragraphs:
45+
# Handle empty paragraphs
46+
if not paragraph.strip():
47+
if current_line_count >= max_lines:
48+
if current_chunk:
49+
chunks.append('\n'.join(current_chunk))
50+
current_chunk = []
51+
current_line_count = 0
52+
53+
if current_line_count < max_lines:
54+
current_chunk.append('')
55+
current_line_count += 1
56+
continue
57+
58+
# For paragraphs longer than max_chars_per_line, break them up
59+
if len(paragraph) > max_chars_per_line:
60+
words = paragraph.split()
61+
62+
# Special case: single very long word
63+
if len(words) == 1:
64+
word = words[0]
65+
for i in range(0, len(word), max_chars_per_line):
66+
if current_line_count >= max_lines:
67+
if current_chunk:
68+
chunks.append('\n'.join(current_chunk))
69+
current_chunk = []
70+
current_line_count = 0
71+
current_chunk.append(word[i:i+max_chars_per_line])
72+
current_line_count += 1
73+
continue
74+
75+
# Regular case: paragraph with multiple words
76+
current_line = []
77+
current_length = 0
78+
79+
for word in words:
80+
# Handle very long words
81+
if len(word) > max_chars_per_line:
82+
# Add accumulated words first
83+
if current_line:
84+
if current_line_count >= max_lines:
85+
if current_chunk:
86+
chunks.append('\n'.join(current_chunk))
87+
current_chunk = []
88+
current_line_count = 0
89+
current_chunk.append(' '.join(current_line))
90+
current_line_count += 1
91+
current_line = []
92+
current_length = 0
93+
94+
# Split the long word
95+
for i in range(0, len(word), max_chars_per_line):
96+
if current_line_count >= max_lines:
97+
if current_chunk:
98+
chunks.append('\n'.join(current_chunk))
99+
current_chunk = []
100+
current_line_count = 0
101+
current_chunk.append(word[i:i+max_chars_per_line])
102+
current_line_count += 1
103+
104+
# Normal word handling
105+
elif current_length + len(word) + (1 if current_line else 0) > max_chars_per_line:
106+
# Finalize current line
107+
if current_line:
108+
if current_line_count >= max_lines:
109+
if current_chunk:
110+
chunks.append('\n'.join(current_chunk))
111+
current_chunk = []
112+
current_line_count = 0
113+
current_chunk.append(' '.join(current_line))
114+
current_line_count += 1
115+
116+
# Start new line with this word
117+
current_line = [word]
118+
current_length = len(word)
119+
120+
else:
121+
# Word fits on current line
122+
if current_line:
123+
current_length += 1 # space
124+
current_line.append(word)
125+
current_length += len(word)
126+
127+
# Add the last line if it exists
128+
if current_line:
129+
if current_line_count >= max_lines:
130+
if current_chunk:
131+
chunks.append('\n'.join(current_chunk))
132+
current_chunk = []
133+
current_line_count = 0
134+
current_chunk.append(' '.join(current_line))
135+
current_line_count += 1
136+
137+
else:
138+
# Paragraph fits on one line
139+
if current_line_count >= max_lines:
140+
if current_chunk:
141+
chunks.append('\n'.join(current_chunk))
142+
current_chunk = []
143+
current_line_count = 0
144+
current_chunk.append(paragraph)
145+
current_line_count += 1
146+
147+
# Add the final chunk
148+
if current_chunk:
149+
chunks.append('\n'.join(current_chunk))
150+
151+
return chunks

pytest.ini

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[pytest]
2+
testpaths = tests
3+
python_files = test_*.py
4+
python_functions = test_*
5+
python_classes = Test*
6+
7+
[coverage:run]
8+
source = .
9+
omit =
10+
tests/*
11+
*/__pycache__/*
12+
*/venv/*
13+
*/virtualenv/*
14+
15+
[coverage:report]
16+
exclude_lines =
17+
pragma: no cover
18+
def __repr__
19+
if __name__ == .__main__.:

requirements-dev.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
pytest>=7.4.0
2+
pytest-cov>=4.1.0
3+
pytest-mock>=3.11.1
4+
pytest-asyncio>=0.21.1
5+
requests-mock>=1.11.0
6+
coverage>=7.3.2
7+
colorama>=0.4.6

requirements.txt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
flask
2-
requests
3-
python-dotenv
4-
google-generativeai
1+
flask>=2.2.0
2+
requests>=2.28.0
3+
python-dotenv>=1.0.0
4+
google-generativeai>=0.3.0
5+
wasenderapi>=0.3.1
6+
httpx>=0.23.0
7+
pydantic>=2.0.0
8+
gunicorn>=21.0.0 # For production deployment

0 commit comments

Comments
 (0)