diff --git a/README.md b/README.md index d9d3810b..bad866e1 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,57 @@ - Supports multiple user roles (User, Admin) for tailored access. - Utilizes Streamlit built-in auth system for google authentication. +#### 📁 Project Structure + +``` +jarvis/ +├── .github/ # GitHub configurations +│ ├── ISSUE_TEMPLATE/ # Issue templates for bug reports, feature requests +│ ├── workflows/ # GitHub Actions CI/CD workflows +│ └── PULL_REQUEST_TEMPLATE.md # PR template for contributors +├── .streamlit/ # Streamlit configuration files +├── assets/ # Images, GIFs, and media assets +├── src/ # Source code directory +│ ├── apps/ # Main application modules +│ │ ├── auth/ # Authentication system +│ │ ├── public/ # Public pages (home, YouTube playlist) +│ │ └── pages/ # Application pages organized by category +│ │ ├── automations/ # Automation tools +│ │ │ ├── Coding/ # Code-related automations +│ │ │ ├── Messenger/ # Email and messaging tools +│ │ │ ├── SocialMediaApps/ # Social media integrations +│ │ │ └── Websites/ # Website automation tools +│ │ ├── models/ # AI/ML model implementations +│ │ │ ├── ImageProcessing/ # Image processing models +│ │ │ ├── ObjectDetection/ # Object detection models +│ │ │ ├── Recommendation/ # Recommendation systems +│ │ │ └── Utility/ # Utility models +│ │ └── programs/ # Various programs and tools +│ │ ├── API/ # API integrations +│ │ ├── Games/ # Interactive games +│ │ ├── ImageGenerators/ # Image generation tools +│ │ ├── Simple/ # Simple utility programs +│ │ └── Study/ # Educational tools +│ ├── helpers/ # Helper functions and utilities +│ └── utils/ # Utility functions +├── Jarvis.py # Main application entry point +├── requirements.txt # Python dependencies +├── pyproject.toml # Project configuration +├── uv.lock # Dependency lock file +├── SETUP.md # Setup instructions +├── Contributing.md # Contribution guidelines +├── CODE_OF_CONDUCT.md # Code of conduct +└── README.md # Project documentation +``` + +The project follows a modular structure where each feature is implemented as a separate module within the appropriate category. The `src/apps/pages/` directory contains the main functionality organized by type: + +- **Automations**: Tools for automating repetitive tasks +- **Models**: AI/ML implementations for various use cases +- **Programs**: Interactive applications and utilities + +Each module is designed to be self-contained and follows the naming conventions specified in the contribution guidelines. +

:zap: Important Points to remember while submitting your work 📍

diff --git a/src/apps/auth/auth.py b/src/apps/auth/auth.py index 3586aa42..2ca50c27 100644 --- a/src/apps/auth/auth.py +++ b/src/apps/auth/auth.py @@ -4,7 +4,7 @@ import pytz import streamlit as st -from src.utils.greeting import GreetUser +from src.utils.greeting import GetRandomWelcomeMessage, GreetUser def unix_to_ist(timestamp): @@ -22,7 +22,7 @@ def auth(): else: st.title(f"🙏 {GreetUser(st.user.given_name)}") - st.success("Welcome to Jarvis AI Assistant!", icon="🤝") + st.success(GetRandomWelcomeMessage(), icon="🤝") st.image(st.user.picture, caption=st.user.name) st.write("Email:", st.user.email) diff --git a/src/apps/pages/automations/Messenger/whatsApp.py b/src/apps/pages/automations/Messenger/whatsApp.py index 67c9ac2b..6443617d 100644 --- a/src/apps/pages/automations/Messenger/whatsApp.py +++ b/src/apps/pages/automations/Messenger/whatsApp.py @@ -1,55 +1,212 @@ +import time + import pandas as pd +import pywhatkit import streamlit as st -# TODO: Rectify the error in the sendMsg function -def sendMsg(phone_no, message): - import time +def validatePhoneNumber(phone_no): + """ + Validate phone number format. + + Args: + phone_no (str): Phone number to validate + + Returns: + tuple: (is_valid, error_message) + """ + phone_str = str(phone_no).strip() + + # Remove common separators + phone_str = phone_str.replace(" ", "").replace("-", "").replace("(", "").replace(")", "") + + # Check if it starts with + + if not phone_str.startswith("+"): + return False, "Phone number must start with '+' followed by country code (e.g., +1234567890)" + + # Check if remaining characters are digits + if not phone_str[1:].isdigit(): + return False, "Phone number must contain only digits after the '+' sign" + + # Check minimum length (country code + number should be at least 10 digits) + if len(phone_str) < 11: + return False, "Phone number is too short. Include country code (e.g., +1234567890)" + + # Check maximum length (most phone numbers with country code are under 15 digits) + if len(phone_str) > 16: + return False, "Phone number is too long" + + return True, None + - import pyautogui - import pywhatkit +def sendMsg(phone_no, message, wait_time=15): + """ + Send WhatsApp message with improved error handling and validation. - pywhatkit.sendwhatmsg_instantly(phone_no, message) - time.sleep(1) - pyautogui.press("enter") - time.sleep(1) - pyautogui.hotkey("ctrl", "w") - time.sleep(1) + Args: + phone_no (str): Phone number with country code (e.g., +1234567890) + message (str): Message to send + wait_time (int): Time to wait before sending (default: 15 seconds) + + Returns: + tuple: (success, error_message) + """ + try: + # Validate phone number + is_valid, error_msg = validatePhoneNumber(phone_no) + if not is_valid: + return False, error_msg + + # Validate message + if not message or message.strip() == "": + return False, "Message cannot be empty" + + # Calculate send time (current time + wait_time seconds) + current_time = time.localtime() + hour = current_time.tm_hour + minute = current_time.tm_min + (wait_time // 60) + + # Handle minute overflow + if minute >= 60: + hour += minute // 60 + minute = minute % 60 + + # Handle hour overflow + if hour >= 24: + hour = hour % 24 + + # Send message using pywhatkit with proper timing + pywhatkit.sendwhatmsg(phone_no, message, hour, minute, wait_time=wait_time, tab_close=True, close_time=3) + + return True, None + + except Exception as e: + error_message = str(e) + if "invalid country calling code" in error_message.lower(): + return False, f"Invalid country code in phone number: {phone_no}" + elif "internet" in error_message.lower() or "connection" in error_message.lower(): + return False, "Network error. Please check your internet connection" + else: + return False, f"Failed to send message: {error_message}" def whatsApp(): st.markdown("### WhatsApp Automation 📨") st.write("This app sends a WhatsApp message to all the contacts in the CSV file.") + st.info("📝 **Note**: Phone numbers must include country code (e.g., +1234567890)", icon="â„šī¸") + st.markdown("#### Upload a CSV file with phone numbers:") uploaded_file = st.file_uploader("Choose a CSV file", type=["csv"]) - bottom = st.number_input("Enter the bottom range of phone numbers:", min_value=0, value=0) - top = st.number_input("Enter the top range of phone numbers:", min_value=bottom + 1, value=bottom + 1) - message = st.text_area("Enter the message to send:") if uploaded_file is not None: - if st.button("Send WhatsApp Message"): - if message == "": - st.warning('Please upload a CSV file with a column named "Phone Number".', icon="âš ī¸") - return - + try: df = pd.read_csv(uploaded_file) - if top > len(df): - st.warning("The top range of phone numbers exceeds the total number of phone numbers in the CSV file.", icon="âš ī¸") - return + # Validate CSV structure if "phone_number" not in df.columns: - st.warning('Please upload a CSV file with a column named "phone_number".', icon="âš ī¸") + st.error('CSV file must contain a column named "phone_number".', icon="đŸšĢ") + st.info("Example CSV format:\n```\nphone_number\n+1234567890\n+9876543210\n```") return - for index, row in df.iterrows(): - if index >= bottom and index < top: - try: - sendMsg(row["phone_number"], message) - except Exception as e: - st.error(f"Message Sending Error: {e}") - st.stop() + st.success(f"✅ CSV file loaded successfully! Found {len(df)} phone numbers.", icon="✅") + + # Display preview of phone numbers + with st.expander("📋 Preview Phone Numbers"): + st.dataframe(df.head(10)) + + # Range selection + col1, col2 = st.columns(2) + with col1: + bottom = st.number_input("Start from row:", min_value=0, max_value=len(df) - 1, value=0) + with col2: + top = st.number_input("End at row:", min_value=bottom + 1, max_value=len(df), value=min(bottom + 1, len(df))) + + # Message input + message = st.text_area("Enter the message to send:", height=150, placeholder="Type your message here...") + + # Wait time configuration + wait_time = st.slider( + "Wait time before sending (seconds):", min_value=10, max_value=60, value=15, help="Time to wait before sending each message" + ) + + # Send button + if st.button("📤 Send WhatsApp Messages", type="primary"): + if not message or message.strip() == "": + st.warning("Please enter a message to send.", icon="âš ī¸") + return + + if top > len(df): + st.error("The end row exceeds the total number of phone numbers in the CSV file.", icon="đŸšĢ") + return + + # Create progress tracking + progress_bar = st.progress(0) + status_text = st.empty() + success_count = 0 + failed_count = 0 + failed_numbers = [] + + total_messages = top - bottom + + # Send messages + for index, row in df.iterrows(): + if index >= bottom and index < top: + phone_no = row["phone_number"] + status_text.text(f"Sending message {index - bottom + 1}/{total_messages} to {phone_no}...") + + success, error_msg = sendMsg(phone_no, message, wait_time) + + if success: + success_count += 1 + st.success(f"✅ Message sent to {phone_no}", icon="✅") + else: + failed_count += 1 + failed_numbers.append((phone_no, error_msg)) + st.error(f"❌ Failed to send to {phone_no}: {error_msg}", icon="đŸšĢ") + + # Update progress + progress = (index - bottom + 1) / total_messages + progress_bar.progress(progress) + + # Final summary + status_text.empty() + progress_bar.empty() + + st.divider() + st.markdown("### 📊 Summary") + col1, col2, col3 = st.columns(3) + with col1: + st.metric("Total Messages", total_messages) + with col2: + st.metric("✅ Successful", success_count) + with col3: + st.metric("❌ Failed", failed_count) + + if failed_count > 0: + with st.expander("❌ Failed Messages Details"): + for phone, error in failed_numbers: + st.error(f"**{phone}**: {error}") + else: + st.success("🎉 All messages sent successfully!", icon="🎉") + + except pd.errors.EmptyDataError: + st.error("The uploaded CSV file is empty.", icon="đŸšĢ") + except pd.errors.ParserError: + st.error("Failed to parse CSV file. Please check the file format.", icon="đŸšĢ") + except Exception as e: + st.error(f"An unexpected error occurred: {str(e)}", icon="đŸšĢ") - st.write("All messages sent successfully!") else: st.info("Please upload a CSV file to send messages.", icon="â„šī¸") + st.markdown(""" + **CSV Format Requirements:** + - Must have a column named `phone_number` + - Phone numbers must include country code (e.g., +1234567890) + - Example: + ``` + phone_number + +1234567890 + +9876543210 + ``` + """) diff --git a/src/utils/greeting.py b/src/utils/greeting.py index c3898693..50f3cbf8 100644 --- a/src/utils/greeting.py +++ b/src/utils/greeting.py @@ -1,3 +1,4 @@ +import random from datetime import datetime import pytz @@ -13,3 +14,20 @@ def GreetUser(name): elif 17 <= hour < 21: return f"Good Evening, {name}" return f"Good Night, {name}" + + +def GetRandomWelcomeMessage(): + """Returns a random welcome message to make Jarvis feel more dynamic and natural.""" + welcome_messages = [ + "I am Jarvis Sir. Please tell me how may I help you.", + "Ready to assist you with anything you need!", + "At your service! What can I do for you today?", + "Hello! I'm here to make your day easier.", + "Great to see you! How can I assist you?", + "I'm all set to help you out. What's on your mind?", + "Standing by to help with whatever you need!", + "Your AI assistant is ready. What would you like to do?", + "Here to help! Just let me know what you need.", + "Ready and waiting to assist you today!", + ] + return random.choice(welcome_messages)