Download reddit_client.py from paulhemb/reddit_intelligence_platform: direct link, hf CLI and curl.
- Browser
- Download file 5.72 kB
-
https://huggingface.co/spaces/paulhemb/reddit_intelligence_platform/resolve/main/reddit_client.py
- Command line
-
hf download hf://spaces/paulhemb/reddit_intelligence_platform/reddit_client.py
-
curl -L -o reddit_client.py https://huggingface.co/spaces/paulhemb/reddit_intelligence_platform/resolve/main/reddit_client.py
5.72 kB
| import os | |
| import time | |
| import random | |
| import requests | |
| import logging | |
| from typing import List, Dict, Optional | |
| # Set up logging | |
| logger = logging.getLogger(__name__) | |
| # Simple User-Agent header – change to your own | |
| HEADERS = {"User-Agent": "AltomicAi/3.0 (by /u/yourusername)"} | |
| class RateLimiter: | |
| """Simple rate limiter to enforce a given number of requests per second.""" | |
| def __init__(self, requests_per_second: float): | |
| self.interval = 1.0 / requests_per_second if requests_per_second > 0 else 0 | |
| self.last_request = 0.0 | |
| def wait(self): | |
| if self.interval <= 0: | |
| return | |
| now = time.time() | |
| elapsed = now - self.last_request | |
| if elapsed < self.interval: | |
| time.sleep(self.interval - elapsed) | |
| self.last_request = time.time() | |
| class RedditClient: | |
| BASE_URL = "https://www.reddit.com" | |
| def __init__(self, requests_per_second: float = 0.3): | |
| self.rate_limiter = RateLimiter(requests_per_second) | |
| # Load proxies from environment variable (Hugging Face secret) | |
| proxy_list_str = os.environ.get("PROXY_LIST") | |
| print(f"[DEBUG] PROXY_LIST env var: {'SET (length ' + str(len(proxy_list_str)) + ')' if proxy_list_str else 'NOT SET'}", flush=True) | |
| self.proxy_list = self._parse_proxies(proxy_list_str) | |
| print(f"[DEBUG] Loaded {len(self.proxy_list)} proxies", flush=True) | |
| if self.proxy_list: | |
| print(f"[DEBUG] First proxy: {self.proxy_list[0] if self.proxy_list else 'None'}", flush=True) | |
| else: | |
| print("[WARNING] No proxies loaded. Requests will be made directly (likely blocked).", flush=True) | |
| def _parse_proxies(self, content: Optional[str]) -> List[Dict[str, str]]: | |
| """Convert proxy lines (http://user:pass@ip:port) to requests proxy dicts.""" | |
| proxies = [] | |
| if not content: | |
| return proxies | |
| for line in content.strip().splitlines(): | |
| line = line.strip() | |
| if line and not line.startswith('#'): | |
| # Ensure the line starts with http:// or https:// | |
| if not line.startswith('http'): | |
| line = f"http://{line}" | |
| proxies.append({"http": line, "https": line}) | |
| return proxies | |
| def _get_random_proxy(self) -> Optional[Dict[str, str]]: | |
| if not self.proxy_list: | |
| return None | |
| return random.choice(self.proxy_list) | |
| def _request(self, url: str, params: dict = None) -> dict: | |
| """Make a request with rate limiting and proxy rotation.""" | |
| self.rate_limiter.wait() | |
| proxy = self._get_random_proxy() | |
| print(f"[DEBUG] Requesting {url} with proxy: {proxy if proxy else 'DIRECT'}", flush=True) | |
| try: | |
| resp = requests.get( | |
| url, | |
| headers=HEADERS, | |
| params=params, | |
| proxies=proxy, | |
| timeout=15 | |
| ) | |
| print(f"[DEBUG] Response status: {resp.status_code}", flush=True) | |
| resp.raise_for_status() | |
| return resp.json() | |
| except requests.exceptions.RequestException as e: | |
| print(f"[ERROR] Reddit request failed: {e}", flush=True) | |
| if hasattr(e, 'response') and e.response is not None: | |
| print(f"[ERROR] Response text: {e.response.text[:500]}", flush=True) | |
| return {} | |
| def get_top_posts(self, subreddit: str, limit: int = 30, time_filter: str = "day") -> List[dict]: | |
| """ | |
| Fetch top posts from a subreddit, handling pagination up to `limit` posts. | |
| Returns a list of post dicts. | |
| """ | |
| print(f"[DEBUG] Fetching top posts from r/{subreddit}, limit={limit}, time_filter={time_filter}", flush=True) | |
| all_posts = [] | |
| after = None | |
| per_page = min(limit, 100) | |
| remaining = limit | |
| while remaining > 0: | |
| params = { | |
| "limit": min(remaining, per_page), | |
| "t": time_filter, | |
| } | |
| if after: | |
| params["after"] = after | |
| url = f"{self.BASE_URL}/r/{subreddit}/top.json" | |
| data = self._request(url, params) | |
| if not data: | |
| print(f"[WARNING] No data returned from Reddit for {url}", flush=True) | |
| break | |
| children = data.get("data", {}).get("children", []) | |
| if not children: | |
| print(f"[INFO] No children in response for {subreddit}", flush=True) | |
| break | |
| for child in children: | |
| all_posts.append(child["data"]) | |
| remaining -= 1 | |
| if remaining == 0: | |
| break | |
| after = data.get("data", {}).get("after") | |
| if not after: | |
| print(f"[INFO] No 'after' token, pagination finished", flush=True) | |
| break | |
| print(f"[DEBUG] Fetched {len(all_posts)} posts from r/{subreddit}", flush=True) | |
| return all_posts | |
| def get_post_comments(self, post_id: str, limit: int = 20) -> List[dict]: | |
| """Fetch top-level comments for a post.""" | |
| url = f"{self.BASE_URL}/comments/{post_id}.json" | |
| data = self._request(url) | |
| if not data or not isinstance(data, list) or len(data) < 2: | |
| return [] | |
| comments_data = data[1].get("data", {}).get("children", []) | |
| comments = [c["data"] for c in comments_data if c["kind"] == "t1"] | |
| return comments[:limit] | |
| def get_subreddit_info(self, subreddit: str) -> dict: | |
| """Fetch subreddit metadata.""" | |
| url = f"{self.BASE_URL}/r/{subreddit}/about.json" | |
| data = self._request(url) | |
| return data.get("data", {}) if data else {} |