Skip to main content
Glama
tai1mo
by tai1mo
README.md
# Blogger Post CLI Script

[日本語版ドキュメントはこちら (README.ja.md)](README.ja.md)

A Python utility to create and publish posts on Blogger using the Google Blogger API v3 from the command line.

## Features
- **Title and Content Specification**: Create or update posts by passing raw HTML content strings (`--content`) or specifying a path to an HTML file (`--file`).
- **Draft or Live Status**: Save as a draft or publish immediately to your blog.
- **Labels Management**: Attach comma-separated labels (tags) to your posts.
- **OAuth 2.0 Authentication**: Automatically handles authorization via web browser and securely caches tokens locally.
- **Update and History Tracking**: Update existing posts using `blogger_update.py`. Post history is automatically saved to `post_history.json`.
- **Fetch Post Information**: Retrieve current live status, content, and other metadata of existing posts directly from Blogger using `blogger_get.py`. Output can be dynamically filtered by specific fields.
- **Module Support**: Import modules into other Python scripts to programmatically create or update posts.
- **Multi-Blog Support**: Dynamically route posts to different blogs based on genre or alias using the `--target_blog` parameter.
- **MCP Server Support**: Includes `Blogger_mcp_server.py` (FastMCP) allowing AI assistants to post or update articles directly.

---

## Setup Instructions

### 1. Install Dependencies

Create a Python 3.x virtual environment and install the required packages:

```bash
# Create virtual environment
python3 -m venv venv

# Activate virtual environment
source venv/bin/activate

# Install required packages
pip install -r requirements.txt
```

### 2. Configure Environment Variables and Credentials

To run this tool, you need to set up two configuration files in the script directory:

1. **`.env` File**
   Create a `.env` file in the same directory as the script with the following content:
   ```env
   BLOG_ID=YOUR_DEFAULT_BLOG_ID
   BLOG_ID_SITE1=YOUR_SITE1_BLOG_ID (Optional: links with --target_blog site1)
   BLOG_ID_SITE2=YOUR_SITE2_BLOG_ID (Optional: links with --target_blog site2)
   GCS_BUCKET_NAME=YOUR_GCS_BUCKET_NAME
   GCP_PROJECT_ID=YOUR_GCP_PROJECT_ID
   ```
2. **`client_secret.json` File**
   Download your Desktop Application OAuth 2.0 client credentials JSON file from the Google Cloud Console, rename it to `client_secret.json`, and place it in the script directory.

3. **GCS Authentication for Image Uploads (ADC)**
   To use the image upload feature (`image_post.py`), run the following command locally and complete the browser authentication for Application Default Credentials (ADC):
   ```bash
   gcloud auth application-default login
   ```

> [!WARNING]
> For security, `.env`, `client_secret.json`, and the auto-generated `token.json` are excluded from Git commits via `.gitignore`. Be careful not to expose your credentials.

---

## Usage

Make sure your virtual environment is activated before running the script.

### Create a Post (Save as Draft)

Provide a title and HTML string content to save a draft post:

```bash
python blogger_post.py \
  --title "My First API Post" \
  --content "<h1>Hello World</h1><p>This is a test post from Blogger API.</p>"
```

### Read from HTML File and Publish (Live Status)

Specify an HTML file path using `--file` and set `--status live` to publish the post immediately:

```bash
python blogger_post.py \
  --title "Article Loaded from HTML File" \
  --file "path/to/article.html" \
  --status live
```

### Add Labels to Your Post

Pass labels as a comma-separated string using the `--labels` option:

```bash
python blogger_post.py \
  --title "Post with Labels" \
  --content "<p>Testing Blogger API v3 labels.</p>" \
  --labels "Python, Blogger API, Test"
```

### Route Post to a Specific Blog (Multi-Blog Support)

If you have configured multiple blog IDs in your `.env` (e.g., `BLOG_ID_SITE1`), use the `--target_blog` argument to dynamically switch the destination:

```bash
python blogger_post.py \
  --title "Site 1 News" \
  --content "<p>This goes to the site1 blog.</p>" \
  --target_blog "site1"
```

### Update an Existing Post

Use `blogger_update.py` to update an existing post by its `post_id`:

```bash
python blogger_update.py \
  --post_id 1234567890123456789 \
  --title "Updated Title" \
  --content "<p>Updated content.</p>" \
  --status live
```
*Note: You can also specify an HTML file using `--file "path/to/article.html"` instead of `--content`. Successful posts and updates are automatically recorded in `post_history.json`.*

### Fetch Post Information

Use `blogger_get.py` to retrieve the current status, content, or other metadata of a specific `post_id`. You can extract only specific fields using the `--fields` argument.

**Available Fields (`--fields`)**:
- `id`: The ID of the post
- `title`: The title of the post
- `status`: The publication status (e.g., `LIVE`, `DRAFT`, `SCHEDULED`)
- `url`: The public URL of the post
- `labels`: A list of labels (tags) attached to the post
- `content`: The HTML content of the post
- `published`: The time the post was published
- `updated`: The time the post was last updated
- `replies`: Comment information
- `author`: Information about the author

```bash
python blogger_get.py \
  --post_id 1234567890123456789 \
  --fields "title,status,url"
```

### Use as a Module

You can also import the script into other Python projects:

```python
from blogger_post import post_article

try:
    url = post_article(
        title="My API Post",
        content="<p>This is posted via Python module.</p>",
        labels=["Python", "Module"],
        status="draft"
    )
    print(f"Posted successfully: {url}")
except Exception as e:
    print(f"Failed to post: {e}")
```

### Use as an MCP Server

The included `Blogger_mcp_server.py` script runs a FastMCP server. By configuring your AI assistant (e.g., Claude) to use this script as an MCP server, it exposes the following tools:

- `post_to_blogger`: Publish or draft new articles and automatically save the history locally. Accepts HTML strings (`content`) or local HTML file paths (`file_path`), as well as `target_blog` to route posts dynamically.
- `update_blogger_post`: Update existing articles by their `post_id` and sync the local history. Accepts HTML strings (`content`) or local HTML file paths (`file_path`), and `target_blog`.
- `upload_image_to_gcs`: Uploads a single local image to GCS (automatically converts to WebP and resizes) and returns the public URL.
- `replace_html_images_with_gcs`: Reads local image links within an HTML document, uploads all of them to GCS, and returns a new HTML string with replaced image URLs.
- `get_blogger_post`: Retrieves the current information of a specific post directly from the Blogger API. You can specify which fields (e.g., status, url) to extract.

*Note: Ensure the `mcp`, `google-cloud-storage`, `Pillow`, and `beautifulsoup4` packages are installed (`pip install -r requirements.txt`) before running the MCP server.*

---

## Error Handling

If any issue occurs, the script catches the exception and outputs localized troubleshooting advice:

- **File Errors**: Warns you if the HTML file is missing, lacks read permissions, or is not UTF-8 encoded.
- **Network Connection Errors**: Catches failures when there is no internet connection, Google servers are unreachable, or the request times out (maximum **30-second socket timeout** to prevent hanging).
- **HTTP Errors (403, 404, 500, etc.)**: Suggests causes and fixes depending on the status code (e.g. check Blogger API enablement for 403, verify BLOG_ID for 404, or wait for Google servers for 500).
- **Authentication Errors**: Guides you if credentials are invalid or expired. This includes a **120-second timeout** for the local browser OAuth flow; if it times out (common when your Google Cloud project is in "Testing" mode and the current account is not registered as a "Test User"), the script will abort and print steps to configure test users in Google Cloud Console.

---

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Disclaimer

- This script is provided "AS IS", without warranty of any kind.
- The authors are not responsible for any damage, data loss, or API quota consumption caused by using this software.
- Please comply with Google's API terms of service when using this tool.