AiDeveloper1 commited on
Commit
0bcc68d
·
verified ·
1 Parent(s): 1fb4f8b

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +362 -337
main.py CHANGED
@@ -1,338 +1,363 @@
1
- from fastapi import FastAPI, HTTPException, Request
2
- from fastapi.responses import HTMLResponse
3
- from fastapi.templating import Jinja2Templates
4
- from fastapi.staticfiles import StaticFiles
5
- from pydantic import HttpUrl
6
- from scraper import scrape_page
7
- from summarizer import quick_summarize
8
- from rich_card_builder import build_rich_card
9
- import asyncio
10
- from urllib.parse import urlparse
11
- import logging
12
- import http.client
13
- import json
14
- from dotenv import load_dotenv
15
- import os
16
- import google.generativeai as genai
17
- from typing import Optional, List, Dict
18
-
19
- # Load environment variables
20
- load_dotenv()
21
-
22
- # Set up logging
23
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
24
-
25
- app = FastAPI(title="Website Scraper API (Enhanced for RCS)")
26
-
27
- # Mount static files
28
- app.mount("/static", StaticFiles(directory="static"), name="static")
29
-
30
- # Set up Jinja2 templates
31
- templates = Jinja2Templates(directory="templates")
32
-
33
- # Sample rich cards for testing
34
- SAMPLE_RICH_CARDS = [
35
- {
36
- "title": "Summer Collection 2025",
37
- "text": "Discover vibrant summer styles at Pantaloons.",
38
- "media": "https://example.com/summer.jpg",
39
- "url": "https://example.com/summer",
40
- "buttons": [
41
- {"type": "weburl", "title": "View Now", "payload": "https://example.com/summer"},
42
- {"type": "postback", "title": "Learn More", "payload": "learn_more_1"}
43
- ],
44
- "quickReplies": [
45
- {"type": "postback", "title": "Show Similar", "payload": "similar_content_1"},
46
- {"type": "call", "title": "Contact Support", "payload": "+12345678901"}
47
- ]
48
- },
49
- {
50
- "title": "Yu Collection",
51
- "text": "Explore trendy youth fashion with Yu Collection.",
52
- "media": "https://example.com/yu.jpg",
53
- "url": "https://example.com/yu_collection",
54
- "buttons": [
55
- {"type": "weburl", "title": "View Now", "payload": "https://example.com/yu_collection"},
56
- {"type": "postback", "title": "Learn More", "payload": "learn_more_2"}
57
- ],
58
- "quickReplies": [
59
- {"type": "postback", "title": "Show Similar", "payload": "similar_content_2"},
60
- {"type": "call", "title": "Contact Support", "payload": "+12345678901"}
61
- ]
62
- }
63
- ]
64
-
65
- async def generate_dynamic_buttons(title: str, description: str, url: str, next_interaction: str = None) -> List[Dict]:
66
- """Generate dynamic quick reply buttons for the next interaction using Gemini-1.5 Flash."""
67
- try:
68
- # Validate inputs
69
- title = title.strip() if title and title.strip() else "News Summary"
70
- description = description.strip() if description and description.strip() else "Explore news and insights."
71
- url = url.strip() if url and url.strip() else "https://example.com"
72
-
73
- logging.info(f"Generating buttons for: title={title}, description={description[:30]}..., url={url}")
74
-
75
- # Get Gemini API key
76
- api_key = os.getenv("GEMINI_API_KEY")
77
- if not api_key:
78
- logging.error("Gemini API key not found. Please set GEMINI_API_KEY in .env file.")
79
- return [{"type": "postback", "title": "Explore More", "payload": f"goto_{next_interaction}", "execute": next_interaction}] if next_interaction else []
80
-
81
- # Configure Gemini client
82
- genai.configure(api_key=api_key)
83
- model = genai.GenerativeModel('gemini-1.5-flash')
84
-
85
- # Combine inputs (no chunking, as input is small)
86
- input_text = f"Title: {title}\nDescription: {description}\nURL: {url}"
87
- input_text = input_text[:500] # Truncate to 500 chars to stay within limits
88
-
89
- # Optimized prompt
90
- prompt = (
91
- f"Based on the following content for the next interaction, suggest up to two concise (3-8 words) quick reply button titles that are action-oriented and invite the user to explore this content. The buttons should be engaging, relevant to the content, and avoid generic terms like 'Show Next'. Return the titles as a JSON array of strings.\n\n"
92
- f"{input_text}\n\n"
93
- f"Example output: [\"Discover Yu Collection\", \"Shop Youth Styles\"]\n"
94
- f"Return only the JSON array, no markdown or extra text."
95
- )
96
-
97
- # Retry mechanism for API calls
98
- max_retries = 3
99
- for attempt in range(max_retries):
100
- try:
101
- response = await model.generate_content_async(prompt)
102
- raw_content = response.text.strip()
103
- logging.info(f"Gemini response: {raw_content}")
104
-
105
- # Remove markdown code block markers if present
106
- raw_content = raw_content.strip('```json').strip('```').strip()
107
-
108
- # Parse response
109
- button_titles = json.loads(raw_content)
110
- if not isinstance(button_titles, list) or not all(isinstance(t, str) for t in button_titles):
111
- logging.warning(f"Invalid Gemini response format: {raw_content}")
112
- raise ValueError("Response is not a list of strings")
113
-
114
- # Filter valid button titles
115
- valid_buttons = [t.strip() for t in button_titles if t.strip() and 3 <= len(t.strip().split()) <= 8]
116
- if not valid_buttons:
117
- logging.warning("No valid button titles in response")
118
- raise ValueError("No valid button titles")
119
-
120
- # Create quick replies
121
- quick_replies = [
122
- {
123
- "type": "postback",
124
- "title": title[:20], # Ensure concise title
125
- "payload": f"goto_{next_interaction}_{i}",
126
- "execute": next_interaction
127
- }
128
- for i, title in enumerate(valid_buttons[:2])
129
- ]
130
- logging.info(f"Generated quick replies: {quick_replies}")
131
- return quick_replies
132
-
133
- except Exception as e:
134
- logging.warning(f"Attempt {attempt + 1} failed: {str(e)}")
135
- if attempt < max_retries - 1:
136
- await asyncio.sleep(1) # Wait before retrying
137
- continue
138
-
139
- # Fallback if all retries fail
140
- logging.error("All retries failed for button generation")
141
- return [{"type": "postback", "title": "Explore More", "payload": f"goto_{next_interaction}", "execute": next_interaction}] if next_interaction else []
142
-
143
- except Exception as e:
144
- logging.error(f"Error generating dynamic buttons: {str(e)}")
145
- return [{"type": "postback", "title": "Explore More", "payload": f"goto_{next_interaction}", "execute": next_interaction}] if next_interaction else []
146
-
147
- async def create_nativemsg_bot(rich_cards: List[Dict], url: str, bot_name: str, api_token: str) -> Dict:
148
- """Create a bot on NativeMSG with connected interactions based on rich cards."""
149
- try:
150
- # Validate API token
151
- if not api_token:
152
- logging.error("NativeMSG API token not provided and not found in .env file.")
153
- raise ValueError("NativeMSG API token is required.")
154
-
155
- # Use provided bot name or default to dynamic name
156
- final_bot_name = bot_name or f"Bot for {urlparse(url).netloc}"
157
-
158
- # Prepare bot payload
159
- interactions = []
160
-
161
- for idx, card in enumerate(rich_cards, 1):
162
- # Determine the next interaction and next card (if any)
163
- next_interaction = f"Interaction #{idx + 1}" if idx < len(rich_cards) else None
164
- next_card = rich_cards[idx] if idx < len(rich_cards) else None
165
-
166
- # Get buttons from current card
167
- buttons = card.get("buttons", [])
168
-
169
- # Generate dynamic quick replies based on *next* card
170
- dynamic_quick_replies = []
171
- if next_card:
172
- dynamic_quick_replies = await generate_dynamic_buttons(
173
- title=next_card.get("title", "News Summary"),
174
- description=next_card.get("text", "Explore news and insights."),
175
- url=next_card.get("url", ""),
176
- next_interaction=next_interaction
177
- )
178
-
179
- # Define quick replies
180
- quick_replies = dynamic_quick_replies + [
181
- {
182
- "type": "call",
183
- "title": "Contact Support",
184
- "payload": "+12345678901"
185
- }
186
- ]
187
-
188
- # Build message for current card
189
- message = {
190
- "text": f"{card['title']}\n\n{card['text']}",
191
- "mediaType": "image",
192
- "media": card.get("media", "") or "https://example.com/placeholder.jpg",
193
- "richCard": {
194
- "cardOrientation": "VERTICAL",
195
- "mediaHeight": "MEDIUM"
196
- },
197
- "buttons": buttons,
198
- "quickReplies": quick_replies
199
- }
200
-
201
- # Build interaction
202
- interaction = {
203
- "name": f"Interaction #{idx}",
204
- "intents": ["show_content", f"content_{idx}"],
205
- "actions": [
206
- {
207
- "send": {
208
- "message": message
209
- },
210
- "type": "RichCard",
211
- "name": f"Send Rich Card #{idx}"
212
- }
213
- ]
214
- }
215
- interactions.append(interaction)
216
-
217
- # Add welcome interaction
218
- welcome_message = {
219
- "text": f"Welcome to the {urlparse(url).netloc} RCS Bot! Explore the latest content.",
220
- "richCard": {
221
- "cardOrientation": "VERTICAL",
222
- "mediaHeight": "MEDIUM"
223
- },
224
- "quickReplies": [
225
- {
226
- "type": "postback",
227
- "title": "Start Exploring",
228
- "payload": "start_exploring",
229
- "execute": "Interaction #1"
230
- }
231
- ]
232
- }
233
- welcome_interaction = {
234
- "name": "Welcome Interaction",
235
- "intents": ["start", "welcome"],
236
- "actions": [
237
- {
238
- "send": {
239
- "message": welcome_message
240
- },
241
- "type": "RichCard",
242
- "name": "Send Welcome Message"
243
- }
244
- ]
245
- }
246
- interactions.insert(0, welcome_interaction)
247
-
248
- payload = {
249
- "name": final_bot_name,
250
- "interactions": interactions
251
- }
252
-
253
- # Log the payload for debugging
254
- logging.info(f"NativeMSG bot payload: {json.dumps(payload, indent=2)}")
255
-
256
- # Send request to NativeMSG API
257
- connection = http.client.HTTPSConnection("api.nativemsg.com")
258
- headers = {
259
- "Authorization": f"Bearer {api_token}",
260
- "Content-Type": "application/json"
261
- }
262
- connection.request("POST", "/v1/bots", json.dumps(payload), headers)
263
- response = connection.getresponse()
264
- response_data = response.read().decode('utf-8')
265
-
266
- logging.info(f"NativeMSG bot creation response: Status {response.status}, Data: {response_data}")
267
-
268
- if response.status != 200:
269
- logging.error(f"Failed to create bot: {response_data}")
270
- raise HTTPException(status_code=500, detail=f"Failed to create bot: {response_data}")
271
-
272
- return json.loads(response_data)
273
-
274
- except Exception as e:
275
- logging.error(f"Error creating NativeMSG bot: {str(e)}")
276
- raise HTTPException(status_code=500, detail=f"Failed to create bot: {str(e)}")
277
-
278
- @app.get("/scrape")
279
- async def crawl_website(
280
- url: HttpUrl,
281
- use_sample: bool = False,
282
- bot_name: Optional[str] = None,
283
- nativemsg_token: Optional[str] = None
284
- ):
285
- """Crawl a website or use sample data, generate rich cards, and create a NativeMSG bot."""
286
- try:
287
- # Determine API token
288
- api_token = nativemsg_token or os.getenv("NATIVEMSG_API_TOKEN")
289
-
290
- if use_sample:
291
- results = SAMPLE_RICH_CARDS
292
- else:
293
- visited = set()
294
- to_visit = {str(url)}
295
- base_domain = urlparse(str(url)).netloc
296
- results = []
297
-
298
- while to_visit and len(visited) < 20: # Limited to 10 for demo
299
- current_url = to_visit.pop()
300
- if current_url in visited:
301
- continue
302
- visited.add(current_url)
303
-
304
- logging.info(f"Scraping page: {current_url}")
305
- page_data, new_links = await scrape_page(current_url, visited, base_domain)
306
- if page_data:
307
- logging.info(f"Scraped data: {page_data}")
308
- summary = await quick_summarize(page_data["text"], page_data["url"])
309
- rich_card = build_rich_card(page_data, summary)
310
- rich_card["title"] = summary.get("title", "News Summary")
311
- rich_card["url"] = page_data.get("url", str(url))
312
- results.append(rich_card)
313
-
314
- to_visit.update(new_links)
315
- await asyncio.sleep(0.5)
316
-
317
- if not results:
318
- logging.error("No rich cards generated from scraping.")
319
- raise HTTPException(status_code=400, detail="No content scraped from the provided URL.")
320
-
321
- # Create NativeMSG bot with the rich cards
322
- bot_response = await create_nativemsg_bot(results, str(url), bot_name, api_token)
323
-
324
- logging.info(f"Final response: {results}, Bot: {bot_response}")
325
- return {"rich_cards": results, "bot_response": bot_response}
326
-
327
- except Exception as e:
328
- logging.error(f"Scraping or bot creation failed: {str(e)}")
329
- raise HTTPException(status_code=500, detail=f"Scraping or bot creation failed: {str(e)}")
330
-
331
- @app.get("/", response_class=HTMLResponse)
332
- async def serve_home(request: Request):
333
- """Serve the frontend HTML page."""
334
- return templates.TemplateResponse("index.html", {"request": request})
335
-
336
- if __name__ == "__main__":
337
- import uvicorn
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338
  uvicorn.run(app, host="0.0.0.0", port=8001)
 
1
+ from fastapi import FastAPI, HTTPException, Request
2
+ from fastapi.responses import HTMLResponse
3
+ from fastapi.templating import Jinja2Templates
4
+ from fastapi.staticfiles import StaticFiles
5
+ from pydantic import HttpUrl, EmailStr
6
+ from scraper import scrape_page
7
+ from summarizer import quick_summarize
8
+ from rich_card_builder import build_rich_card
9
+ from send_email import send_rcs_email
10
+ import asyncio
11
+ from urllib.parse import urlparse
12
+ import logging
13
+ import uuid
14
+ import json
15
+ import http.client
16
+ from dotenv import load_dotenv
17
+ import os
18
+ import google.generativeai as genai
19
+ from typing import Optional, List, Dict
20
+
21
+ # Load environment variables
22
+ load_dotenv()
23
+
24
+ # Set up logging
25
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
26
+
27
+ app = FastAPI(title="Website Scraper API (Enhanced for RCS)")
28
+
29
+ # Mount static files
30
+ app.mount("/static", StaticFiles(directory="static"), name="static")
31
+
32
+ # Set up Jinja2 templates
33
+ templates = Jinja2Templates(directory="templates")
34
+
35
+ # In-memory session storage
36
+ sessions = {}
37
+
38
+ async def generate_dynamic_buttons(title: str, description: str, url: str, next_interaction: str = None) -> List[Dict]:
39
+ """Generate dynamic quick reply buttons for the next interaction using Gemini-1.5 pro."""
40
+ try:
41
+ # Validate inputs with defaults
42
+ title = title.strip() if title and title.strip() else "News Summary"
43
+ description = description.strip() if description and description.strip() else "Explore news and insights."
44
+ url = url.strip() if url and url.strip() else "https://example.com"
45
+
46
+ logging.info(f"Generating buttons for: title={title}, description={description[:30]}..., url={url}")
47
+
48
+ # Get Gemini API key
49
+ api_key = os.getenv("GEMINI_API_KEY")
50
+ if not api_key:
51
+ logging.error("Gemini API key not found. Please set GEMINI_API_KEY in .env file.")
52
+ return [
53
+ {
54
+ "type": "postback",
55
+ "title": "View Details",
56
+ "payload": f"goto_{next_interaction}",
57
+ "execute": next_interaction
58
+ }
59
+ ] if next_interaction else []
60
+
61
+ # Configure Gemini client
62
+ genai.configure(api_key=api_key)
63
+ model = genai.GenerativeModel('gemini-1.5-pro')
64
+ # Combine inputs
65
+ input_text = f"Title: {title}\nDescription: {description}\nURL: {url}"
66
+ input_text = input_text[:500] # Truncate to avoid exceeding limits
67
+
68
+ # Optimized prompt for dynamic, contextually relevant buttons
69
+ prompt = (
70
+ f"Based on the following content, generate up to two concise (3-8 words) quick reply button titles that are action-oriented, engaging, and relevant to the content. Avoid generic terms like 'Show Next' or 'Explore More'. Return the titles as a JSON array of strings.\n\n"
71
+ f"Content:\n{input_text}\n\n"
72
+ f"Example output: [\"Discover New Styles\", \"Shop Now Online\"]\n"
73
+ f"Return only the JSON array, no markdown or extra text."
74
+ )
75
+
76
+ # Retry mechanism for API calls
77
+ max_retries = 3
78
+ for attempt in range(max_retries):
79
+ try:
80
+ response = await model.generate_content_async(prompt)
81
+ raw_content = response.text.strip()
82
+ logging.info(f"Gemini response: {raw_content}")
83
+
84
+ # Remove markdown code block markers if present
85
+ raw_content = raw_content.strip('```json').strip('```').strip()
86
+
87
+ # Parse response
88
+ button_titles = json.loads(raw_content)
89
+ if not isinstance(button_titles, list) or not all(isinstance(t, str) for t in button_titles):
90
+ logging.warning(f"Invalid Gemini response format: {raw_content}")
91
+ raise ValueError("Response is not a list of strings")
92
+
93
+ # Filter valid button titles
94
+ valid_buttons = [t.strip() for t in button_titles if t.strip() and 3 <= len(t.strip().split()) <= 8]
95
+ if not valid_buttons:
96
+ logging.warning("No valid button titles in response")
97
+ raise ValueError("No valid button titles")
98
+
99
+ # Create quick replies
100
+ quick_replies = [
101
+ {
102
+ "type": "postback",
103
+ "title": title,
104
+ "payload": f"goto_{next_interaction}_{i}",
105
+ "execute": next_interaction
106
+ }
107
+ for i, title in enumerate(valid_buttons[:2])
108
+ ]
109
+ logging.info(f"Generated quick replies: {quick_replies}")
110
+ return quick_replies
111
+
112
+ except Exception as e:
113
+ logging.warning(f"Attempt {attempt + 1} failed: {str(e)}")
114
+ if attempt < max_retries - 1:
115
+ await asyncio.sleep(1) # Wait before retrying
116
+ continue
117
+
118
+ # Fallback if all retries fail
119
+ logging.error("All retries failed for button generation")
120
+ return [
121
+ {
122
+ "type": "postback",
123
+ "title": "View Details",
124
+ "payload": f"goto_{next_interaction}",
125
+ "execute": next_interaction
126
+ }
127
+ ] if next_interaction else []
128
+
129
+ except Exception as e:
130
+ logging.error(f"Error generating dynamic buttons: {str(e)}")
131
+ return [
132
+ {
133
+ "type": "postback",
134
+ "title": "View Details",
135
+ "payload": f"goto_{next_interaction}",
136
+ "execute": next_interaction
137
+ }
138
+ ] if next_interaction else []
139
+
140
+ async def create_nativemsg_bot(rich_cards: List[Dict], url: str, bot_name: str, api_token: str) -> Dict:
141
+ """Create a bot on NativeMSG with connected interactions based on rich cards."""
142
+ try:
143
+ # Validate API token
144
+ if not api_token:
145
+ logging.error("NativeMSG API token not provided and not found in .env file.")
146
+ raise ValueError("NativeMSG API token is required.")
147
+
148
+ # Use provided bot name or default to dynamic name
149
+ final_bot_name = bot_name or f"Bot for {urlparse(url).netloc}"
150
+
151
+ # Prepare bot payload
152
+ interactions = []
153
+
154
+ for idx, card in enumerate(rich_cards, 1):
155
+ # Build interaction using the original rich card structure
156
+ message = {
157
+ "text": f"{card['title']}\n\n{card['text']}",
158
+ "mediaType": "image",
159
+ "media": card.get("media", "") or "https://example.com/placeholder.jpg",
160
+ "richCard": {
161
+ "cardOrientation": "VERTICAL",
162
+ "mediaHeight": "MEDIUM"
163
+ },
164
+ "buttons": card.get("buttons", []),
165
+ "quickReplies": card.get("quickReplies", [])
166
+ }
167
+
168
+ # Build interaction
169
+ interaction = {
170
+ "name": f"Interaction #{idx}",
171
+ "intents": ["show_content", f"content_{idx}"],
172
+ "actions": [
173
+ {
174
+ "send": {
175
+ "message": message
176
+ },
177
+ "type": "RichCard",
178
+ "name": f"Send Rich Card #{idx}"
179
+ }
180
+ ]
181
+ }
182
+ interactions.append(interaction)
183
+
184
+ # Add welcome interaction
185
+ welcome_message = {
186
+ "text": f"Welcome to the {urlparse(url).netloc} RCS Bot! Explore the latest content.",
187
+ "richCard": {
188
+ "cardOrientation": "VERTICAL",
189
+ "mediaHeight": "MEDIUM"
190
+ },
191
+ "quickReplies": [
192
+ {
193
+ "type": "postback",
194
+ "title": "Start Exploring",
195
+ "payload": "start_exploring",
196
+ "execute": "Interaction #1"
197
+ }
198
+ ]
199
+ }
200
+ welcome_interaction = {
201
+ "name": "Welcome Interaction",
202
+ "intents": ["start", "welcome"],
203
+ "actions": [
204
+ {
205
+ "send": {
206
+ "message": welcome_message
207
+ },
208
+ "type": "RichCard",
209
+ "name": "Send Welcome Message"
210
+ }
211
+ ]
212
+ }
213
+ interactions.insert(0, welcome_interaction)
214
+
215
+ payload = {
216
+ "name": final_bot_name,
217
+ "interactions": interactions
218
+ }
219
+
220
+ # Log the payload for debugging
221
+ logging.info(f"NativeMSG bot payload: {json.dumps(payload, indent=2)}")
222
+
223
+ # Send request to NativeMSG API
224
+ connection = http.client.HTTPSConnection("api.nativemsg.com")
225
+ headers = {
226
+ "Authorization": f"Bearer {api_token}",
227
+ "Content-Type": "application/json"
228
+ }
229
+ connection.request("POST", "/v1/bots", json.dumps(payload), headers)
230
+ response = connection.getresponse()
231
+ response_data = response.read().decode('utf-8')
232
+
233
+ logging.info(f"NativeMSG bot creation response: Status {response.status}, Data: {response_data}")
234
+
235
+ if response.status != 200:
236
+ logging.error(f"Failed to create bot: {response_data}")
237
+ raise HTTPException(status_code=500, detail=f"Failed to create bot: {response_data}")
238
+
239
+ return json.loads(response_data)
240
+
241
+ except Exception as e:
242
+ logging.error(f"Error creating NativeMSG bot: {str(e)}")
243
+ raise HTTPException(status_code=500, detail=f"Failed to create bot: {str(e)}")
244
+
245
+ @app.get("/scrape")
246
+ async def crawl_website(
247
+ url: HttpUrl,
248
+ email: EmailStr,
249
+ bot_name: Optional[str] = None,
250
+ nativemsg_token: Optional[str] = None
251
+ ):
252
+ """Crawl a website, generate rich cards with dynamic buttons, create a NativeMSG bot, and send an email with a link to view the RCS."""
253
+ try:
254
+ # Determine API token
255
+ api_token = nativemsg_token or os.getenv("NATIVEMSG_API_TOKEN")
256
+
257
+ # Scrape the website
258
+ visited = set()
259
+ to_visit = {str(url)}
260
+ base_domain = urlparse(str(url)).netloc
261
+ results = []
262
+
263
+ while to_visit and len(visited) < 20: # Limited to 20 for demo
264
+ current_url = to_visit.pop()
265
+ if current_url in visited:
266
+ continue
267
+ visited.add(current_url)
268
+
269
+ logging.info(f"Scraping page: {current_url}")
270
+ page_data, new_links = await scrape_page(current_url, visited, base_domain)
271
+ if page_data:
272
+ logging.info(f"Scraped data: {page_data}")
273
+ summary = await quick_summarize(page_data["text"], page_data["url"])
274
+ rich_card = build_rich_card(page_data, summary)
275
+ rich_card["title"] = summary.get("title", "News Summary")
276
+ rich_card["url"] = page_data.get("url", str(url))
277
+ results.append(rich_card)
278
+
279
+ to_visit.update(new_links)
280
+ await asyncio.sleep(0.5)
281
+
282
+ if not results:
283
+ logging.error("No rich cards generated from scraping.")
284
+ raise HTTPException(status_code=400, detail="No content scraped from the provided URL.")
285
+
286
+ # Generate dynamic quick replies for each rich card
287
+ for idx, card in enumerate(results):
288
+ next_interaction = f"Interaction #{idx + 2}" if idx < len(results) - 1 else None
289
+ next_card = results[idx + 1] if idx < len(results) - 1 else None
290
+
291
+ dynamic_quick_replies = []
292
+ if next_card:
293
+ dynamic_quick_replies = await generate_dynamic_buttons(
294
+ title=next_card.get("title", "News Summary"),
295
+ description=next_card.get("text", "Explore news and insights."),
296
+ url=next_card.get("url", ""),
297
+ next_interaction=next_interaction
298
+ )
299
+ else:
300
+ # Fallback for the last card to ensure it has a button
301
+ dynamic_quick_replies = await generate_dynamic_buttons(
302
+ title=card.get("title", "News Summary"),
303
+ description=card.get("text", "Explore news and insights."),
304
+ url=card.get("url", ""),
305
+ next_interaction=None
306
+ )
307
+
308
+ # Update the rich card's quickReplies
309
+ card["quickReplies"] = dynamic_quick_replies + [
310
+ {
311
+ "type": "call",
312
+ "title": "Contact Support",
313
+ "payload": "+12345678901"
314
+ }
315
+ ]
316
+
317
+ # Create NativeMSG bot with the rich cards
318
+ bot_response = await create_nativemsg_bot(results, str(url), bot_name, api_token)
319
+
320
+ # Store the results
321
+ session_id = str(uuid.uuid4())
322
+ sessions[session_id] = {
323
+ "rich_cards": results,
324
+ "bot_response": bot_response
325
+ }
326
+ logging.info(f"Session created with ID: {session_id}, Session data: {sessions[session_id]}")
327
+
328
+ # Generate the direct link to view the RCS
329
+ direct_link = f"http://localhost:8001/view-rcs/{session_id}"
330
+ logging.info(f"Generated direct link: {direct_link}")
331
+
332
+ # Send email with the direct link
333
+ await send_rcs_email(email, direct_link)
334
+
335
+ logging.info(f"Final response: {results}, Bot: {bot_response}, Email sent to: {email}, Session ID: {session_id}")
336
+ return {"rich_cards": results, "bot_response": bot_response, "view_link": direct_link}
337
+
338
+ except Exception as e:
339
+ logging.error(f"Scraping or bot creation failed: {str(e)}")
340
+ raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
341
+
342
+ @app.get("/view-rcs/{session_id}", response_class=HTMLResponse)
343
+ async def view_rcs(session_id: str, request: Request):
344
+ """Serve the RCS cards for a specific session ID."""
345
+ logging.info(f"Attempting to access session with ID: {session_id}")
346
+ logging.info(f"Current sessions: {list(sessions.keys())}")
347
+
348
+ if session_id not in sessions:
349
+ logging.error(f"Session ID {session_id} not found in sessions.")
350
+ raise HTTPException(status_code=404, detail="Session not found.")
351
+
352
+ rich_cards = sessions[session_id]["rich_cards"]
353
+ logging.info(f"Retrieved session data for ID {session_id}: {rich_cards}")
354
+ return templates.TemplateResponse("rcs_view.html", {"request": request, "rich_cards": rich_cards})
355
+
356
+ @app.get("/", response_class=HTMLResponse)
357
+ async def serve_home(request: Request):
358
+ """Serve the frontend HTML page."""
359
+ return templates.TemplateResponse("index.html", {"request": request})
360
+
361
+ if __name__ == "__main__":
362
+ import uvicorn
363
  uvicorn.run(app, host="0.0.0.0", port=8001)