Skip to main content

Python SDK for Dooray.com with Socket Mode support

Project description

Dooray SDK - Python

A comprehensive Python SDK for building agents on Dooray.com. This document covers the core classes, decorators, and usage patterns.


Installation

Install via pip

# Currently in alpha (requires --pre flag)
pip install --pre dooray-sdk

# Or specify version explicitly
pip install dooray-sdk==0.0.1a2

Install with aiohttp (Recommended)

pip install --pre "dooray-sdk[aiohttp]"

# Or specify version explicitly
pip install "dooray-sdk[aiohttp]==0.0.1a2"

Using Virtual Environment (Recommended)

# Create virtual environment
python3 -m venv .venv

# Activate (Linux/macOS)
source .venv/bin/activate

# Activate (Windows)
.venv\Scripts\activate

# Install
pip install --pre "dooray-sdk[aiohttp]"

Dependencies

Package Version Description
aiohttp >= 3.8.0 Async HTTP/WebSocket client
requests >= 2.28.0 Sync HTTP client

Quick Start (Templates)

Use the CLI tool to quickly bootstrap your project.

Install with Templates

pip install --pre "dooray-sdk[templates]"

Usage

# List available templates
dooray-sdk list

# Create an echo agent project
dooray-sdk init echo-agent

# Create with default values (no prompts)
dooray-sdk init echo-agent --no-input

# Create in a specific directory
dooray-sdk init echo-agent -o ./projects

Available Templates

Template Description
echo-agent A simple agent that echoes messages back

Environment Variables

The SDK uses the following environment variables:

Variable Required Description Example
DOORAY_AGENT_TOKEN Yes Agent authentication token your_agent_token
DOORAY_DOMAIN Yes Dooray domain company.dooray.com

Classes

Agent

The core class for building Dooray agents. Supports message handling, auto-replies, and event processing.

class Agent:
    def __init__(
        self,
        token: str = None,
        domain: str = None,
        services: List[str] = None,
        **kwargs
    )

Constructor Parameters

Parameter Type Default Description
token str | None None Agent token (falls back to DOORAY_AGENT_TOKEN env var)
domain str | None None Dooray domain (falls back to DOORAY_DOMAIN env var)
services List[str] | None ["messenger"] List of services to connect

Properties

Property Type Description
token str Agent authentication token
domain str Dooray domain
services List[str] Connected service list
base_url str API base URL
is_connected bool Connection status

Methods

Method Description
run() Start the agent (blocking)
on(event_type, action, service) Event handler decorator
add_handler(event_type, handler, action, service) Register handler (non-decorator)
reply_to(trigger, response) Simple response mapping
send_message(channel, text) Send a message

Example - Basic Usage

from dooray_sdk import Agent

agent = Agent()

@agent.messenger
def handle_message(req):
    """Handle messenger messages."""
    if req.text:
        req.reply(f"Received: {req.text}")

agent.run()

Example - Multi-Service

from dooray_sdk import Agent

agent = Agent(services=["messenger", "task", "wiki"])

@agent.messenger
def handle_messenger(req):
    req.reply("This is a messenger message")

@agent.task
def handle_task(req):
    print(f"Task event: {req.action}")

@agent.wiki
def handle_wiki(req):
    print(f"Wiki event: {req.action}")

agent.run()

SocketModeRequest

Represents an incoming request received via WebSocket.

@dataclass
class SocketModeRequest:
    envelope_id: str
    type: str
    payload: Dict[str, Any]
    service: str = ""
    action: str = ""
    entity: EntityWrapper = None
    actor: ActorWrapper = None
    action_data: ActionWrapper = None

Properties

Property Type Description
envelope_id str Unique message ID
type str Event type (message, task, page, etc.)
payload Dict[str, Any] Raw payload data
service str Service name (messenger, task, wiki)
action str Action type (create, update, delete)
entity EntityWrapper Entity information wrapper
actor ActorWrapper Actor information wrapper
action_data ActionWrapper Action data wrapper

Convenience Properties

Property Type Description
is_message bool Whether this is a message type
text str | None Message text
channel str | None Channel ID
data Dict[str, Any] Raw data (alias for payload)

Methods

Method Description
reply(text) Send sync/async response
is_type(types) Check type
is_action(actions) Check action
is_service(services) Check service
to_dict() Convert to dictionary
from_dict(data, default_service) Create from dictionary (class method)

Example - Request Handling

@agent.messenger
def handle(req):
    # Check message type
    if req.is_message:
        print(f"Text: {req.text}")
        print(f"Channel: {req.channel}")

    # Check action
    if req.is_action("create"):
        print("This is a new message")

    # Check service
    if req.is_service("messenger"):
        req.reply("Responding from messenger")

WebClient

Synchronous REST API client.

class WebClient:
    def __init__(
        self,
        token: str,
        base_url: str = "https://api.dooray.com/",
        timeout: int = 30
    )

Methods

Method Return Type Description
send_message(channel, text, **kwargs) Dict Send a message
get_member(member_id) Dict Get organization member info

AsyncWebClient

Asynchronous REST API client.

class AsyncWebClient:
    def __init__(
        self,
        token: str,
        base_url: str = "https://api.dooray.com/"
    )

Methods

Method Return Type Description
send_message(channel, text, **kwargs) Dict Send a message
get_member(member_id) Dict Get organization member info

Decorators

Service Decorators

Register event handlers for each service.

@agent.messenger   # All messenger service events
@agent.task        # All task service events
@agent.wiki        # All wiki service events

@agent.on() Decorator

Unified event handler decorator.

def on(
    event_type: Union[str, List[str]] = "all",
    action: Union[str, List[str]] = None,
    service: Union[str, List[str]] = None
) -> Callable

Parameters

Parameter Type Default Description
event_type str | List[str] "all" Event type filter
action str | List[str] | None None Action filter
service str | List[str] | None None Service filter

Examples

# Handle only message events
@agent.on("message")
def handle_message(req):
    pass

# Handle specific action
@agent.on("message", action="create")
def handle_new_message(req):
    pass

# Handle multiple types
@agent.on(["task", "page"])
def handle_task_or_page(req):
    pass

# Combine service and action
@agent.on(service="wiki", action=["create", "update"])
def handle_wiki_changes(req):
    pass

Request Data Structure

The SDK provides wrapper types that enable dot notation access to dictionary data.

Type Structure Overview

SocketModeRequest
├── entity: EntityWrapper      # Entity info (task, page, etc.)
│   ├── type: str              # Entity type
│   └── data: DataWrapper      # Entity data
├── actor: ActorWrapper        # Action performer info
│   ├── type: str              # Actor type
│   └── data: DataWrapper      # Actor data
└── action_data: ActionWrapper # Action details
    ├── type: str              # Action type
    └── data: DataWrapper      # Action data

DataWrapper

The base class for all wrapper types. Enables dot notation access to dictionary data.

class DataWrapper:
    def __getattr__(self, name: str) -> Any   # Dot notation access
    def __getitem__(self, key: str) -> Any    # Dictionary access
    def get(self, key: str, default=None)     # Safe access
    def to_dict(self) -> Dict[str, Any]       # Return original dictionary

Usage:

# Dot notation access
data.id
data.subject
data.nested.value

# Dictionary access
data["id"]
data.get("subject", "default")

# Get original dictionary
data.to_dict()

EntityWrapper

Wraps entity information (tasks, pages, etc.).

Property Type Description
type str Entity type (e.g., "task", "page")
data DataWrapper Entity detail data

Usage:

@agent.on("task")
def handle_task(req):
    print(req.entity.type)           # "task"
    print(req.entity.data.id)        # Task ID
    print(req.entity.data.subject)   # Task subject

Message Data

For messenger messages, entity.type is "message".

Property Type Description
id str Message ID
channelId str Channel ID
senderId str Sender member ID
text str Message text
sentAt int Sent time (timestamp ms)
seq int Message sequence number
directMemberId str DM recipient member ID
parentChannelId str Parent channel ID (threads)
@agent.messenger
def handle(req):
    print(req.entity.message.text)
    print(req.entity.message.channelId)
    print(req.entity.message.senderId)
    print(req.entity.message.sentAt)

ActorWrapper

Wraps information about the user who performed the action.

Property Type Description
type str Actor type (e.g., "organizationMember")
data DataWrapper Actor detail data
organizationMember LazyMemberData | None Organization member info (supports lazy loading)

Usage:

@agent.on("message")
def handle(req):
    if req.actor:
        print(req.actor.type)         # "organizationMember"

        # Access via organizationMember (recommended)
        member = req.actor.organizationMember
        print(member.id)              # Member ID (local data)
        print(member.name)            # Member name (lazy loading)

organizationMember Fields

Property Type Description
id str Member ID
name str Member name
externalEmailAddress str Email address
nickname str Nickname
englishName str English name
nativeName str Native name
userCode str User code
locale str Locale
timezoneName str Timezone

Lazy Loading

organizationMember returns data included in the WebSocket message immediately, and fetches missing fields via API when accessed.

@agent.messenger
async def handle(req):
    member = req.actor.organizationMember

    # Local data (no API call)
    member_id = member.id

    # Async access - API call when needed
    name = await member.name
    email = await member.externalEmailAddress

    # Sync access (print, comparison, etc.)
    print(member.name)
    if member.name == "John Doe":
        await req.reply("Hello!")

    # Get full data
    full_data = await member

ActionWrapper

Wraps action detail information.

Property Type Description
type str Action type (e.g., "create", "update")
data DataWrapper Action detail data

Usage Examples

Echo Agent

from dooray_sdk import Agent

agent = Agent()

@agent.messenger
def echo(req):
    if req.text:
        req.reply(f"Echo: {req.text}")

agent.run()

Auto-Reply Agent

from dooray_sdk import Agent

agent = Agent()

# Simple mapping
agent.reply_to("ping", "pong")
agent.reply_to("hello", "Hello there!")

# Custom handler
@agent.messenger
def handle(req):
    text = req.text or ""
    if text.startswith("help"):
        req.reply("Available commands: ping, hello, help")

agent.run()

Multi-Service Agent

from dooray_sdk import Agent

agent = Agent(services=["messenger", "task", "wiki"])

@agent.messenger
def on_message(req):
    print(f"[Messenger] {req.text}")
    req.reply("Message received!")

@agent.task
def on_task(req):
    print(f"[Task] {req.action}: {req.entity.data.subject}")

@agent.wiki
def on_wiki(req):
    print(f"[Wiki] {req.action}: {req.entity.data.title}")

@agent.on("all")
def on_any(req):
    print(f"[All] {req.service}/{req.type}/{req.action}")

agent.run()

Error Handling

Common Exceptions

Exception Cause Solution
ValueError Environment variables not set Set DOORAY_AGENT_TOKEN and DOORAY_DOMAIN
ConnectionError WebSocket connection failed Check network and token
RuntimeError Calling reply before agent starts Use after agent.run()

Exception Handling Example

from dooray_sdk import Agent

try:
    agent = Agent()
except ValueError as e:
    print(f"Configuration error: {e}")
    print("Please set DOORAY_AGENT_TOKEN and DOORAY_DOMAIN")
    exit(1)

@agent.messenger
def handle(req):
    try:
        # Business logic
        process_message(req)
    except Exception as e:
        # Log error (avoid exposing errors to users)
        logger.error(f"Processing error: {e}")

agent.run()

License

MIT License

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

dooray_sdk-0.0.1a4.tar.gz (77.3 kB view details)

Uploaded Source

Built Distribution

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

dooray_sdk-0.0.1a4-py3-none-any.whl (66.2 kB view details)

Uploaded Python 3

File details

Details for the file dooray_sdk-0.0.1a4.tar.gz.

File metadata

  • Download URL: dooray_sdk-0.0.1a4.tar.gz
  • Upload date:
  • Size: 77.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for dooray_sdk-0.0.1a4.tar.gz
Algorithm Hash digest
SHA256 02dfa36a23eaa430a29d1d90e8f115e5d3212ed13e6f58d73ed6e32aba89ebf6
MD5 02caa803aec5ab0dfcad2761416e992f
BLAKE2b-256 0927bb714d20663707340126c64ea288a96af8fd34c163eed3b057067db710a2

See more details on using hashes here.

File details

Details for the file dooray_sdk-0.0.1a4-py3-none-any.whl.

File metadata

  • Download URL: dooray_sdk-0.0.1a4-py3-none-any.whl
  • Upload date:
  • Size: 66.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for dooray_sdk-0.0.1a4-py3-none-any.whl
Algorithm Hash digest
SHA256 d1f1f999b89d2c30f736b64f02a335b48a53778010ddb5984c020637601fc396
MD5 fa7b6cd16d4c52bad5c223c0c66a1c4b
BLAKE2b-256 839c6aba7bfd676fc34dc2d4d63233ca665f011e6add574e3342643254c193cf

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