{
  "schemaVersion": "1.0",
  "item": {
    "slug": "google-docs-skill",
    "name": "Google Docs Skill",
    "source": "tencent",
    "type": "skill",
    "category": "效率提升",
    "sourceUrl": "https://clawhub.ai/zagran/google-docs-skill",
    "canonicalUrl": "https://clawhub.ai/zagran/google-docs-skill",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/google-docs-skill",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=google-docs-skill",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-23T16:43:11.935Z",
      "expiresAt": "2026-04-30T16:43:11.935Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
        "contentDisposition": "attachment; filename=\"4claw-imageboard-1.0.1.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/google-docs-skill"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/google-docs-skill",
    "agentPageUrl": "https://openagent3.xyz/skills/google-docs-skill/agent",
    "manifestUrl": "https://openagent3.xyz/skills/google-docs-skill/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/google-docs-skill/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Google Docs API Integration",
        "body": "Direct access to the Google Docs API using OAuth 2.0. Create documents, insert and format text, and manage document content."
      },
      {
        "title": "Prerequisites",
        "body": "Google Cloud Project Setup\n\nGo to Google Cloud Console\nCreate a new project or select existing one\nEnable the Google Docs API\nCreate OAuth 2.0 credentials (Desktop app or Web application)\nDownload the credentials JSON file\n\n\n\nEnvironment Setup\nexport GOOGLE_CLIENT_ID=\"your-client-id\"\nexport GOOGLE_CLIENT_SECRET=\"your-client-secret\"\nexport GOOGLE_REFRESH_TOKEN=\"your-refresh-token\""
      },
      {
        "title": "Getting OAuth Tokens",
        "body": "Use the following Python script to obtain your refresh token (one-time setup):\n\nimport urllib.request\nimport urllib.parse\nimport json\nimport webbrowser\nfrom http.server import HTTPServer, BaseHTTPRequestHandler\nimport os\n\n# OAuth configuration\nCLIENT_ID = os.environ.get('GOOGLE_CLIENT_ID')\nCLIENT_SECRET = os.environ.get('GOOGLE_CLIENT_SECRET')\nREDIRECT_URI = 'http://localhost:8080'\nSCOPES = 'https://www.googleapis.com/auth/documents'\n\n# Step 1: Get authorization code\nauth_url = (\n    f\"https://accounts.google.com/o/oauth2/v2/auth?\"\n    f\"client_id={CLIENT_ID}&\"\n    f\"redirect_uri={REDIRECT_URI}&\"\n    f\"response_type=code&\"\n    f\"scope={urllib.parse.quote(SCOPES)}&\"\n    f\"access_type=offline&\"\n    f\"prompt=consent\"\n)\n\nprint(f\"Opening browser for authorization...\")\nwebbrowser.open(auth_url)\n\n# Step 2: Capture authorization code\nauth_code = None\n\nclass OAuthHandler(BaseHTTPRequestHandler):\n    def do_GET(self):\n        global auth_code\n        query = urllib.parse.urlparse(self.path).query\n        params = urllib.parse.parse_qs(query)\n        auth_code = params.get('code', [None])[0]\n        \n        self.send_response(200)\n        self.send_header('Content-type', 'text/html')\n        self.end_headers()\n        self.wfile.write(b'<html><body><h1>Authorization successful!</h1><p>You can close this window.</p></body></html>')\n\nserver = HTTPServer(('localhost', 8080), OAuthHandler)\nserver.handle_request()\n\n# Step 3: Exchange code for tokens\nif auth_code:\n    data = urllib.parse.urlencode({\n        'code': auth_code,\n        'client_id': CLIENT_ID,\n        'client_secret': CLIENT_SECRET,\n        'redirect_uri': REDIRECT_URI,\n        'grant_type': 'authorization_code'\n    }).encode()\n    \n    req = urllib.request.Request('https://oauth2.googleapis.com/token', data=data)\n    response = json.load(urllib.request.urlopen(req))\n    \n    print(f\"\\nRefresh Token: {response['refresh_token']}\")\n    print(f\"Access Token: {response['access_token']}\")\n    print(f\"\\nSet your refresh token:\")\n    print(f\"export GOOGLE_REFRESH_TOKEN=\\\"{response['refresh_token']}\\\"\")"
      },
      {
        "title": "Getting Access Token",
        "body": "Before making API calls, get a fresh access token:\n\nimport urllib.request\nimport urllib.parse\nimport json\nimport os\n\ndef get_access_token():\n    \"\"\"Get a fresh access token using refresh token\"\"\"\n    data = urllib.parse.urlencode({\n        'client_id': os.environ['GOOGLE_CLIENT_ID'],\n        'client_secret': os.environ['GOOGLE_CLIENT_SECRET'],\n        'refresh_token': os.environ['GOOGLE_REFRESH_TOKEN'],\n        'grant_type': 'refresh_token'\n    }).encode()\n    \n    req = urllib.request.Request('https://oauth2.googleapis.com/token', data=data)\n    response = json.load(urllib.request.urlopen(req))\n    return response['access_token']\n\n# Store for reuse\naccess_token = get_access_token()\nprint(f\"Access Token: {access_token}\")"
      },
      {
        "title": "Base URL",
        "body": "https://docs.googleapis.com/v1\n\nAll requests require the access token in the Authorization header:\n\nAuthorization: Bearer {access_token}"
      },
      {
        "title": "Quick Start",
        "body": "import urllib.request\nimport json\nimport os\n\n# Get access token first (using function from above)\naccess_token = get_access_token()\n\n# Get document\nreq = urllib.request.Request('https://docs.googleapis.com/v1/documents/{documentId}')\nreq.add_header('Authorization', f'Bearer {access_token}')\ndoc = json.load(urllib.request.urlopen(req))\nprint(json.dumps(doc, indent=2))"
      },
      {
        "title": "Create Document",
        "body": "import urllib.request\nimport json\n\naccess_token = get_access_token()\n\ndata = json.dumps({'title': 'My New Document'}).encode()\nreq = urllib.request.Request(\n    'https://docs.googleapis.com/v1/documents',\n    data=data,\n    method='POST'\n)\nreq.add_header('Authorization', f'Bearer {access_token}')\nreq.add_header('Content-Type', 'application/json')\n\nresponse = json.load(urllib.request.urlopen(req))\ndoc_id = response['documentId']\nprint(f\"Created document: {doc_id}\")\nprint(f\"URL: https://docs.google.com/document/d/{doc_id}/edit\")"
      },
      {
        "title": "Get Document",
        "body": "req = urllib.request.Request(\n    f'https://docs.googleapis.com/v1/documents/{doc_id}'\n)\nreq.add_header('Authorization', f'Bearer {access_token}')\ndoc = json.load(urllib.request.urlopen(req))"
      },
      {
        "title": "Batch Update Document",
        "body": "# Insert text at beginning\nupdates = {\n    'requests': [\n        {\n            'insertText': {\n                'location': {'index': 1},\n                'text': 'Hello, World!\\n\\n'\n            }\n        }\n    ]\n}\n\ndata = json.dumps(updates).encode()\nreq = urllib.request.Request(\n    f'https://docs.googleapis.com/v1/documents/{doc_id}:batchUpdate',\n    data=data,\n    method='POST'\n)\nreq.add_header('Authorization', f'Bearer {access_token}')\nreq.add_header('Content-Type', 'application/json')\n\nresponse = json.load(urllib.request.urlopen(req))"
      },
      {
        "title": "Insert Text",
        "body": "{\n    'requests': [\n        {\n            'insertText': {\n                'location': {'index': 1},\n                'text': 'Your text here'\n            }\n        }\n    ]\n}"
      },
      {
        "title": "Format Text (Bold, Italic, Font Size)",
        "body": "{\n    'requests': [\n        {\n            'updateTextStyle': {\n                'range': {\n                    'startIndex': 1,\n                    'endIndex': 10\n                },\n                'textStyle': {\n                    'bold': True,\n                    'italic': True,\n                    'fontSize': {\n                        'magnitude': 14,\n                        'unit': 'PT'\n                    }\n                },\n                'fields': 'bold,italic,fontSize'\n            }\n        }\n    ]\n}"
      },
      {
        "title": "Insert Table",
        "body": "{\n    'requests': [\n        {\n            'insertTable': {\n                'location': {'index': 1},\n                'rows': 3,\n                'columns': 4\n            }\n        }\n    ]\n}"
      },
      {
        "title": "Replace Text",
        "body": "{\n    'requests': [\n        {\n            'replaceAllText': {\n                'containsText': {\n                    'text': '{{placeholder}}',\n                    'matchCase': True\n                },\n                'replaceText': 'Actual value'\n            }\n        }\n    ]\n}"
      },
      {
        "title": "Delete Content",
        "body": "{\n    'requests': [\n        {\n            'deleteContentRange': {\n                'range': {\n                    'startIndex': 1,\n                    'endIndex': 50\n                }\n            }\n        }\n    ]\n}"
      },
      {
        "title": "Insert Page Break",
        "body": "{\n    'requests': [\n        {\n            'insertPageBreak': {\n                'location': {'index': 1}\n            }\n        }\n    ]\n}"
      },
      {
        "title": "Update Paragraph Style",
        "body": "{\n    'requests': [\n        {\n            'updateParagraphStyle': {\n                'range': {\n                    'startIndex': 1,\n                    'endIndex': 50\n                },\n                'paragraphStyle': {\n                    'namedStyleType': 'HEADING_1',\n                    'alignment': 'CENTER'\n                },\n                'fields': 'namedStyleType,alignment'\n            }\n        }\n    ]\n}"
      },
      {
        "title": "Complete Example: Create and Format Document",
        "body": "import urllib.request\nimport urllib.parse\nimport json\nimport os\n\ndef get_access_token():\n    \"\"\"Get fresh access token\"\"\"\n    data = urllib.parse.urlencode({\n        'client_id': os.environ['GOOGLE_CLIENT_ID'],\n        'client_secret': os.environ['GOOGLE_CLIENT_SECRET'],\n        'refresh_token': os.environ['GOOGLE_REFRESH_TOKEN'],\n        'grant_type': 'refresh_token'\n    }).encode()\n    \n    req = urllib.request.Request('https://oauth2.googleapis.com/token', data=data)\n    response = json.load(urllib.request.urlopen(req))\n    return response['access_token']\n\ndef create_document(title, access_token):\n    \"\"\"Create a new document\"\"\"\n    data = json.dumps({'title': title}).encode()\n    req = urllib.request.Request(\n        'https://docs.googleapis.com/v1/documents',\n        data=data,\n        method='POST'\n    )\n    req.add_header('Authorization', f'Bearer {access_token}')\n    req.add_header('Content-Type', 'application/json')\n    \n    response = json.load(urllib.request.urlopen(req))\n    return response['documentId']\n\ndef batch_update(doc_id, requests, access_token):\n    \"\"\"Apply batch updates to document\"\"\"\n    data = json.dumps({'requests': requests}).encode()\n    req = urllib.request.Request(\n        f'https://docs.googleapis.com/v1/documents/{doc_id}:batchUpdate',\n        data=data,\n        method='POST'\n    )\n    req.add_header('Authorization', f'Bearer {access_token}')\n    req.add_header('Content-Type', 'application/json')\n    \n    return json.load(urllib.request.urlopen(req))\n\n# Main workflow\naccess_token = get_access_token()\n\n# Create document\ndoc_id = create_document('Project Report', access_token)\nprint(f\"Created: https://docs.google.com/document/d/{doc_id}/edit\")\n\n# Add content with formatting\nrequests = [\n    # Insert title\n    {\n        'insertText': {\n            'location': {'index': 1},\n            'text': 'Project Report\\n\\n'\n        }\n    },\n    # Format title as heading\n    {\n        'updateParagraphStyle': {\n            'range': {'startIndex': 1, 'endIndex': 15},\n            'paragraphStyle': {'namedStyleType': 'HEADING_1'},\n            'fields': 'namedStyleType'\n        }\n    },\n    # Add body text\n    {\n        'insertText': {\n            'location': {'index': 16},\n            'text': 'Executive Summary\\n\\nThis report covers...\\n\\n'\n        }\n    },\n    # Format section header\n    {\n        'updateParagraphStyle': {\n            'range': {'startIndex': 16, 'endIndex': 34},\n            'paragraphStyle': {'namedStyleType': 'HEADING_2'},\n            'fields': 'namedStyleType'\n        }\n    }\n]\n\nbatch_update(doc_id, requests, access_token)\nprint(\"Document updated successfully!\")"
      },
      {
        "title": "Document Indexing",
        "body": "Indices are 1-based (document starts at index 1)\nEach character (including newlines) occupies one index\nTo append at the end, use endOfSegmentLocation"
      },
      {
        "title": "Batch Updates",
        "body": "Multiple requests are applied atomically\nRequests are processed in order\nGet the document first to determine correct indices"
      },
      {
        "title": "Field Masks",
        "body": "When updating styles, specify which fields to update\nUse comma-separated field names: 'fields': 'bold,fontSize,foregroundColor'"
      },
      {
        "title": "Error Handling",
        "body": "Status CodeMeaning400Bad Request - Invalid request format401Unauthorized - Invalid or expired access token403Forbidden - Insufficient permissions404Not Found - Document doesn't exist429Rate Limited - Too many requests"
      },
      {
        "title": "Token Refresh",
        "body": "Access tokens expire after 1 hour. If you get a 401 error, refresh the token:\n\ntry:\n    # Make API call\n    req = urllib.request.Request(url)\n    req.add_header('Authorization', f'Bearer {access_token}')\n    response = urllib.request.urlopen(req)\nexcept urllib.error.HTTPError as e:\n    if e.code == 401:\n        # Refresh token and retry\n        access_token = get_access_token()\n        req = urllib.request.Request(url)\n        req.add_header('Authorization', f'Bearer {access_token}')\n        response = urllib.request.urlopen(req)"
      },
      {
        "title": "Best Practices",
        "body": "Token Management\n\nCache access tokens (valid for 1 hour)\nStore refresh token securely\nImplement automatic token refresh\n\n\n\nBatch Operations\n\nCombine multiple updates into single batch call\nReduces API calls and improves performance\n\n\n\nIndex Calculation\n\nAlways get current document state before updates\nAccount for newline characters (\\n)\nUse endOfSegmentLocation for appending\n\n\n\nRate Limits\n\nGoogle Docs API has quotas (check Cloud Console)\nImplement exponential backoff for rate limit errors"
      },
      {
        "title": "Resources",
        "body": "Google Docs API Documentation\nOAuth 2.0 for Desktop Apps\nDocument Structure\nRequest Types Reference\nPython Quickstart"
      }
    ],
    "body": "Google Docs API Integration\n\nDirect access to the Google Docs API using OAuth 2.0. Create documents, insert and format text, and manage document content.\n\nPrerequisites\n\nGoogle Cloud Project Setup\n\nGo to Google Cloud Console\nCreate a new project or select existing one\nEnable the Google Docs API\nCreate OAuth 2.0 credentials (Desktop app or Web application)\nDownload the credentials JSON file\n\nEnvironment Setup\n\nexport GOOGLE_CLIENT_ID=\"your-client-id\"\nexport GOOGLE_CLIENT_SECRET=\"your-client-secret\"\nexport GOOGLE_REFRESH_TOKEN=\"your-refresh-token\"\n\nAuthentication\nGetting OAuth Tokens\n\nUse the following Python script to obtain your refresh token (one-time setup):\n\nimport urllib.request\nimport urllib.parse\nimport json\nimport webbrowser\nfrom http.server import HTTPServer, BaseHTTPRequestHandler\nimport os\n\n# OAuth configuration\nCLIENT_ID = os.environ.get('GOOGLE_CLIENT_ID')\nCLIENT_SECRET = os.environ.get('GOOGLE_CLIENT_SECRET')\nREDIRECT_URI = 'http://localhost:8080'\nSCOPES = 'https://www.googleapis.com/auth/documents'\n\n# Step 1: Get authorization code\nauth_url = (\n    f\"https://accounts.google.com/o/oauth2/v2/auth?\"\n    f\"client_id={CLIENT_ID}&\"\n    f\"redirect_uri={REDIRECT_URI}&\"\n    f\"response_type=code&\"\n    f\"scope={urllib.parse.quote(SCOPES)}&\"\n    f\"access_type=offline&\"\n    f\"prompt=consent\"\n)\n\nprint(f\"Opening browser for authorization...\")\nwebbrowser.open(auth_url)\n\n# Step 2: Capture authorization code\nauth_code = None\n\nclass OAuthHandler(BaseHTTPRequestHandler):\n    def do_GET(self):\n        global auth_code\n        query = urllib.parse.urlparse(self.path).query\n        params = urllib.parse.parse_qs(query)\n        auth_code = params.get('code', [None])[0]\n        \n        self.send_response(200)\n        self.send_header('Content-type', 'text/html')\n        self.end_headers()\n        self.wfile.write(b'<html><body><h1>Authorization successful!</h1><p>You can close this window.</p></body></html>')\n\nserver = HTTPServer(('localhost', 8080), OAuthHandler)\nserver.handle_request()\n\n# Step 3: Exchange code for tokens\nif auth_code:\n    data = urllib.parse.urlencode({\n        'code': auth_code,\n        'client_id': CLIENT_ID,\n        'client_secret': CLIENT_SECRET,\n        'redirect_uri': REDIRECT_URI,\n        'grant_type': 'authorization_code'\n    }).encode()\n    \n    req = urllib.request.Request('https://oauth2.googleapis.com/token', data=data)\n    response = json.load(urllib.request.urlopen(req))\n    \n    print(f\"\\nRefresh Token: {response['refresh_token']}\")\n    print(f\"Access Token: {response['access_token']}\")\n    print(f\"\\nSet your refresh token:\")\n    print(f\"export GOOGLE_REFRESH_TOKEN=\\\"{response['refresh_token']}\\\"\")\n\nGetting Access Token\n\nBefore making API calls, get a fresh access token:\n\nimport urllib.request\nimport urllib.parse\nimport json\nimport os\n\ndef get_access_token():\n    \"\"\"Get a fresh access token using refresh token\"\"\"\n    data = urllib.parse.urlencode({\n        'client_id': os.environ['GOOGLE_CLIENT_ID'],\n        'client_secret': os.environ['GOOGLE_CLIENT_SECRET'],\n        'refresh_token': os.environ['GOOGLE_REFRESH_TOKEN'],\n        'grant_type': 'refresh_token'\n    }).encode()\n    \n    req = urllib.request.Request('https://oauth2.googleapis.com/token', data=data)\n    response = json.load(urllib.request.urlopen(req))\n    return response['access_token']\n\n# Store for reuse\naccess_token = get_access_token()\nprint(f\"Access Token: {access_token}\")\n\nBase URL\nhttps://docs.googleapis.com/v1\n\n\nAll requests require the access token in the Authorization header:\n\nAuthorization: Bearer {access_token}\n\nQuick Start\nimport urllib.request\nimport json\nimport os\n\n# Get access token first (using function from above)\naccess_token = get_access_token()\n\n# Get document\nreq = urllib.request.Request('https://docs.googleapis.com/v1/documents/{documentId}')\nreq.add_header('Authorization', f'Bearer {access_token}')\ndoc = json.load(urllib.request.urlopen(req))\nprint(json.dumps(doc, indent=2))\n\nAPI Reference\nCreate Document\nimport urllib.request\nimport json\n\naccess_token = get_access_token()\n\ndata = json.dumps({'title': 'My New Document'}).encode()\nreq = urllib.request.Request(\n    'https://docs.googleapis.com/v1/documents',\n    data=data,\n    method='POST'\n)\nreq.add_header('Authorization', f'Bearer {access_token}')\nreq.add_header('Content-Type', 'application/json')\n\nresponse = json.load(urllib.request.urlopen(req))\ndoc_id = response['documentId']\nprint(f\"Created document: {doc_id}\")\nprint(f\"URL: https://docs.google.com/document/d/{doc_id}/edit\")\n\nGet Document\nreq = urllib.request.Request(\n    f'https://docs.googleapis.com/v1/documents/{doc_id}'\n)\nreq.add_header('Authorization', f'Bearer {access_token}')\ndoc = json.load(urllib.request.urlopen(req))\n\nBatch Update Document\n# Insert text at beginning\nupdates = {\n    'requests': [\n        {\n            'insertText': {\n                'location': {'index': 1},\n                'text': 'Hello, World!\\n\\n'\n            }\n        }\n    ]\n}\n\ndata = json.dumps(updates).encode()\nreq = urllib.request.Request(\n    f'https://docs.googleapis.com/v1/documents/{doc_id}:batchUpdate',\n    data=data,\n    method='POST'\n)\nreq.add_header('Authorization', f'Bearer {access_token}')\nreq.add_header('Content-Type', 'application/json')\n\nresponse = json.load(urllib.request.urlopen(req))\n\nCommon Operations\nInsert Text\n{\n    'requests': [\n        {\n            'insertText': {\n                'location': {'index': 1},\n                'text': 'Your text here'\n            }\n        }\n    ]\n}\n\nFormat Text (Bold, Italic, Font Size)\n{\n    'requests': [\n        {\n            'updateTextStyle': {\n                'range': {\n                    'startIndex': 1,\n                    'endIndex': 10\n                },\n                'textStyle': {\n                    'bold': True,\n                    'italic': True,\n                    'fontSize': {\n                        'magnitude': 14,\n                        'unit': 'PT'\n                    }\n                },\n                'fields': 'bold,italic,fontSize'\n            }\n        }\n    ]\n}\n\nInsert Table\n{\n    'requests': [\n        {\n            'insertTable': {\n                'location': {'index': 1},\n                'rows': 3,\n                'columns': 4\n            }\n        }\n    ]\n}\n\nReplace Text\n{\n    'requests': [\n        {\n            'replaceAllText': {\n                'containsText': {\n                    'text': '{{placeholder}}',\n                    'matchCase': True\n                },\n                'replaceText': 'Actual value'\n            }\n        }\n    ]\n}\n\nDelete Content\n{\n    'requests': [\n        {\n            'deleteContentRange': {\n                'range': {\n                    'startIndex': 1,\n                    'endIndex': 50\n                }\n            }\n        }\n    ]\n}\n\nInsert Page Break\n{\n    'requests': [\n        {\n            'insertPageBreak': {\n                'location': {'index': 1}\n            }\n        }\n    ]\n}\n\nUpdate Paragraph Style\n{\n    'requests': [\n        {\n            'updateParagraphStyle': {\n                'range': {\n                    'startIndex': 1,\n                    'endIndex': 50\n                },\n                'paragraphStyle': {\n                    'namedStyleType': 'HEADING_1',\n                    'alignment': 'CENTER'\n                },\n                'fields': 'namedStyleType,alignment'\n            }\n        }\n    ]\n}\n\nComplete Example: Create and Format Document\nimport urllib.request\nimport urllib.parse\nimport json\nimport os\n\ndef get_access_token():\n    \"\"\"Get fresh access token\"\"\"\n    data = urllib.parse.urlencode({\n        'client_id': os.environ['GOOGLE_CLIENT_ID'],\n        'client_secret': os.environ['GOOGLE_CLIENT_SECRET'],\n        'refresh_token': os.environ['GOOGLE_REFRESH_TOKEN'],\n        'grant_type': 'refresh_token'\n    }).encode()\n    \n    req = urllib.request.Request('https://oauth2.googleapis.com/token', data=data)\n    response = json.load(urllib.request.urlopen(req))\n    return response['access_token']\n\ndef create_document(title, access_token):\n    \"\"\"Create a new document\"\"\"\n    data = json.dumps({'title': title}).encode()\n    req = urllib.request.Request(\n        'https://docs.googleapis.com/v1/documents',\n        data=data,\n        method='POST'\n    )\n    req.add_header('Authorization', f'Bearer {access_token}')\n    req.add_header('Content-Type', 'application/json')\n    \n    response = json.load(urllib.request.urlopen(req))\n    return response['documentId']\n\ndef batch_update(doc_id, requests, access_token):\n    \"\"\"Apply batch updates to document\"\"\"\n    data = json.dumps({'requests': requests}).encode()\n    req = urllib.request.Request(\n        f'https://docs.googleapis.com/v1/documents/{doc_id}:batchUpdate',\n        data=data,\n        method='POST'\n    )\n    req.add_header('Authorization', f'Bearer {access_token}')\n    req.add_header('Content-Type', 'application/json')\n    \n    return json.load(urllib.request.urlopen(req))\n\n# Main workflow\naccess_token = get_access_token()\n\n# Create document\ndoc_id = create_document('Project Report', access_token)\nprint(f\"Created: https://docs.google.com/document/d/{doc_id}/edit\")\n\n# Add content with formatting\nrequests = [\n    # Insert title\n    {\n        'insertText': {\n            'location': {'index': 1},\n            'text': 'Project Report\\n\\n'\n        }\n    },\n    # Format title as heading\n    {\n        'updateParagraphStyle': {\n            'range': {'startIndex': 1, 'endIndex': 15},\n            'paragraphStyle': {'namedStyleType': 'HEADING_1'},\n            'fields': 'namedStyleType'\n        }\n    },\n    # Add body text\n    {\n        'insertText': {\n            'location': {'index': 16},\n            'text': 'Executive Summary\\n\\nThis report covers...\\n\\n'\n        }\n    },\n    # Format section header\n    {\n        'updateParagraphStyle': {\n            'range': {'startIndex': 16, 'endIndex': 34},\n            'paragraphStyle': {'namedStyleType': 'HEADING_2'},\n            'fields': 'namedStyleType'\n        }\n    }\n]\n\nbatch_update(doc_id, requests, access_token)\nprint(\"Document updated successfully!\")\n\nKey Concepts\nDocument Indexing\nIndices are 1-based (document starts at index 1)\nEach character (including newlines) occupies one index\nTo append at the end, use endOfSegmentLocation\nBatch Updates\nMultiple requests are applied atomically\nRequests are processed in order\nGet the document first to determine correct indices\nField Masks\nWhen updating styles, specify which fields to update\nUse comma-separated field names: 'fields': 'bold,fontSize,foregroundColor'\nError Handling\nStatus Code\tMeaning\n400\tBad Request - Invalid request format\n401\tUnauthorized - Invalid or expired access token\n403\tForbidden - Insufficient permissions\n404\tNot Found - Document doesn't exist\n429\tRate Limited - Too many requests\nToken Refresh\n\nAccess tokens expire after 1 hour. If you get a 401 error, refresh the token:\n\ntry:\n    # Make API call\n    req = urllib.request.Request(url)\n    req.add_header('Authorization', f'Bearer {access_token}')\n    response = urllib.request.urlopen(req)\nexcept urllib.error.HTTPError as e:\n    if e.code == 401:\n        # Refresh token and retry\n        access_token = get_access_token()\n        req = urllib.request.Request(url)\n        req.add_header('Authorization', f'Bearer {access_token}')\n        response = urllib.request.urlopen(req)\n\nBest Practices\n\nToken Management\n\nCache access tokens (valid for 1 hour)\nStore refresh token securely\nImplement automatic token refresh\n\nBatch Operations\n\nCombine multiple updates into single batch call\nReduces API calls and improves performance\n\nIndex Calculation\n\nAlways get current document state before updates\nAccount for newline characters (\\n)\nUse endOfSegmentLocation for appending\n\nRate Limits\n\nGoogle Docs API has quotas (check Cloud Console)\nImplement exponential backoff for rate limit errors\nResources\nGoogle Docs API Documentation\nOAuth 2.0 for Desktop Apps\nDocument Structure\nRequest Types Reference\nPython Quickstart"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/zagran/google-docs-skill",
    "publisherUrl": "https://clawhub.ai/zagran/google-docs-skill",
    "owner": "zagran",
    "version": "0.1.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/google-docs-skill",
    "downloadUrl": "https://openagent3.xyz/downloads/google-docs-skill",
    "agentUrl": "https://openagent3.xyz/skills/google-docs-skill/agent",
    "manifestUrl": "https://openagent3.xyz/skills/google-docs-skill/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/google-docs-skill/agent.md"
  }
}