misaka001

Untitled

Mar 16th, 2026
46
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.92 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3.  
  4. """
  5. Yahoo Mail Client - OAuth 2.0 Version
  6. Supports SOCKS5 Proxy (with Auth)
  7. Authentication: XOAUTH2
  8. """
  9.  
  10. import os
  11. import sys
  12. import imaplib
  13. import smtplib
  14. import socket
  15. import socks
  16. import email
  17. import base64
  18. import json
  19. import logging
  20. import requests
  21. from email.mime.text import MIMEText
  22. from email.mime.multipart import MIMEMultipart
  23. from typing import List, Optional, Union, Dict, Any
  24. from pathlib import Path
  25. from datetime import datetime, timedelta
  26.  
  27. # Logging Configuration
  28. logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
  29. logger = logging.getLogger(__name__)
  30.  
  31.  
  32. class Socks5ProxyAdapter(requests.adapters.HTTPAdapter):
  33. """Requests adapter supporting SOCKS5 proxy"""
  34.  
  35. def __init__(self, proxy_host: str, proxy_port: int,
  36. proxy_user: Optional[str] = None,
  37. proxy_pass: Optional[str] = None, *args, **kwargs):
  38. self.proxy_host = proxy_host
  39. self.proxy_port = proxy_port
  40. self.proxy_user = proxy_user
  41. self.proxy_pass = proxy_pass
  42. super().__init__(*args, **kwargs)
  43.  
  44. def init_poolmanager(self, *args, **kwargs):
  45. # Setup SOCKS5 proxy
  46. socks.set_default_proxy(
  47. socks.SOCKS5,
  48. self.proxy_host,
  49. self.proxy_port,
  50. username=self.proxy_user,
  51. password=self.proxy_pass
  52. )
  53. socket.socket = socks.socksocket
  54. return super().init_poolmanager(*args, **kwargs)
  55.  
  56.  
  57. class YahooOAuth2Client:
  58. """Yahoo OAuth 2.0 Authentication Client"""
  59.  
  60. # Yahoo OAuth 2.0 Endpoints
  61. TOKEN_URL = "https://api.login.yahoo.com/oauth2/get_token"
  62. AUTH_URL = "https://api.login.yahoo.com/oauth2/request_auth"
  63.  
  64. # Yahoo Mail Servers
  65. SMTP_SERVER = "smtp.mail.yahoo.com"
  66. SMTP_PORT = 587 # TLS
  67.  
  68. IMAP_SERVER = "imap.mail.yahoo.com"
  69. IMAP_PORT = 993
  70.  
  71. def __init__(self, email_address: str,
  72. client_id: str,
  73. refresh_token: str,
  74. proxy_host: Optional[str] = None,
  75. proxy_port: Optional[int] = None,
  76. proxy_username: Optional[str] = None,
  77. proxy_password: Optional[str] = None):
  78. """
  79. Initialize Yahoo OAuth 2.0 Client
  80. """
  81. self.email_address = email_address
  82. self.client_id = client_id
  83. self.refresh_token = refresh_token
  84. self.access_token = None
  85. self.token_expiry = None
  86.  
  87. self.proxy_host = proxy_host
  88. self.proxy_port = proxy_port
  89. self.proxy_username = proxy_username
  90. self.proxy_password = proxy_password
  91.  
  92. # Setup proxy
  93. self._setup_proxy()
  94.  
  95. # Save original socket
  96. self.original_socket = socket.socket
  97.  
  98. def _setup_proxy(self):
  99. """Configure SOCKS5 Proxy"""
  100. if self.proxy_host and self.proxy_port:
  101. if self.proxy_username and self.proxy_password:
  102. socks.set_default_proxy(
  103. socks.SOCKS5,
  104. self.proxy_host,
  105. self.proxy_port,
  106. username=self.proxy_username,
  107. password=self.proxy_password
  108. )
  109. else:
  110. socks.set_default_proxy(
  111. socks.SOCKS5,
  112. self.proxy_host,
  113. self.proxy_port
  114. )
  115. socket.socket = socks.socksocket
  116. logger.info(f"SOCKS5 Proxy enabled: {self.proxy_host}:{self.proxy_port}")
  117.  
  118. def _disable_proxy(self):
  119. """Disable Proxy"""
  120. socket.socket = self.original_socket
  121.  
  122. def get_access_token(self, force_refresh: bool = False) -> Optional[str]:
  123. """
  124. Retrieve Access Token (Auto-refresh)
  125. """
  126. if not force_refresh and self.access_token and self.token_expiry:
  127. if datetime.now() < self.token_expiry:
  128. return self.access_token
  129.  
  130. logger.info("Refreshing Access Token...")
  131.  
  132. payload = {
  133. "grant_type": "refresh_token",
  134. "client_id": self.client_id,
  135. "refresh_token": self.refresh_token
  136. }
  137. headers = {
  138. "Content-Type": "application/x-www-form-urlencoded"
  139. }
  140.  
  141. try:
  142. response = requests.post(
  143. self.TOKEN_URL,
  144. data=payload,
  145. headers=headers,
  146. timeout=15
  147. )
  148.  
  149. response.raise_for_status()
  150. result = response.json()
  151.  
  152. new_token = result.get("access_token")
  153. expires_in = result.get("expires_in", 3600)
  154.  
  155. if new_token:
  156. self.access_token = new_token
  157. self.token_expiry = datetime.now() + timedelta(seconds=expires_in)
  158. logger.info("Access Token refreshed successfully.")
  159. return new_token
  160. else:
  161. logger.error(f"Refresh failed: 'access_token' missing in response. Data: {result}")
  162. return None
  163.  
  164. except requests.exceptions.Timeout:
  165. logger.error("Error: Yahoo connection timed out (Check proxy/network)")
  166. except requests.exceptions.ConnectionError:
  167. logger.error("Error: Connection failed (Check if proxy is active)")
  168. except requests.exceptions.HTTPError as e:
  169. logger.error(f"Error: HTTP request failed (Status: {response.status_code})")
  170. logger.error(f"Response: {response.text}")
  171. except Exception as e:
  172. logger.error(f"Unexpected error: {str(e)}")
  173.  
  174. return None
  175.  
  176. def _create_xoauth2_string(self) -> str:
  177. """Create XOAUTH2 Authentication String"""
  178. if not self.access_token:
  179. self.get_access_token()
  180.  
  181. auth_string = f"user={self.email_address}\x01auth=Bearer {self.access_token}\x01\x01"
  182. return base64.b64encode(auth_string.encode('utf-8')).decode('utf-8')
  183.  
  184. def send_email(self, to_addrs: Union[str, List[str]], subject: str,
  185. body: str, body_type: str = 'plain') -> bool:
  186. """
  187. Send Email via SMTP (XOAUTH2)
  188. """
  189. try:
  190. if isinstance(to_addrs, str):
  191. to_addrs = [to_addrs]
  192.  
  193. msg = MIMEMultipart()
  194. msg['From'] = self.email_address
  195. msg['To'] = ', '.join(to_addrs)
  196. msg['Subject'] = subject
  197. msg.attach(MIMEText(body, body_type, 'utf-8'))
  198.  
  199. logger.info("Connecting to SMTP server...")
  200. server = smtplib.SMTP(self.SMTP_SERVER, self.SMTP_PORT, timeout=30)
  201. server.starttls()
  202.  
  203. auth_string = self._create_xoauth2_string()
  204. server.docmd('AUTH', f'XOAUTH2 {auth_string}')
  205.  
  206. logger.info("SMTP Authentication successful.")
  207.  
  208. server.sendmail(self.email_address, to_addrs, msg.as_string())
  209. logger.info(f"Email sent successfully: '{subject}'")
  210.  
  211. server.quit()
  212. return True
  213.  
  214. except Exception as e:
  215. logger.error(f"Failed to send email: {e}")
  216. return False
  217.  
  218. def receive_emails(self, folder: str = 'INBOX',
  219. max_emails: int = 10) -> List[Dict[str, Any]]:
  220. """
  221. Receive Emails via IMAP (XOAUTH2)
  222. """
  223. emails = []
  224. mail = None
  225.  
  226. try:
  227. logger.info("Connecting to IMAP server...")
  228. mail = imaplib.IMAP4_SSL(self.IMAP_SERVER, self.IMAP_PORT)
  229.  
  230. auth_string = f"user={self.email_address}\x01auth=Bearer {self.access_token}\x01\x01"
  231. mail.authenticate('XOAUTH2', lambda x: auth_string.encode('utf-8'))
  232.  
  233. logger.info("IMAP Authentication successful.")
  234.  
  235. mail.select(folder)
  236. status, message_ids = mail.search(None, 'ALL')
  237.  
  238. if status != 'OK':
  239. logger.error("Failed to search emails.")
  240. return emails
  241.  
  242. id_list = message_ids[0].split()
  243. logger.info(f"Found {len(id_list)} emails total.")
  244.  
  245. for msg_id in id_list[-max_emails:]:
  246. status, msg_data = mail.fetch(msg_id, '(RFC822)')
  247. if status != 'OK':
  248. continue
  249.  
  250. email_body = msg_data[0][1]
  251. msg = email.message_from_bytes(email_body)
  252.  
  253. emails.append({
  254. 'subject': self._decode_header(msg.get('Subject', 'No Subject')),
  255. 'from': self._decode_header(msg.get('From', 'Unknown')),
  256. 'date': msg.get('Date', ''),
  257. 'body': self._get_email_body(msg)
  258. })
  259.  
  260. logger.info(f"Successfully retrieved {len(emails)} emails.")
  261.  
  262. except Exception as e:
  263. logger.error(f"Failed to receive emails: {e}")
  264.  
  265. finally:
  266. if mail:
  267. try:
  268. mail.close()
  269. mail.logout()
  270. except:
  271. pass
  272.  
  273. return emails
  274.  
  275. def _get_email_body(self, msg) -> str:
  276. if msg.is_multipart():
  277. for part in msg.walk():
  278. if part.get_content_type() == 'text/plain':
  279. try:
  280. return part.get_payload(decode=True).decode('utf-8', errors='ignore')
  281. except:
  282. pass
  283. else:
  284. try:
  285. return msg.get_payload(decode=True).decode('utf-8', errors='ignore')
  286. except:
  287. pass
  288. return ""
  289.  
  290. def _decode_header(self, header: str) -> str:
  291. if not header:
  292. return ""
  293. try:
  294. decoded_parts = email.header.decode_header(header)
  295. result = []
  296. for part, charset in decoded_parts:
  297. if isinstance(part, bytes):
  298. if charset:
  299. result.append(part.decode(charset, errors='ignore'))
  300. else:
  301. result.append(part.decode('utf-8', errors='ignore'))
  302. else:
  303. result.append(part)
  304. return ' '.join(result)
  305. except:
  306. return header
  307.  
  308. def __enter__(self):
  309. return self
  310.  
  311. def __exit__(self, exc_type, exc_val, exc_tb):
  312. self._disable_proxy()
  313.  
  314.  
  315. def main():
  316. """Main Program"""
  317. print("=== Yahoo Mail Client (OAuth 2.0) ===\n")
  318.  
  319. # Credentials
  320. EMAIL = "[email protected]"
  321. CLIENT_ID = "dj0yJmk9RDNqOWdZc0wwZm40JmQ9WVdrOU5FTkdZbFE1YlZBbWNHbzlNQT09JnM9Y29uc3VtZXJzZWNyZXQmc3Y9MCZ4PWNi"
  322. REFRESH_TOKEN = "ALiaoGnXJpbq3Y7WJwjXcH2pUsr0~001~fh2nne1vPVUZp4UocDMn"
  323.  
  324. # Proxy Configuration
  325. PROXY_HOST = None # e.g., "127.0.0.1"
  326. PROXY_PORT = None # e.g., 1080
  327. PROXY_USER = None
  328. PROXY_PASS = None
  329.  
  330. # Init Client
  331. with YahooOAuth2Client(
  332. email_address=EMAIL,
  333. client_id=CLIENT_ID,
  334. refresh_token=REFRESH_TOKEN,
  335. proxy_host=PROXY_HOST,
  336. proxy_port=PROXY_PORT,
  337. proxy_username=PROXY_USER,
  338. proxy_password=PROXY_PASS
  339. ) as client:
  340.  
  341. print("Testing Access Token retrieval...")
  342. token = client.get_access_token()
  343. if token:
  344. print(f"✅ Access Token retrieved: {token[:20]}...")
  345. else:
  346. print("❌ Failed to retrieve Access Token.")
  347. return
  348.  
  349. # Main Menu
  350. while True:
  351. print("\nSelect an action:")
  352. print("1. Send Email")
  353. print("2. Receive Latest Emails")
  354. print("3. Refresh Token")
  355. print("4. Exit")
  356.  
  357. choice = input("Enter option (1-4): ").strip()
  358.  
  359. if choice == '1':
  360. to_addr = input("To: ").strip()
  361. subject = input("Subject: ").strip()
  362. body = input("Body: ").strip()
  363.  
  364. if client.send_email(to_addr, subject, body):
  365. print("✅ Sent successfully")
  366. else:
  367. print("❌ Failed to send")
  368.  
  369. elif choice == '2':
  370. emails = client.receive_emails(max_emails=5)
  371. print(f"\nLatest {len(emails)} Emails:")
  372. for i, email_data in enumerate(emails, 1):
  373. print(f"{i}. {email_data['subject']}")
  374. print(f" From: {email_data['from']}")
  375. print(f" Date: {email_data['date']}")
  376. print("-" * 20)
  377.  
  378. elif choice == '3':
  379. token = client.get_access_token(force_refresh=True)
  380. if token:
  381. print(f"✅ Token refreshed successfully.")
  382. else:
  383. print("❌ Token refresh failed.")
  384.  
  385. elif choice == '4':
  386. print("Goodbye!")
  387. break
  388.  
  389.  
  390. if __name__ == "__main__":
  391. main()
Add Comment
Please, Sign In to add comment