Skip to main content

Unofficial Python client for Dida365/TickTick API - Community-maintained package for task automation

Project description

Dida365/TickTick API Client

Documentation

An unofficial Python client library for the Dida365/TickTick API, supporting both the Chinese (Dida365) and international (TickTick) versions of the service. Built with modern async Python and robust error handling.

This is a package created to facilitate task management automation. It is not affiliated with or endorsed by Dida365 or TickTick.

Project documentation is available here

API Documentation References

OAuth2 Setup

  1. Get your OAuth2 credentials:

  2. Configure OAuth2 redirect URL:

    • In your Manage App page, click "Edit" of your newly created app
    • Add the redirect URL: http://localhost:8080/callback at "OAuth redirect URL"
    • Save the changes
    • Note: If you want to use a different redirect URL, make sure to update it in both:
      • The app settings on TickTick/Dida365 developer portal
      • Your .env file (see below)
  3. Configure your credentials:

    # .env file
    DIDA365_CLIENT_ID=your_client_id        # From step 1
    DIDA365_CLIENT_SECRET=your_client_secret # From step 1
    DIDA365_REDIRECT_URI=http://localhost:8080/callback  # From step 2
    DIDA365_SERVICE_TYPE=ticktick  # or dida365
    

Features

  • ✨ Full async support using httpx
  • 🔒 OAuth2 authentication with automatic token management
  • 📝 Type-safe with Pydantic v2 models
  • 🌐 Configurable endpoints (Dida365/TickTick)
  • 🛡️ Comprehensive error handling
  • ⚡ Automatic retry mechanism
  • 🔄 Environment file integration
  • 📊 State management for tasks and projects

Installation

pip install dida365

Quick Start

import asyncio
from datetime import datetime, timezone
from dida365 import Dida365Client, ServiceType, TaskCreate, ProjectCreate, TaskPriority

async def main():
    # Initialize client (credentials can also be loaded from .env file)
    client = Dida365Client(
        client_id="your_client_id",  # Optional if in .env
        client_secret="your_client_secret",  # Optional if in .env
        service_type=ServiceType.TICKTICK,  # or DIDA365
        redirect_uri="http://localhost:8080/callback",  # Optional
        save_to_env=True  # Automatically save credentials and tokens to .env
    )

    # First-time authentication:
    if not client.auth.token:
        # This will start a local server at the redirect_uri
        # and open your browser for authorization
        await client.authenticate()
        # Token will be automatically saved to .env if save_to_env=True

    # Create a project
    project = await client.create_project(
        ProjectCreate(
            name="My Project",
            color="#FF0000"
        )
    )

    # Create a task
    task = await client.create_task(
        TaskCreate(
            project_id=project.id,
            title="My new task",
            content="Task description",
            priority=TaskPriority.HIGH,
            start_date=datetime.now(timezone.utc),
            is_all_day=False,
            time_zone="UTC"
        )
    )
    
    print(f"Created task: {task.title}")

if __name__ == "__main__":
    asyncio.run(main())

CRUD Operations

Projects

from dida365 import ProjectCreate, ProjectUpdate, ViewMode, ProjectKind, Dida365Client

async def manage_projects(client: Dida365Client):
    # Create a project
    project = await client.create_project(
        ProjectCreate(
            name="My Project",
            color="#FF0000",  # Optional: hex color code
            view_mode=ViewMode.KANBAN,  # Optional: LIST, KANBAN, TIMELINE
            kind=ProjectKind.TASK  # Optional: TASK, NOTE
        )
    )

    # Get project we just created
    project = await client.get_project(project_id=project.id)

    # Get project with all tasks and columns
    project_data = await client.get_project_with_data(project_id=project.id)
    print(f"Project {project_data.project.name} has {len(project_data.tasks)} tasks")
    for column in project_data.columns:  # Only present in KANBAN view
        print(f"Column: {column.name}")

    # Update project
    updated_project = await client.update_project(
        ProjectUpdate(
            id=project.id,
            name="Updated Project Name",
            color="#00FF00",
            view_mode=ViewMode.LIST
        )
    )

    # Delete project
    await client.delete_project(project_id=project.id)

    # List all projects
    projects = await client.get_projects()
    for project in projects:
        print(f"Project: {project.name} ({project.id})")

Tasks

from datetime import datetime, timezone
from dida365 import TaskCreate, TaskUpdate, TaskPriority, Dida365Client, Project

async def manage_tasks(client: Dida365Client, project: Project):
    # Create a task
    task = await client.create_task(
        TaskCreate(
            project_id=project.id,  # Required: tasks must belong to a project
            title="Complete documentation",
            content="Add CRUD examples",
            priority=TaskPriority.HIGH,  # Enum: NONE, LOW, MEDIUM, HIGH
            start_date=datetime.now(timezone.utc),
            due_date=datetime.now(timezone.utc),
            is_all_day=True,
            time_zone="UTC"
        )
    )

    # Read a task
    task = await client.get_task(project_id=project.id, task_id=task.id)

    # Update a task
    updated_task = await client.update_task(
        TaskUpdate(
            id=task.id,
            project_id=task.project_id,  # Both id and project_id are required
            title="Updated title",
            content="Added more details",
            priority=TaskPriority.MEDIUM
        )
    )

    # Complete a task
    await client.complete_task(project_id=task.project_id, task_id=task.id)
    
    # Delete a task
    await client.delete_task(project_id=task.project_id, task_id=task.id)

State Management

The client maintains an internal state of tasks and projects:

# Access cached state
tasks = client.state["tasks"]
projects = client.state["projects"]
tags = client.state["tags"]

# State is automatically updated when you:
# 1. Create new items
# 2. Update existing items
# 3. Delete items

Configuration

The client can be configured through:

  1. Environment variables
  2. Constructor parameters
  3. pyproject.toml settings

Environment Variables

Create a .env file:

# Required credentials
DIDA365_CLIENT_ID=your_client_id
DIDA365_CLIENT_SECRET=your_client_secret

# Optional configurations
DIDA365_SERVICE_TYPE=ticktick     # or dida365 
DIDA365_ACCESS_TOKEN=your_token   # Will be saved automatically after auth
DIDA365_BASE_URL=custom_url      # Optional: custom API endpoint
DIDA365_LOG_LEVEL=INFO          # Optional: DEBUG, INFO, WARNING, ERROR

You can also use a custom .env file location:

from dotenv import load_dotenv

load_dotenv("/path/to/your/.env")
client = Dida365Client()  # Will load from the specified .env file

Request Timeouts

Configure request timeouts in pyproject.toml:

[tool.dida365.request_timeout]
connect = 10.0  # Connection timeout
read = 30.0     # Read timeout
write = 30.0    # Write timeout
pool = 5.0      # Pool timeout

Logging Configuration

[tool.dida365]
log_level = "INFO"
log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
log_date_format = "%Y-%m-%d %H:%M:%S"
log_file = ""  # Set to a path to enable file logging
debug = false

Error Handling

The library provides detailed error handling:

from dida365.exceptions import (
    ApiError,
    AuthenticationError,
    NotFoundError,
    RateLimitError,
    ValidationError
)

try:
    task = await client.get_task("project_id", "task_id")
except NotFoundError:
    print("Task not found")
except AuthenticationError:
    print("Authentication failed - token may have expired")
except RateLimitError as e:
    print(f"Rate limit exceeded. Retry after {e.retry_after} seconds")
except ValidationError as e:
    print(f"Invalid data: {e}")
except ApiError as e:
    print(f"API error: {e.status_code} - {e.message}")

Author

Carter Yifeng Cheng (@cyfine)

License

This project is licensed under the MIT License - see the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dida365-0.1.9.tar.gz (30.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dida365-0.1.9-py3-none-any.whl (21.1 kB view details)

Uploaded Python 3

File details

Details for the file dida365-0.1.9.tar.gz.

File metadata

  • Download URL: dida365-0.1.9.tar.gz
  • Upload date:
  • Size: 30.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.9.13

File hashes

Hashes for dida365-0.1.9.tar.gz
Algorithm Hash digest
SHA256 d537dad627bfdf627586cd6a5d02eb832ca500000e4114bb8d0f730c05b2f037
MD5 f80c749ce66dd58d22b296e7805eef19
BLAKE2b-256 9e6bea2dd0a5ba7def23197494fd2be5a49e630441263c61d8eca64509f16673

See more details on using hashes here.

File details

Details for the file dida365-0.1.9-py3-none-any.whl.

File metadata

  • Download URL: dida365-0.1.9-py3-none-any.whl
  • Upload date:
  • Size: 21.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.9.13

File hashes

Hashes for dida365-0.1.9-py3-none-any.whl
Algorithm Hash digest
SHA256 add1848e8bc4177fd693664ad8ab811fb0a7d6def1927874ae1d9ee314f53919
MD5 921d063bae74f78883dc4a5a1c920209
BLAKE2b-256 f734003e4fcd167fdd44c2f319dcdc5ec3e3dd67506a7b5ca6f0ae780539d3f8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page