# TrustCafé API Wrapper A Python wrapper for the TrustCafé API, providing a convenient interface to interact with TrustCafé features programmatically. ## Table of Contents - [Prerequisites](#prerequisites) - [Installation](#installation) - [Quick Start](#quick-start) - [Authentication](#authentication) - [API Reference](#api-reference) - [Jobs](#jobs) - [Wrappers](#wrappers) - [Custom Requests](#custom-requests) - [Environment Setup](#environment-setup) - [Debug Mode](#debug-mode) - [Examples](#examples) - [Development](#development) - [Testing](#testing) ## Prerequisites Before you begin, ensure you have: - **Python 3.11 or higher** installed - **API Client ID and Secret** from TrustCafé - **python-dotenv** (for `.env` file support) or ability to set environment variables ### Getting API Credentials 1. Visit your TrustCafé account page: - [Production API Access](https://www.trustcafe.io/en/myaccount/apiaccess) - [Alpha Environment API Access](https://alpha.wts2.net/en/myaccount/apiaccess) 2. Click "Create new client credentials key pair" 3. Choose the scopes you need (or select all for full access) 4. Click "Save" 5. Copy the **client_id** and **client_secret** to your environment: - The secret cannot be retrieved later — make sure to save it securely - Do not commit these credentials to version control ## Installation ```bash # Using pip pip install trustcafeapiwrapper # Using uv (recommended) uv add trustcafeapiwrapper ``` ## Quick Start The simplest way to get started: ```python import trustcafeapiwrapper import os # Initialize the API client API = trustcafeapiwrapper.APIClient( client_id=os.getenv("TRUSTCAFE_CLIENT_ID"), client_secret=os.getenv("TRUSTCAFE_CLIENT_SECRET"), environment="alpha", # Options: "alpha" or "production" debug=False ) # Authenticate and get user profile API.handle_token() profile = API.run_job('userprofile.get', "your-user-slug") print(profile) ``` ## Authentication The API wrapper handles OAuth2 client-credentials authentication automatically. ### Automatic Token Management ```python API.handle_token() ``` **What it does:** - Checks for an existing token file (`token_data_{env}.json`) - If found, verifies if the token is still valid - If expired or not found, obtains a new access token (~3 month duration) - Saves the token to a local file for future requests ### Manual Token Management For more control over token storage: ```python def getMyToken(): # Retrieve token from your custom storage (database, file, etc.) return { "access_token": "your_token_here", "access_token_timeout": 9999999999 # Unix timestamp } def saveMyToken(token_data): # Save to your preferred storage with open("my_token_cache.json", "w") as f: json.dump(token_data, f) return True # Set the token manually or pass to initialize API = APIClient( client_id=os.getenv("client_id"), client_secret=os.getenv("client_secret"), environment="production", debug=False ) # Use existing token or get a new one if not API.is_token_valid(): saveMyToken(API.sign_in()) ``` ### Token Validation ```python if API.is_token_valid(): print("Token is valid") else: API.handle_token() # Get a new token ``` ## Environment Setup The wrapper supports two environments: | Environment | URL | Purpose | |------------|-----|---------| | `alpha` | `alpha.wts2.net` | Development/staging environment | | `production` | `trustcafe.io` | Production environment | ```python # Alpha environment (default) API = APIClient( client_id="your-client-id", client_secret="your-client-secret", environment="alpha", debug=False ) # Production environment API = APIClient( client_id="your-client-id", client_secret="your-client-secret", environment="production", debug=False ) # Change environment dynamically API.set_environment("production") ``` ## API Reference ### Jobs Jobs are pre-built functions that map to specific API endpoints. They return the raw response from the API. #### Post Operations ```python # Get a specific post post = API.run_job('post.get', "post-slug-or-id") # Create a post post = API.run_job('post.create', { "postText": "This is a test post", "parent": { "pk": "maintrunk#maintrunk", "sk": "maintrunk#maintrunk" } }) # List all posts posts = API.run_job('post.listall') # List public posts public_posts = API.run_job('post.listpublic') # List posts by a specific branch branch_posts = API.run_job('post.listbybranch', "branch-name") # List posts by a user profile user_posts = API.run_job('post.listbyuserprofile', "username") # Update a post updated_post = API.run_job('post.update', { "pk": "post-id", "sk": "post-id", "newPostText": "Updated text" }) # List removed posts removed = API.run_job('post.listremoved') ``` #### Comment Operations ```python # Create a comment comment = API.run_job('comment.create', { "commentText": "This is a comment", "parent": { "pk": "post-id", "sk": "post-id" } }) # Get comments by post ID comments = API.run_job('comment.listtbypostid', "post-id") ``` #### User Profile Operations ```python # Get user profile profile = API.run_job('userprofile.get', "user-slug") ``` #### Follow Operations ```python # Follow a user followed = API.run_job('follow.follow', { "isFollowing": True, "parent": { "pk": "userprofile#username", "sk": "userprofile#username" }, "followType": "userprofile", "parentSlug": "username" }) ``` #### Vote Operations ```python # Cast a vote voted = API.run_job('vote.votecast', { "parent": { "pk": "post-id", "sk": "post-id", "slug": "post-slug", "entity": "post" }, "vote": "up" # or "down" }) ``` #### Reaction Operations ```python # React to something (post, comment, etc.) reactions = API.run_job('reaction.reacttosomething', { "parent": { "pk": "post-id", "sk": "post-id" }, "reaction": "like" # various types supported }) # Get reactions by parent post_reactions = API.run_job('reaction.getbyparent', "parent-id") # List reactions reactions_list = API.run_job('reaction.listbyparent', "parent-id") ``` #### Notification Operations ```python # List all notifications notifications = API.run_job('notification.listnotifications') # Mark all as read API.run_job('notification.markallasread') ``` #### Trust Operations ```python # Create or update trust relationship trust = API.run_job('trust.createorupdate', { "pk": "trust_id", "sk": "trust_id", "trustType": "positive" }) # List trust by user initialization trusts = API.run_job('trust.listbyusersinit', "username") # List trust by user has trusts = API.run_job('trust.listbyuserhas', "username") ``` #### Branch Operations ```python # Get a specific branch branch = API.run_job('branch.get', "branch-name") # List branches (all branches with pagination) branches = API.run_job('branch.listbyname') ``` #### Feed Operations ```python # Get café feed feed = API.run_job('feed.cafefeed') # Get following feed following_feed = API.run_job('feed.followingfeed') ``` #### Block Operations ```python # Block a user (function exists but needs implementation) # TODO: Add job function for block operations ``` #### Mute Operations ```python # Mute functionality (to be added) # TODO: Add job function for mute operations ``` ### Wrappers Wrappers are high-level functions that simplify common operations by handling payload preparation and job execution automatically. #### Using a Wrapper ```python API.handle_token() # Create a post with a wrapper (recommended for simplicity) from trustcafeapiwrapper.wrappers.post.create_post import create_post API.wrapped(create_post( "This is a test post created via the wrapper.", parent_path="/", # Root path (optional) blur_label=None, # Optional blurring card_url=None, # Optional card URL collaborative=False # Optional collaboration flag )) # Create a comment from trustcafeapiwrapper.wrappers.comment.create_comment import create_comment API.wrapped(create_comment( "This is a comment", parent_path="/" # Parent path (required) )) ``` **About Wrappers:** - Wrappers simplify common operations - They prepare the payload and job execution automatically - More wrappers are being added for consistency - For flexibility, use Jobs or Custom Requests ### Using `run_job` vs. Wrappers | Feature | `run_job` | Wrappers | |---------|----------|----------| | Simplicity | Higher | Highest for common tasks | | Flexibility | Maximum | Optimized for specific use cases | | Discovery | Need to know job format | Self-documenting payloads | | Customization | Full control | Pre-defined options | **Recommendation:** - Use **Wrappers** for common, well-documented operations - Use **Jobs** when you need fine-grained control - Use **Custom Requests** when neither fits your needs ## Custom Requests For operations not covered by jobs or wrappers, use the `make_request` method: ```python # Make a raw API request response = API.make_request( method="GET", endpoint="content", path="post/some-custom-path", authenticate=True, query_params={"param1": "value1"} ) ``` **Parameters:** - `method` (str): HTTP method - "GET", "POST", "PUT", "DELETE" - `endpoint` (str): API endpoint - "content", "auth", "audrey", etc. - `path` (str): API path after endpoint - `authenticate` (bool): Whether to include authentication token - `query_params` (dict): Optional query parameters **Example - Custom Request:** ```python # Get posts from a specific branch with filters posts = API.make_request( "GET", "content", "post/ref-subwiki/music", query_params={"limit": 10, "offset": 0} ) ``` ## Debug Mode Enable debug mode to see all API requests and responses: ```python API = APIClient( client_id=os.getenv("client_id"), client_secret=os.getenv("client_secret"), environment="production", debug=True # Enable debug mode ) # Now API calls will print request details API.handle_token() ``` ## Examples ### Example 1: Basic Post Creation ```python import trustcafeapiwrapper import os # Setup API = trustcafeapiwrapper.APIClient( client_id=os.getenv("TRUSTCAFE_CLIENT_ID"), client_secret=os.getenv("TRUSTCAFE_CLIENT_SECRET"), environment="alpha", debug=False ) # Handle authentication API.handle_token() # Create a post in the music branch from trustcafeapiwrapper.wrappers.post.create_post import create_post API.wrapped(create_post( "New music discussion post", parent_path="/music", blur_label=None, card_url=None, collaborative=False )) ``` ### Example 2: Following a User and Getting Their Profile ```python import trustcafeapiwrapper import os API = trustcafeapiwrapper.APIClient( client_id=os.getenv("TRUSTCAFE_CLIENT_ID"), client_secret=os.getenv("TRUSTCAFE_CLIENT_SECRET"), environment="production" ) # Follow a user follow = API.run_job('follow.follow', { "isFollowing": True, "parent": { "pk": "userprofile#philosopher-jon", "sk": "userprofile#philosopher-jon" }, "followType": "userprofile", "parentSlug": "philosopher-jon" }) print(f"Followed user: {follow}") # Get their profile profile = API.run_job('userprofile.get', "philosopher-jon") print(f"Profile data: {profile}") ``` ### Example 3: Managing Notifications ```python import trustcafeapiwrapper import os API = trustcafeapiwrapper.APIClient( client_id=os.getenv("TRUSTCAFE_CLIENT_ID"), client_secret=os.getenv("TRUSTCAFE_CLIENT_SECRET"), environment="production" ) API.handle_token() # Check for new notifications notifications = API.run_job('notification.listnotifications') print(f"Unread notifications: {notifications}") # Mark all as read API.run_job('notification.markallasread') print("All notifications marked as read") ``` ### Example 4: Creating a Collaborative Post ```python import trustcafeapiwrapper import os API = trustcafeapiwrapper.APIClient( client_id=os.getenv("TRUSTCAFE_CLIENT_ID"), client_secret=os.getenv("TRUSTCAFE_CLIENT_SECRET"), environment="production" ) API.handle_token() # Create a collaborative post from trustcafeapiwrapper.wrappers.post.create_post import create_post API.wrapped(create_post( "Research paper draft - collaborative editing", parent_path="/research", collaborative=True # Enables collaborative editing )) ``` ## Development ### Project Structure ``` trustcafe-api-wrapper/ ├── src/ │ └── trustcafeapiwrapper/ │ ├── apiclient.py # Core API client │ ├── jobs/ # API job functions │ │ ├── post/ │ │ ├── comment/ │ │ ├── userprofile/ │ │ ├── follow/ │ │ └── ... (all jobs) │ ├── wrappers/ # High-level wrappers │ │ ├── post/ │ │ ├── comment/ │ │ └── ... │ └── utils/ # Helper functions ├── tests/ # Test files ├── README.md # This file ├── documentation.md # Basic documentation ├── development.md # Project notes └── pyproject.toml # Project configuration ``` ### Adding New Jobs 1. Create a new file in `src/trustcafeapiwrapper/jobs/` 2. Define a function matching the job name 3. Export it from the module's `__init__.py` 4. Document the parameters and return value **Example - Adding a Job:** ```python # src/trustcafeapiwrapper/jobs/example/new_job.py def my_new_job(API, param1: str, param2: int) -> dict: """ Create a new job function. Args: API: The APIClient instance param1 (str): Description of parameter 1 param2 (int): Description of parameter 2 Returns: dict: The API response """ response = API.make_request( "POST", "endpoint", "path/to/resource", data={"param1": param1, "param2": param2} ) return response ``` ### Known Limitations - **Incomplete Jobs**: Not all available API endpoints have wrapper functions yet - **Enums**: Some enum types are not strictly typed - **Block Operations**: Block functionality exists in backend but not yet exposed - **Mute Operations**: Mute functionality pending implementation ### Future Enhancements - Complete coverage of all TrustCafé API endpoints - Type hints for better IDE support - Async support for better performance - Comprehensive test suite - Pydantic models for request/response validation ## Testing Run the test suite: ```bash pytest ``` Or run specific tests: ```bash # Test the API client python -m pytest tests/apiclient.py # Run all tests python -m pytest tests/ ``` ## License [Depends on TrustCafé project license] ## Support For issues, questions, or contributions: - [TrustCafé Website](https://trustcafe.io) - [GitLab Repository](https://gitlab.com/trustcafe/trustcafe-api-wrapper) - Contact the WikiTribune team ## Version History - **0.1.0.13** - Current version with ongoing development --- **Note:** This wrapper is for internal WikiTribune/TrustCafé use. Public API endpoints are subject to change.