Download test/fetchPosts.py from Wyomike/topicBuzz: direct link, hf CLI and curl.
- Browser
- Download file 6.03 kB
-
https://huggingface.co/spaces/Wyomike/topicBuzz/resolve/main/test/fetchPosts.py
- Command line
-
hf download hf://spaces/Wyomike/topicBuzz/test/fetchPosts.py
-
curl -L -o fetchPosts.py https://huggingface.co/spaces/Wyomike/topicBuzz/resolve/main/test/fetchPosts.py
6.03 kB
| import chromadb | |
| import re | |
| import time | |
| from mastodon import Mastodon | |
| from datetime import datetime | |
| from pathlib import Path | |
| import json | |
| # --- 1. CONFIGURE YOUR SETTINGS --- | |
| # !! Fill these in !! | |
| MASTODON_SERVER_URL = r"https://mastodon.social" # e.g., "https://mastodon.social" | |
| MASTODON_ACCESS_TOKEN = json.load(open(r"D:\cs452\TopicBuzz\secrets.json"))["token"] # The token you just generated | |
| USER_LIST_FILE = "usersAll.txt" | |
| DB_PATH = "./my_mastodon_db" | |
| COLLECTION_NAME = "mastodon_posts" | |
| POSTS_TO_FETCH = 100 | |
| # ----------------------------------- | |
| # Simple regex to strip HTML tags from post content | |
| def strip_html(text): | |
| return re.sub(r'<[^<]+?>', '', text) | |
| # Helper function to sleep and respect rate limits | |
| def polite_request(api_call, *args, **kwargs): | |
| """ | |
| Makes an API call and then waits 1 second. | |
| This respects the default 300 requests / 5 min (1 req/sec) limit. | |
| """ | |
| response = api_call(*args, **kwargs) | |
| time.sleep(1) | |
| return response | |
| def main(): | |
| print("--- Mastodon Post Fetcher ---") | |
| # --- 2. Initialize Clients --- | |
| try: | |
| # Initialize Mastodon client | |
| mastodon = Mastodon( | |
| access_token=MASTODON_ACCESS_TOKEN, | |
| api_base_url=MASTODON_SERVER_URL | |
| ) | |
| print(f"Mastodon client connected to {MASTODON_SERVER_URL}") | |
| # Initialize ChromaDB client | |
| client = chromadb.PersistentClient(path=DB_PATH) | |
| collection = client.get_or_create_collection(name=COLLECTION_NAME) | |
| print(f"ChromaDB client connected to {DB_PATH}") | |
| except Exception as e: | |
| print(f"Error during initialization: {e}") | |
| return | |
| # --- 3. Read User List --- | |
| user_file = Path(USER_LIST_FILE) | |
| if not user_file.exists(): | |
| print(f"Error: User file not found at {USER_LIST_FILE}") | |
| return | |
| with open(user_file, 'r') as f: | |
| usernames = [line.strip() for line in f if line.strip()] | |
| print(f"Found {len(usernames)} users to track.") | |
| # --- 4. Main Fetch Loop --- | |
| for username in usernames: | |
| print(f"\nFetching posts for user: {username}") | |
| try: | |
| # First, find the user's account ID | |
| account = polite_request(mastodon.account_lookup, username) | |
| if not account: | |
| print(f"Could not find user {username}. Skipping.") | |
| continue | |
| user_id = account['id'] | |
| # Mastodon's API is paginated, max 40 posts per request | |
| # We need to make multiple requests to get 100 | |
| # Fetch Page 1 (posts 1-40) | |
| page1 = polite_request(mastodon.account_statuses, id=user_id, limit=40) | |
| # Fetch Page 2 (posts 41-80) | |
| page2 = polite_request(mastodon.fetch_next, page1) if page1 else [] | |
| # Fetch Page 3 (posts 81-120) | |
| page3 = polite_request(mastodon.fetch_next, page2) if page2 else [] | |
| # Combine pages and get the most recent 100 | |
| # (or fewer if the user doesn't have 100) | |
| all_posts = (page1 or []) + (page2 or []) + (page3 or []) | |
| recent_posts = all_posts[:POSTS_TO_FETCH] | |
| if not recent_posts: | |
| print(f"No posts found for {username}. Skipping.") | |
| continue | |
| # --- 5. Prepare Data for ChromaDB --- | |
| ids_batch = [] | |
| docs_batch = [] | |
| meta_batch = [] | |
| for post in recent_posts: | |
| doc_content = "" | |
| doc_id = str(post['id']) # Use the status ID (reblog or original) as the unique ID | |
| doc_meta = {} | |
| if post['reblog']: | |
| # This is a REBLOG | |
| original_post = post['reblog'] | |
| # Use the ORIGINAL post's content for the vector | |
| doc_content = strip_html(original_post['content']) | |
| doc_meta = { | |
| "type": "reblog", | |
| "reblogger_user_id": str(post['account']['id']), # The user who reblogged | |
| "reblog_timestamp": int(post['created_at'].timestamp()), # Time of the reblog | |
| "original_post_id": str(original_post['id']), | |
| "original_author_id": str(original_post['account']['id']), | |
| "language": str(original_post.get('language')) | |
| } | |
| else: | |
| # This is an ORIGINAL post | |
| doc_content = strip_html(post['content']) | |
| doc_meta = { | |
| "type": "original", | |
| "author_user_id": str(post['account']['id']), # The user who posted | |
| "author_handle": str(post['account']['acct']), # FIX FOR FUTURE USE WITH USERNAME | |
| "post_timestamp": int(post['created_at'].timestamp()), # Time of the post | |
| "language": str(post.get('language')) | |
| } | |
| # Don't add empty posts (e.g., if content stripping failed) | |
| if doc_content: | |
| ids_batch.append(doc_id) | |
| docs_batch.append(doc_content) | |
| meta_batch.append(doc_meta) | |
| # --- 6. Store in ChromaDB --- | |
| if ids_batch: | |
| collection.upsert( | |
| ids=ids_batch, | |
| documents=docs_batch, | |
| metadatas=meta_batch | |
| ) | |
| print(f"Successfully added/updated {len(ids_batch)} posts for {username}.") | |
| else: | |
| print(f"No new original posts found for {username}.") | |
| except Exception as e: | |
| print(f"An error occurred while processing {username}: {e}") | |
| # Continue to the next user even if one fails | |
| pass | |
| print("\n--- All users processed. ---") | |
| if __name__ == "__main__": | |
| main() |