Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/ajfaysal_smart-ai-social-automation-agent.json
T

1 line
12 KiB
JSON

{"repo": "ajfaysal/smart-ai-social-automation-agent", "files": {"x_automation.py": {"d8b36b2070a67002c8be68847fe64a09ae20fd8a": {"t": 1785595161.0032988, "n": 11234, "h": "ac00d001f56d2ae6", "c": "import base64\nimport binascii\nimport json\nimport os\nimport re\nimport time\nfrom urllib.parse import quote\n\nimport requests\nfrom playwright.sync_api import TimeoutError as PlaywrightTimeoutError\nfrom playwright.sync_api import sync_playwright\n\nPOST_CHAR_LIMIT = 260\nPOST_CONFIRMATION_WAIT_MS = 5000\nPOST_BUTTON_TIMEOUT_MS = 60000\nHIDDEN_CHAR_PATTERN = re.compile(r\"[\\s\\u200b\\u200c\\u200d\\u200e\\u200f\\u202a-\\u202e\\u2060-\\u206f\\ufeff]\")\nOUTPUT_IMAGE_FILENAME = \"x_card.png\"\n\n# Default fallbacks if user secrets are missing\nDEFAULT_TWEET_TEXT = (\n \"🔥 New AI automation drop for creators.\\n\\n\"\n \"→ Build faster workflows\\n\"\n \"→ Save hours weekly\\n\\n\"\n \"Try it today 👇\\n\\n\"\n \"#AI #Automation #CreatorTools\"\n)\n\nDEFAULT_IMAGE_PROMPT = (\n \"Dark blue/purple background, bold white text, gold/green accent color, \"\n \"clean minimal professional design, 16:9 ratio, no faces, no robots.\"\n)\n\n\ndef normalize_auth_header(api_key):\n if not api_key:\n return None\n if api_key.lower().startswith(\"bearer \"):\n return api_key\n return \"Bearer \" + api_key\n\n\ndef sanitize_env_value(value):\n if value is None:\n return None\n cleaned = HIDDEN_CHAR_PATTERN.sub(\"\", value)\n return cleaned or None\n\n\ndef clean_json_response(text):\n cleaned = text.strip()\n if \"```\" in cleaned:\n cleaned = cleaned.split(\"```\", 1)[1]\n if \"```\" in cleaned:\n cleaned = cleaned.split(\"```\", 1)[0]\n if cleaned.lower().startswith(\"json\"):\n cleaned = cleaned[4:].strip()\n return cleaned.strip()\n\n\ndef fallback_with_error(label, exc):\n print(f\"{label}: {type(exc).__name__}\")\n return DEFAULT_TWEET_TEXT, DEFAULT_IMAGE_PROMPT\n\n\ndef normalize_same_site(value):\n if isinstance(value, str):\n normalized = value.strip().lower()\n if normalized == \"strict\":\n return \"Strict\"\n elif normalized == \"lax\":\n return \"Lax\"\n elif normalized == \"none\":\n return \"None\"\n return \"Lax\"\n\n\ndef decode_base64_json(payload):\n try:\n decoded_bytes = base64.b64decode(payload)\n except (binascii.Error, ValueError) as exc:\n raise ValueError(\n \"The X_COOKIES environment variable is not valid base64 encoding.\"\n ) from exc\n try:\n decoded = decoded_bytes.decode(\"utf-8\")\n except UnicodeDecodeError as exc:\n raise ValueError(\"X_COOKIES base64 payload must be UTF-8 JSON.\") from exc\n try:\n return json.loads(decoded)\n except json.JSONDecodeError as exc:\n raise ValueError(\"X_COOKIES base64 payload must contain valid JSON.\") from exc\n\n\ndef parse_cookie_json(cookies_json):\n if not cookies_json:\n return []\n try:\n data = json.loads(cookies_json)\n except json.JSONDecodeError:\n data = decode_base64_json(cookies_json)\n if isinstance(data, dict) and \"cookies\" in data:\n data = data[\"cookies\"]\n if not isinstance(data, list):\n raise ValueError(\"X_COOKIES must be a JSON array of cookie objects.\")\n return data\n\n\ndef has_cookie_scope(cookie):\n return \"url\" in cookie or \"domain\" in cookie\n\n\ndef sanitize_cookies(raw_cookies):\n allowed_keys = {\n \"name\",\n \"value\",\n \"domain\",\n \"path\",\n \"expires\",\n \"httpOnly\",\n \"secure\",\n \"sameSite\",\n \"url\",\n }\n sanitized = []\n for cookie in raw_cookies:\n if not isinstance(cookie, dict):\n continue\n clean_cookie = {key: cookie[key] for key in allowed_keys if key in cookie}\n if \"name\" not in clean_cookie or \"value\" not in clean_cookie:\n continue\n if \"expires\" not in clean_cookie and \"expirationDate\" in cookie:\n clean_cookie[\"expires\"] = cookie[\"expirationDate\"]\n if \"sameSite\" in clean_cookie:\n clean_cookie[\"sameSite\"] = normalize_same_site(clean_cookie[\"sameSite\"])\n else:\n clean_cookie[\"sameSite\"] = \"Lax\"\n if \"expires\" in clean_cookie:\n try:\n expires_value = float(clean_cookie[\"expires\"])\n except (TypeError, ValueError):\n clean_cookie.pop(\"expires\", None)\n else:\n if expires_value < 0:\n clean_cookie.pop(\"expires\", None)\n else:\n clean_cookie[\"expires\"] = int(expires_value)\n if not has_cookie_scope(clean_cookie):\n continue\n if \"path\" not in clean_cookie:\n clean_cookie[\"path\"] = \"/\"\n sanitized.append(clean_cookie)\n return sanitized\n\n\ndef call_premium_ai():\n # Public Setup: Dynamically load configurations from user's GitHub Secrets\n api_endpoint = os.getenv(\"PREMIUM_API_URL\", \"https://api.xiaomimimo.com/anthropic/v1/messages\").strip()\n api_key = os.getenv(\"PREMIUM_API_KEY\", \"\").strip()\n model_name = os.getenv(\"PREMIUM_MODEL\", \"claude-3-5-sonnet-20240620\").strip()\n \n # Custom prompts provided by the user, or generic defaults\n system_prompt = os.getenv(\"SYSTEM_PROMPT\", \"You are a professional X content creator. Write an engaging English post under 260 characters total, including hashtags. Return JSON format with 'tweet_text' and 'image_prompt' keys.\").strip()\n user_prompt = os.getenv(\"USER_PROMPT\", \"Generate the JSON response now.\").strip()\n\n if not api_key:\n raise ValueError(\"PREMIUM_API_KEY is missing in GitHub Secrets.\")\n\n headers = {\n \"Authorization\": f\"Bearer {api_key}\",\n \"Content-Type\": \"application/json\"\n }\n \n # Handle Anthropic headers if endpoint matches\n if \"anthropic\" in api_endpoint.lower():\n headers[\"anthropic-version\"] = \"2023-06-01\"\n\n # Dynamic payload adapting to user configuration\n payload = {\n \"model\": model_name,\n \"max_tokens\": 1000,\n \"messages\": [{\"role\": \"user\", \"content\": user_prompt}]\n }\n \n if \"anthropic\" in api_endpoint.lower():\n payload[\"system\"] = system_prompt\n else:\n # Standard OpenAI formatting fallback\n payload[\"messages\"].insert(0, {\"role\": \"system\", \"content\": system_prompt})\n payload[\"response_format\"] = {\"type\": \"json_object\"}\n\n response = requests.post(api_endpoint, json=payload, headers=headers, timeout=60)\n response.raise_for_status()\n \n if \"anthropic\" in api_endpoint.lower():\n return response.json()[\"content\"][0][\"text\"]\n else:\n return response.json()[\"choices\"][0][\"message\"][\"content\"]\n\n\ndef normalize_tweet_text(post_text):\n post_text = post_text.replace(\"\\r\\n\", \"\\n\").strip()\n if not post_text:\n post_text = DEFAULT_TWEET_TEXT\n if len(post_text) > POST_CHAR_LIMIT:\n post_text = post_text[:POST_CHAR_LIMIT].rstrip()\n return post_text\n\n\ndef generate_post():\n try:\n content = call_premium_ai()\n parsed = json.loads(clean_json_response(content))\n post_text = parsed.get(\"tweet_text\")\n image_prompt = parsed.get(\"image_prompt\")\n if not isinstance(post_text, str):\n post_text = DEFAULT_TWEET_TEXT\n if not isinstance(image_prompt, str):\n image_prompt = DEFAULT_IMAGE_PROMPT\n except Exception as exc:\n print(f\"AI Generation fell back to default layout due to: {exc}\")\n return DEFAULT_TWEET_TEXT, DEFAULT_IMAGE_PROMPT\n\n post_text = normalize_tweet_text(post_text)\n image_prompt = image_prompt.strip() or DEFAULT_IMAGE_PROMPT\n return post_text, image_prompt\n\n\ndef download_pollinations_image(image_prompt, output_path):\n prompt = image_prompt.strip() or DEFAULT_IMAGE_PROMPT\n encoded_prompt = quote(prompt, safe=\"\")\n url = f\"https://image.pollinations.ai/prompt/{encoded_prompt}?width=1280&height=720&nologo=true\"\n \n response = requests.get(url, timeout=60)\n response.raise_for_status()\n with open(output_path, \"wb\") as file:\n file.write(response.content)\n return output_path\n\n\ndef click_post_button(page):\n # Aggressive UI Injection to forcefully publish on X across different viewport configurations\n try:\n page.keyboard.press(\"Control+Enter\")\n time.sleep(2)\n except Exception:\n pass\n \n try:\n page.evaluate('''\n const selectors = [\n 'button[data-testid=\"tweetButtonInline\"]', \n 'div[data-testid=\"tweetButtonInline\"]', \n 'button[data-testid=\"tweetButton\"]'\n ];\n for (let sel of selectors) {\n let elements = document.querySelectorAll(sel);\n for (let el of elements) {\n el.click();\n }\n }\n ''')\n time.sleep(2)\n except Exception:\n pass\n \n try:\n selectors = \"button[data-testid='tweetButtonInline'], div[data-testid='tweetButtonInline'], button[data-testid='tweetButton']\"\n page.locator(selectors).first.click(force=True, timeout=10000)\n return True\n except Exception:\n pass\n \n try:\n page.locator('//span[contains(text(), \"Post\")]').first.click(force=True, timeout=10000)\n return True\n except Exception:\n return False\n\n\ndef post_to_x(post_text, image_path):\n cookies_json = os.getenv(\"X_COOKIES\")\n headless = os.getenv(\"X_HEADLESS\", \"true\").lower() != \"false\"\n\n if not cookies_json:\n print(\"X_COOKIES missing; skipping X post.\")\n return False\n\n try:\n cookies = sanitize_cookies(parse_cookie_json(cookies_json))\n except Exception as exc:\n print(f\"Cookie parsing exception: {exc}\")\n return False\n \n if not cookies:\n print(\"No usable cookies detected.\")\n return False\n\n with sync_playwright() as p:\n browser = p.chromium.launch(headless=headless)\n context = browser.new_context()\n try:\n context.add_cookies(cookies)\n page = context.new_page()\n page.goto(\"https://x.com/home\", wait_until=\"domcontentloaded\")\n\n try:\n page.wait_for_selector(\"div[data-testid='tweetTextarea_0']\", timeout=15000)\n except PlaywrightTimeoutError:\n print(\"Session verification failed; cookies might be invalid.\")\n return False\n\n page.click(\"div[data-testid='tweetTextarea_0']\")\n page.keyboard.type(post_text)\n\n if image_path and os.path.exists(image_path):\n page.set_input_files(\"input[data-testid='fileInput']\", image_path)\n\n page.wait_for_timeout(1000)\n if not click_post_button(page):\n return False\n page.wait_for_timeout(POST_CONFIRMATION_WAIT_MS)\n return True\n finally:\n context.close()\n browser.close()\n\n\ndef main():\n post_text, image_prompt = generate_post()\n image_path = None\n try:\n image_path = download_pollinations_image(image_prompt, OUTPUT_IMAGE_FILENAME)\n except Exception as exc:\n print(f\"Media pipeline skipped: {exc}\")\n \n try:\n posted = post_to_x(post_text, image_path)\n if not posted:\n print(\"X deployment aborted.\")\n finally:\n if image_path and os.path.exists(image_path):\n os.remove(image_path)\n\n\nif __name__ == \"__main__\":\n main()\n \n"}}}}