once url copied from post, open that url and also show in log 

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}")

                    # 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.")