1. Remove comments per day and global comments etc part . 
2. Keep search function run until I stop script

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
import time
import json
import yaml  # Import yaml to load settings properly
import os  # Import os to work with file paths
import logging
import pyperclip  # Import pyperclip to handle clipboard operations

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# Suppress unnecessary log messages
os.environ['WDM_LOG_LEVEL'] = '0'


def normalize_cookie(cookie):
    # Ensure all required fields are present and format is compatible with Selenium
    required_fields = ["name", "value", "domain", "path"]
    normalized = {key: cookie.get(key, '') for key in required_fields}

    # Convert expirationDate to expiry for Selenium compatibility
    if "expirationDate" in cookie:
        normalized["expiry"] = int(cookie["expirationDate"])
    
    # Set default secure and httpOnly values
    normalized["secure"] = cookie.get("secure", True)
    normalized["httpOnly"] = cookie.get("httpOnly", False)
    
    # Normalize sameSite values to match Selenium expectations
    same_site = cookie.get("sameSite", '').lower()
    if same_site in ["no_restriction", "unspecified"]:
        normalized["sameSite"] = "None"
    elif same_site in ["strict", "lax", "none"]:
        normalized["sameSite"] = same_site.capitalize()
    else:
        normalized["sameSite"] = "None"  # Default to 'None' if value is invalid

    return normalized


def load_settings():
    # Load settings from YAML file
    logger.info("Loading settings from YAML file...")
    script_dir = os.path.dirname(os.path.abspath(__file__))
    settings_path = os.path.join(script_dir, 'settings.yaml')
    if os.path.exists(settings_path):
        with open(settings_path, 'r') as file:
            settings_data = yaml.safe_load(file)
            logger.info("Settings loaded successfully.")
            return settings_data
    else:
        raise FileNotFoundError("Settings file not found. Please ensure settings.yaml exists.")


def wait_for_element(driver, locator, condition=EC.presence_of_element_located, timeout=20):
    # Helper function to wait for an element based on a specific condition
    try:
        element = WebDriverWait(driver, timeout).until(condition(locator))
        return element
    except TimeoutException:
        logger.error(f"Timeout waiting for element: {locator}")
        return None


def search_linkedin(driver, keyword, post_no_elements):
    # Function to perform LinkedIn search for a single keyword
    try:
        logger.info(f"Searching for keyword: {keyword}")
        search_url = f"https://www.linkedin.com/search/results/content/?datePosted=%22past-24h%22&keywords={keyword}&origin=FACETED_SEARCH&sid=&sortBy=%22date_posted%22"
        driver.get(search_url)
        if wait_for_element(driver, (By.CLASS_NAME, 'search-results-container')):
            logger.info(f"Search completed for keyword: {keyword}")

            # Read URLs in the file and save them into a list
            script_dir = os.path.dirname(os.path.abspath(__file__))
            urls_path = os.path.join(script_dir, 'urls.txt')
            with open(urls_path, 'r') as file:
                urls_in_file = [line.strip() for line in file.readlines()]
            logger.info(f"Read {len(urls_in_file)} URLs from urls.txt file.")

            time.sleep(3)  # Wait for 3 seconds after reading URLs

            # Wait for the copy link option and click
            overflow_web_ios_small_xpath = f"(//button[contains(@class,'feed-shared-control-menu__trigger artdeco-button')])[ {post_no_elements} ]"
            overflow_web_ios_small = wait_for_element(driver, (By.XPATH, overflow_web_ios_small_xpath))
            if overflow_web_ios_small:
                overflow_web_ios_small.click()
                logger.info("Clicked post successfully.")

                # Wait for the copy link button to appear and click it
                copy_link_xpath = "//*/text()[normalize-space(.)='Copy link to post']/parent::*"
                copy_link_element = wait_for_element(driver, (By.XPATH, copy_link_xpath))
                if copy_link_element:
                    copy_link_element.click()
                    logger.info("Clicked on 'Copy link to post' button successfully.")
                    time.sleep(3)  # Wait for 3 seconds after clicking the link

                    # Get the copied URL from the clipboard
                    copied_url = pyperclip.paste()
                    logger.info(f"Copied URL: {copied_url}")

                    # Open the copied URL in a new tab
                    driver.execute_script("window.open('{}', '_blank');".format(copied_url))

                    # Append the copied URL to urls.txt
                    with open(urls_path, 'a') as file:
                        file.write(f"{copied_url}\n\n")
                    logger.info("Copied URL added to urls.txt successfully.")

                    # Increment the post number for subsequent actions
                    post_no_elements += 1
    except TimeoutException:
        logger.error(f"Timeout while searching for keyword: {keyword}")


def create_or_check_urls_file():
    # Function to create or check the urls.txt file
    script_dir = os.path.dirname(os.path.abspath(__file__))
    urls_path = os.path.join(script_dir, 'urls.txt')
    if not os.path.exists(urls_path):
        with open(urls_path, 'w') as file:
            logger.info("urls.txt file created.")
    else:
        logger.info("urls.txt file already exists.")


def start_linkedin_automation(cookie, keywords, comments_per_day, global_comments, headless=True, close_browser_after=True):
    logger.info("Starting LinkedIn automation...")
    # Setup WebDriver using webdriver-manager to automatically manage ChromeDriver
    chrome_options = Options()
    if headless:
        chrome_options.add_argument("--headless")  # Run in headless mode if desired

    # Suppress unnecessary log messages
    chrome_options.add_argument("--log-level=3")  # Suppress Chrome logs
    chrome_options.add_argument("--disable-gpu")  # Disable GPU to avoid GPU-related issues
    chrome_options.add_argument("--no-sandbox")  # Disables the sandbox for all process types that are normally sandboxed
    chrome_options.add_argument("--disable-dev-shm-usage")  # Overcome limited resource problems
    chrome_options.add_argument("--incognito")  # Opens the browser in incognito mode

    # Suppress "Chrome is being controlled by automated test software" message
    chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
    chrome_options.add_experimental_option('useAutomationExtension', False)

    # Disable WebGL and suppress warnings to avoid unnecessary logs
    chrome_options.add_argument("--disable-webgl")
    chrome_options.add_argument("--disable-logging")
    chrome_options.add_argument("--disable-blink-features=AutomationControlled")
    chrome_options.add_argument("--disable-features=WebGL2ComputeContext")

    driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)

    try:
        # Load LinkedIn
        logger.info("Opening LinkedIn website...")
        driver.get("https://www.linkedin.com")
        if not wait_for_element(driver, (By.TAG_NAME, 'body'), EC.presence_of_element_located, timeout=30):
            logger.error("Failed to load LinkedIn home page.")
            return

        # Load Cookies
        logger.info("Loading cookies...")
        try:
            for c in cookie['cookies']:
                normalized_cookie = normalize_cookie(c)  # Normalize the cookie format for Selenium
                driver.add_cookie(normalized_cookie)
        except KeyError as e:
            logger.error(f"Error loading cookies, missing key: {str(e)}")
            return
        except Exception as e:
            logger.error(f"Unexpected error while loading cookies: {str(e)}")
            return
        
        driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
        driver.refresh()  # Refresh after adding cookies to log in
        logger.info("Cookies loaded and page refreshed.")

        # Confirm Login Successful
        if "feed" not in driver.current_url:
            logger.error("Login failed. Please check the cookie.")
            return
        
        logger.info("Logged in successfully!")
        logger.info(f"Bot will do Total: {comments_per_day} comments")

        # Create or check urls.txt file
        create_or_check_urls_file()

        # Perform search for each keyword
        post_no_elements = 1
        for keyword in keywords:
            search_linkedin(driver, keyword, post_no_elements)
            global_comments += 1
            post_no_elements += 1  # Increase post_no_elements to make subsequent searches dynamic
            logger.info(f"Total comments completed: {global_comments}")
            if global_comments >= comments_per_day:
                break
        
        logger.info("Search process completed.")

    finally:
        # Close the browser after reaching the required number of comments
        if global_comments >= comments_per_day or close_browser_after:
            logger.info("Closing browser...")
            try:
                driver.quit()
                logger.info("Browser closed after completing the comments.")
            except Exception as e:
                logger.error(f"Error occurred while closing the browser: {str(e)}")
        else:
            logger.info("Automation complete. Browser will remain open for manual inspection.")