Last active
October 17, 2025 15:40
-
-
Save extratone/b06a894f0fdeb3052102bc9b849ec7c6 to your computer and use it in GitHub Desktop.
A script querying the mastodon API for all status urls found for a given Mastodon-formatted search query.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import requests | |
| import sys | |
| import argparse | |
| import time | |
| from datetime import datetime | |
| from dateutil import parser as date_parser | |
| from dateutil import tz | |
| from markdownify import markdownify as md | |
| # --- Configuration --- | |
| INSTANCE = "mastodon.social" | |
| ACCESS_TOKEN = "YOUR_ACCESS_TOKEN" | |
| # Set your local timezone to correctly format the timestamp. | |
| # For Columbia, Missouri, 'America/Chicago' is the correct IANA time zone name. | |
| LOCAL_TIMEZONE = "America/Chicago" | |
| # --- End Configuration --- | |
| def format_status_as_embed(status: dict) -> str: | |
| """ | |
| Formats a single status object into the specified ad-Mastodon embed string. | |
| """ | |
| # --- 1. Parse and Format Timestamp --- | |
| utc_time = date_parser.isoparse(status['created_at']) | |
| local_tz = tz.gettz(LOCAL_TIMEZONE) | |
| local_time = utc_time.astimezone(local_tz) | |
| # Format to MMddYYYY-HHmmss ("davodtime") | |
| davodtime_str = local_time.strftime("%m%d%Y-%H%M%S") | |
| # --- 2. Convert Content from HTML to Markdown --- | |
| # The 'content' from the API is HTML | |
| content_md = md(status.get('content', '')).strip() | |
| # --- 3. Get Status URL --- | |
| status_url = status.get('url', '') | |
| # --- 4. Build the Embed Block --- | |
| embed_lines = [ | |
| "\`\`\`ad-Mastodon", | |
| "title: Mastodon", | |
| f"{content_md}", | |
| ] | |
| # --- 5. Conditionally Add Attachments --- | |
| attachments = status.get('media_attachments', []) | |
| if attachments: | |
| embed_lines.append("") # Add a blank line before attachments | |
| for attachment in attachments: | |
| attachment_url = attachment.get('url') | |
| if attachment_url: | |
| # Format as a Markdown image/embed link | |
| embed_lines.append(f"") | |
| # --- 6. Add the Timestamp Link --- | |
| embed_lines.append("") # Add a blank line before the footer | |
| embed_lines.append(f"[{davodtime_str}]({status_url})") | |
| embed_lines.append("\`\`\`") | |
| return "\n".join(embed_lines) | |
| def run_search(query: str): | |
| """ | |
| Executes a paginated search and prints a formatted embed for each result. | |
| """ | |
| if ACCESS_TOKEN == "YOUR_ACCESS_TOKEN": | |
| print("Error: Please update the ACCESS_TOKEN in the script.", file=sys.stderr) | |
| sys.exit(1) | |
| headers = {'Authorization': f'Bearer {ACCESS_TOKEN}'} | |
| api_endpoint = f"https://{INSTANCE}/api/v2/search" | |
| params = {'q': query, 'type': 'statuses', 'limit': 40, 'resolve': 'true'} | |
| total_found = 0 | |
| page_count = 0 | |
| while True: | |
| page_count += 1 | |
| print(f"Fetching page {page_count}...", file=sys.stderr) | |
| try: | |
| response = requests.get(api_endpoint, headers=headers, params=params) | |
| response.raise_for_status() | |
| statuses = response.json().get('statuses', []) | |
| if not statuses: | |
| print("No more results found.", file=sys.stderr) | |
| break | |
| # Process each status and print the formatted embed | |
| for status in statuses: | |
| embed_output = format_status_as_embed(status) | |
| print(embed_output) | |
| print() # Add a line break between each embed block | |
| total_found += 1 | |
| # Update params for the next page request | |
| params['max_id'] = statuses[-1]['id'] | |
| time.sleep(1) | |
| except requests.exceptions.RequestException as e: | |
| print(f"\nAn error occurred: {e}", file=sys.stderr) | |
| break | |
| print("---", file=sys.stderr) | |
| print(f"Search complete. Generated embeds for {total_found} statuses.", file=sys.stderr) | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description="Search Mastodon and generate formatted embeds for each status.", | |
| epilog='Example: python3 generate_mastodon_embeds.py \'"Obsidian" from:DavidBlue\'' | |
| ) | |
| parser.add_argument("query", type=str, help="The search query string.") | |
| args = parser.parse_args() | |
| run_search(args.query) | |
| if __name__ == "__main__": | |
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import requests | |
| import sys | |
| import argparse | |
| # --- Configuration --- | |
| # Your Mastodon instance. The script will be most effective on the instance | |
| # where the user account in the query (if any) resides. | |
| INSTANCE = "mastodon.social" | |
| # Your access token from your instance's Preferences -> Development page. | |
| # A token is required for the search endpoint. | |
| ACCESS_TOKEN = "YOUR_ACCESS_TOKEN" | |
| # --- End Configuration --- | |
| def run_search(query: str): | |
| """ | |
| Executes the search against the Mastodon API and prints status URLs. | |
| Args: | |
| query: The search string, e.g., '"Obsidian" from:DavidBlue'. | |
| """ | |
| if ACCESS_TOKEN == "YOUR_ACCESS_TOKEN": | |
| print("Error: Please replace 'YOUR_ACCESS_TOKEN' with your actual access token in the script.", file=sys.stderr) | |
| sys.exit(1) | |
| headers = {'Authorization': f'Bearer {ACCESS_TOKEN}'} | |
| # We use the v2 search endpoint, which is powerful and supports pagination. | |
| # The initial request includes the search parameters. | |
| api_endpoint = f"https://{INSTANCE}/api/v2/search" | |
| params = { | |
| 'q': query, | |
| 'type': 'statuses', # We are only interested in statuses | |
| 'limit': 40, # Request the maximum number of results per page | |
| 'resolve': 'true' # Attempt to resolve non-local accounts and posts | |
| } | |
| found_urls = [] | |
| page_count = 0 | |
| while api_endpoint: | |
| page_count += 1 | |
| print(f"Fetching page {page_count}...", file=sys.stderr) | |
| try: | |
| # The 'params' are only sent with the first request. | |
| # Subsequent requests will use the full URL from the 'Link' header. | |
| response = requests.get(api_endpoint, headers=headers, params=params) | |
| response.raise_for_status() # Check for HTTP errors like 401 Unauthorized or 404 Not Found | |
| # After the first request, we no longer need the params dictionary | |
| if params: | |
| params = None | |
| data = response.json() | |
| statuses = data.get('statuses', []) | |
| if not statuses: | |
| # No more statuses on this page, we are done. | |
| break | |
| for status in statuses: | |
| status_url = status.get('url') | |
| if status_url: | |
| print(status_url) # Print the final URL directly to standard output | |
| found_urls.append(status_url) | |
| # The 'requests' library parses the 'Link' header for pagination. | |
| # We look for the 'next' link to get the URL for the next page of results. | |
| if 'next' in response.links: | |
| api_endpoint = response.links['next']['url'] | |
| else: | |
| # No 'next' link means we have reached the last page. | |
| api_endpoint = None | |
| except requests.exceptions.RequestException as e: | |
| print(f"Error during API request: {e}", file=sys.stderr) | |
| break | |
| print("---", file=sys.stderr) | |
| print(f"Search complete. Found {len(found_urls)} matching statuses.", file=sys.stderr) | |
| def main(): | |
| """ | |
| Parses command-line arguments and initiates the search. | |
| """ | |
| parser = argparse.ArgumentParser( | |
| description="Search the Mastodon API for statuses and output their URLs.", | |
| epilog='Example: python3 mastodon_search.py \'"Obsidian" from:DavidBlue\'' | |
| ) | |
| parser.add_argument( | |
| "query", | |
| type=str, | |
| help="The search query string, enclosed in quotes if it contains spaces or special characters." | |
| ) | |
| args = parser.parse_args() | |
| run_search(args.query) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The Pak games Apk is Pakistan’s top mobile earning app, offering real-cash rewards through JazzCash, Easypaisa, and bank transfers. Play Aviator, Color Prediction, Rummy, and more all in one lightweight Android APK.