feat(block) : Todoist rest api blocks (#9369)

- Resolves - #9303 and #9304
- Depends on - https://github.com/Significant-Gravitas/AutoGPT/pull/9319

### Blocks list

Block Name | What It Does | Manually Tested
-- | -- | --
Todoist Create Label | Creates a new label in Todoist | 
Todoist List Labels | Retrieves all personal labels from Todoist | 
Todoist Get Label | Retrieves a specific label by ID | 
Todoist Create Task | Creates a new task in Todoist | 
Todoist Get Tasks | Retrieves active tasks from Todoist | 
Todoist Update Task | Updates an existing task | 
Todoist Close Task | Completes/closes a task | 
Todoist Reopen Task | Reopens a completed task | 
Todoist Delete Task | Permanently deletes a task | 
Todoist List Projects | Retrieves all projects from Todoist | 
Todoist Create Project | Creates a new project in Todoist | 
Todoist Get Project | Retrieves details for a specific project | 
Todoist Update Project | Updates an existing project | 
Todoist Delete Project | Deletes a project and its contents | 
Todoist List Collaborators | Retrieves collaborators on a project | 
Todoist List Sections | Retrieves sections from Todoist | 
Todoist Get Section | Retrieves details for a specific section | 
Todoist Delete Section | Deletes a section and its tasks | 
Todoist Create Comment | Creates a new comment on a task or project | 
Todoist Get Comments | Retrieves all comments for a task or project | 
Todoist Get Comment | Retrieves a specific comment by ID | 
Todoist Update Comment | Updates an existing comment | 
Todoist Delete Comment | Deletes a comment | 

> I’ve only created action blocks in Todoist because webhooks can only
be manually created [we can't do it programatically right now]. I’ve
already emailed Todoist for help, but they haven’t replied yet. Once I
receive a reply, I’ll create a pull request for webhook triggers in
Todoist.
This commit is contained in:
Abhimanyu Yadav
2025-01-30 19:03:29 +05:30
committed by GitHub
parent bd27ce5f26
commit a44c9333d3
21 changed files with 3673 additions and 136 deletions

View File

@@ -82,6 +82,14 @@ TWITTER_CLIENT_SECRET=
LINEAR_CLIENT_ID=
LINEAR_CLIENT_SECRET=
# To obtain Todoist API credentials:
# 1. Create a Todoist account at todoist.com
# 2. Visit the Developer Console: https://developer.todoist.com/appconsole.html
# 3. Click "Create new app"
# 4. Once created, copy your Client ID and Client Secret below
TODOIST_CLIENT_ID=
TODOIST_CLIENT_SECRET=
## ===== OPTIONAL API KEYS ===== ##
# LLM

View File

@@ -0,0 +1,61 @@
from typing import Literal
from pydantic import SecretStr
from backend.data.model import (
CredentialsField,
CredentialsMetaInput,
OAuth2Credentials,
ProviderName,
)
from backend.integrations.oauth.todoist import TodoistOAuthHandler
from backend.util.settings import Secrets
secrets = Secrets()
TODOIST_OAUTH_IS_CONFIGURED = bool(
secrets.todoist_client_id and secrets.todoist_client_secret
)
TodoistCredentials = OAuth2Credentials
TodoistCredentialsInput = CredentialsMetaInput[
Literal[ProviderName.TODOIST], Literal["oauth2"]
]
def TodoistCredentialsField(scopes: list[str]) -> TodoistCredentialsInput:
"""
Creates a Todoist credentials input on a block.
Params:
scopes: The authorization scopes needed for the block to work.
"""
return CredentialsField(
required_scopes=set(TodoistOAuthHandler.DEFAULT_SCOPES + scopes),
description="The Todoist integration requires OAuth2 authentication.",
)
TEST_CREDENTIALS = OAuth2Credentials(
id="01234567-89ab-cdef-0123-456789abcdef",
provider="todoist",
access_token=SecretStr("mock-todoist-access-token"),
refresh_token=None,
access_token_expires_at=None,
scopes=[
"task:add",
"data:read",
"data:read_write",
"data:delete",
"project:delete",
],
title="Mock Todoist OAuth2 Credentials",
username="mock-todoist-username",
refresh_token_expires_at=None,
)
TEST_CREDENTIALS_INPUT = {
"provider": TEST_CREDENTIALS.provider,
"id": TEST_CREDENTIALS.id,
"type": TEST_CREDENTIALS.type,
"title": TEST_CREDENTIALS.title,
}

View File

@@ -0,0 +1,24 @@
from enum import Enum
class Colors(Enum):
berry_red = "berry_red"
red = "red"
orange = "orange"
yellow = "yellow"
olive_green = "olive_green"
lime_green = "lime_green"
green = "green"
mint_green = "mint_green"
teal = "teal"
sky_blue = "sky_blue"
light_blue = "light_blue"
blue = "blue"
grape = "grape"
violet = "violet"
lavender = "lavender"
magenta = "magenta"
salmon = "salmon"
charcoal = "charcoal"
grey = "grey"
taupe = "taupe"

View File

@@ -0,0 +1,439 @@
from typing import Literal, Union
from pydantic import BaseModel
from todoist_api_python.api import TodoistAPI
from typing_extensions import Optional
from backend.blocks.todoist._auth import (
TEST_CREDENTIALS,
TEST_CREDENTIALS_INPUT,
TodoistCredentials,
TodoistCredentialsField,
TodoistCredentialsInput,
)
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
class TaskId(BaseModel):
discriminator: Literal["task"]
task_id: str
class ProjectId(BaseModel):
discriminator: Literal["project"]
project_id: str
class TodoistCreateCommentBlock(Block):
"""Creates a new comment on a Todoist task or project"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
content: str = SchemaField(description="Comment content")
id_type: Union[TaskId, ProjectId] = SchemaField(
discriminator="discriminator",
description="Specify either task_id or project_id to comment on",
default=TaskId(discriminator="task", task_id=""),
advanced=False,
)
attachment: Optional[dict] = SchemaField(
description="Optional file attachment", default=None
)
class Output(BlockSchema):
id: str = SchemaField(description="ID of created comment")
content: str = SchemaField(description="Comment content")
posted_at: str = SchemaField(description="Comment timestamp")
task_id: Optional[str] = SchemaField(
description="Associated task ID", default=None
)
project_id: Optional[str] = SchemaField(
description="Associated project ID", default=None
)
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="1bba7e54-2310-4a31-8e6f-54d5f9ab7459",
description="Creates a new comment on a Todoist task or project",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistCreateCommentBlock.Input,
output_schema=TodoistCreateCommentBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"content": "Test comment",
"id_type": {"discriminator": "task", "task_id": "2995104339"},
},
test_credentials=TEST_CREDENTIALS,
test_output=[
("id", "2992679862"),
("content", "Test comment"),
("posted_at", "2016-09-22T07:00:00.000000Z"),
("task_id", "2995104339"),
("project_id", None),
],
test_mock={
"create_comment": lambda content, credentials, task_id=None, project_id=None, attachment=None: {
"id": "2992679862",
"content": "Test comment",
"posted_at": "2016-09-22T07:00:00.000000Z",
"task_id": "2995104339",
"project_id": None,
}
},
)
@staticmethod
def create_comment(
credentials: TodoistCredentials,
content: str,
task_id: Optional[str] = None,
project_id: Optional[str] = None,
attachment: Optional[dict] = None,
):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
comment = api.add_comment(
content=content,
task_id=task_id,
project_id=project_id,
attachment=attachment,
)
return comment.__dict__
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
task_id = None
project_id = None
if isinstance(input_data.id_type, TaskId):
task_id = input_data.id_type.task_id
else:
project_id = input_data.id_type.project_id
comment_data = self.create_comment(
credentials,
input_data.content,
task_id=task_id,
project_id=project_id,
attachment=input_data.attachment,
)
if comment_data:
yield "id", comment_data["id"]
yield "content", comment_data["content"]
yield "posted_at", comment_data["posted_at"]
yield "task_id", comment_data["task_id"]
yield "project_id", comment_data["project_id"]
except Exception as e:
yield "error", str(e)
class TodoistGetCommentsBlock(Block):
"""Get all comments for a Todoist task or project"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
id_type: Union[TaskId, ProjectId] = SchemaField(
discriminator="discriminator",
description="Specify either task_id or project_id to get comments for",
default=TaskId(discriminator="task", task_id=""),
advanced=False,
)
class Output(BlockSchema):
comments: list = SchemaField(description="List of comments")
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="9972d8ae-ddf2-11ef-a9b8-32d3674e8b7e",
description="Get all comments for a Todoist task or project",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistGetCommentsBlock.Input,
output_schema=TodoistGetCommentsBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"id_type": {"discriminator": "task", "task_id": "2995104339"},
},
test_credentials=TEST_CREDENTIALS,
test_output=[
(
"comments",
[
{
"id": "2992679862",
"content": "Test comment",
"posted_at": "2016-09-22T07:00:00.000000Z",
"task_id": "2995104339",
"project_id": None,
"attachment": None,
}
],
)
],
test_mock={
"get_comments": lambda credentials, task_id=None, project_id=None: [
{
"id": "2992679862",
"content": "Test comment",
"posted_at": "2016-09-22T07:00:00.000000Z",
"task_id": "2995104339",
"project_id": None,
"attachment": None,
}
]
},
)
@staticmethod
def get_comments(
credentials: TodoistCredentials,
task_id: Optional[str] = None,
project_id: Optional[str] = None,
):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
comments = api.get_comments(task_id=task_id, project_id=project_id)
return [comment.__dict__ for comment in comments]
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
task_id = None
project_id = None
if isinstance(input_data.id_type, TaskId):
task_id = input_data.id_type.task_id
else:
project_id = input_data.id_type.project_id
comments = self.get_comments(
credentials, task_id=task_id, project_id=project_id
)
yield "comments", comments
except Exception as e:
yield "error", str(e)
class TodoistGetCommentBlock(Block):
"""Get a single comment from Todoist using comment ID"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
comment_id: str = SchemaField(description="Comment ID to retrieve")
class Output(BlockSchema):
content: str = SchemaField(description="Comment content")
id: str = SchemaField(description="Comment ID")
posted_at: str = SchemaField(description="Comment timestamp")
project_id: Optional[str] = SchemaField(
description="Associated project ID", default=None
)
task_id: Optional[str] = SchemaField(
description="Associated task ID", default=None
)
attachment: Optional[dict] = SchemaField(
description="Optional file attachment", default=None
)
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="a809d264-ddf2-11ef-9764-32d3674e8b7e",
description="Get a single comment from Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistGetCommentBlock.Input,
output_schema=TodoistGetCommentBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"comment_id": "2992679862",
},
test_credentials=TEST_CREDENTIALS,
test_output=[
("content", "Test comment"),
("id", "2992679862"),
("posted_at", "2016-09-22T07:00:00.000000Z"),
("project_id", None),
("task_id", "2995104339"),
("attachment", None),
],
test_mock={
"get_comment": lambda credentials, comment_id: {
"content": "Test comment",
"id": "2992679862",
"posted_at": "2016-09-22T07:00:00.000000Z",
"project_id": None,
"task_id": "2995104339",
"attachment": None,
}
},
)
@staticmethod
def get_comment(credentials: TodoistCredentials, comment_id: str):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
comment = api.get_comment(comment_id=comment_id)
return comment.__dict__
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
comment_data = self.get_comment(
credentials, comment_id=input_data.comment_id
)
if comment_data:
yield "content", comment_data["content"]
yield "id", comment_data["id"]
yield "posted_at", comment_data["posted_at"]
yield "project_id", comment_data["project_id"]
yield "task_id", comment_data["task_id"]
yield "attachment", comment_data["attachment"]
except Exception as e:
yield "error", str(e)
class TodoistUpdateCommentBlock(Block):
"""Updates a Todoist comment"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
comment_id: str = SchemaField(description="Comment ID to update")
content: str = SchemaField(description="New content for the comment")
class Output(BlockSchema):
success: bool = SchemaField(description="Whether the update was successful")
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="b773c520-ddf2-11ef-9f34-32d3674e8b7e",
description="Updates a Todoist comment",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistUpdateCommentBlock.Input,
output_schema=TodoistUpdateCommentBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"comment_id": "2992679862",
"content": "Need one bottle of milk",
},
test_credentials=TEST_CREDENTIALS,
test_output=[("success", True)],
test_mock={"update_comment": lambda credentials, comment_id, content: True},
)
@staticmethod
def update_comment(credentials: TodoistCredentials, comment_id: str, content: str):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
api.update_comment(comment_id=comment_id, content=content)
return True
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
success = self.update_comment(
credentials,
comment_id=input_data.comment_id,
content=input_data.content,
)
yield "success", success
except Exception as e:
yield "error", str(e)
class TodoistDeleteCommentBlock(Block):
"""Deletes a Todoist comment"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
comment_id: str = SchemaField(description="Comment ID to delete")
class Output(BlockSchema):
success: bool = SchemaField(description="Whether the deletion was successful")
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="bda4c020-ddf2-11ef-b114-32d3674e8b7e",
description="Deletes a Todoist comment",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistDeleteCommentBlock.Input,
output_schema=TodoistDeleteCommentBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"comment_id": "2992679862",
},
test_credentials=TEST_CREDENTIALS,
test_output=[("success", True)],
test_mock={"delete_comment": lambda credentials, comment_id: True},
)
@staticmethod
def delete_comment(credentials: TodoistCredentials, comment_id: str):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
success = api.delete_comment(comment_id=comment_id)
return success
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
success = self.delete_comment(credentials, comment_id=input_data.comment_id)
yield "success", success
except Exception as e:
yield "error", str(e)

View File

@@ -0,0 +1,557 @@
from todoist_api_python.api import TodoistAPI
from typing_extensions import Optional
from backend.blocks.todoist._auth import (
TEST_CREDENTIALS,
TEST_CREDENTIALS_INPUT,
TodoistCredentials,
TodoistCredentialsField,
TodoistCredentialsInput,
)
from backend.blocks.todoist._types import Colors
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
class TodoistCreateLabelBlock(Block):
"""Creates a new label in Todoist"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
name: str = SchemaField(description="Name of the label")
order: Optional[int] = SchemaField(description="Label order", default=None)
color: Optional[Colors] = SchemaField(
description="The color of the label icon", default=Colors.charcoal
)
is_favorite: bool = SchemaField(
description="Whether the label is a favorite", default=False
)
class Output(BlockSchema):
id: str = SchemaField(description="ID of the created label")
name: str = SchemaField(description="Name of the label")
color: str = SchemaField(description="Color of the label")
order: int = SchemaField(description="Label order")
is_favorite: bool = SchemaField(description="Favorite status")
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="7288a968-de14-11ef-8997-32d3674e8b7e",
description="Creates a new label in Todoist, It will not work if same name already exists",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistCreateLabelBlock.Input,
output_schema=TodoistCreateLabelBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"name": "Test Label",
"color": Colors.charcoal.value,
"order": 1,
"is_favorite": False,
},
test_credentials=TEST_CREDENTIALS,
test_output=[
("id", "2156154810"),
("name", "Test Label"),
("color", "charcoal"),
("order", 1),
("is_favorite", False),
],
test_mock={
"create_label": lambda *args, **kwargs: {
"id": "2156154810",
"name": "Test Label",
"color": "charcoal",
"order": 1,
"is_favorite": False,
}
},
)
@staticmethod
def create_label(credentials: TodoistCredentials, name: str, **kwargs):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
label = api.add_label(name=name, **kwargs)
return label.__dict__
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
label_args = {
"order": input_data.order,
"color": (
input_data.color.value if input_data.color is not None else None
),
"is_favorite": input_data.is_favorite,
}
label_data = self.create_label(
credentials,
input_data.name,
**{k: v for k, v in label_args.items() if v is not None},
)
if label_data:
yield "id", label_data["id"]
yield "name", label_data["name"]
yield "color", label_data["color"]
yield "order", label_data["order"]
yield "is_favorite", label_data["is_favorite"]
except Exception as e:
yield "error", str(e)
class TodoistListLabelsBlock(Block):
"""Gets all personal labels from Todoist"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
class Output(BlockSchema):
labels: list = SchemaField(description="List of complete label data")
label_ids: list = SchemaField(description="List of label IDs")
label_names: list = SchemaField(description="List of label names")
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="776dd750-de14-11ef-b927-32d3674e8b7e",
description="Gets all personal labels from Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistListLabelsBlock.Input,
output_schema=TodoistListLabelsBlock.Output,
test_input={"credentials": TEST_CREDENTIALS_INPUT},
test_credentials=TEST_CREDENTIALS,
test_output=[
(
"labels",
[
{
"id": "2156154810",
"name": "Test Label",
"color": "charcoal",
"order": 1,
"is_favorite": False,
}
],
),
("label_ids", ["2156154810"]),
("label_names", ["Test Label"]),
],
test_mock={
"get_labels": lambda *args, **kwargs: [
{
"id": "2156154810",
"name": "Test Label",
"color": "charcoal",
"order": 1,
"is_favorite": False,
}
]
},
)
@staticmethod
def get_labels(credentials: TodoistCredentials):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
labels = api.get_labels()
return [label.__dict__ for label in labels]
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
labels = self.get_labels(credentials)
yield "labels", labels
yield "label_ids", [label["id"] for label in labels]
yield "label_names", [label["name"] for label in labels]
except Exception as e:
yield "error", str(e)
class TodoistGetLabelBlock(Block):
"""Gets a personal label from Todoist by ID"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
label_id: str = SchemaField(description="ID of the label to retrieve")
class Output(BlockSchema):
id: str = SchemaField(description="ID of the label")
name: str = SchemaField(description="Name of the label")
color: str = SchemaField(description="Color of the label")
order: int = SchemaField(description="Label order")
is_favorite: bool = SchemaField(description="Favorite status")
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="7f236514-de14-11ef-bd7a-32d3674e8b7e",
description="Gets a personal label from Todoist by ID",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistGetLabelBlock.Input,
output_schema=TodoistGetLabelBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"label_id": "2156154810",
},
test_credentials=TEST_CREDENTIALS,
test_output=[
("id", "2156154810"),
("name", "Test Label"),
("color", "charcoal"),
("order", 1),
("is_favorite", False),
],
test_mock={
"get_label": lambda *args, **kwargs: {
"id": "2156154810",
"name": "Test Label",
"color": "charcoal",
"order": 1,
"is_favorite": False,
}
},
)
@staticmethod
def get_label(credentials: TodoistCredentials, label_id: str):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
label = api.get_label(label_id=label_id)
return label.__dict__
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
label_data = self.get_label(credentials, input_data.label_id)
if label_data:
yield "id", label_data["id"]
yield "name", label_data["name"]
yield "color", label_data["color"]
yield "order", label_data["order"]
yield "is_favorite", label_data["is_favorite"]
except Exception as e:
yield "error", str(e)
class TodoistUpdateLabelBlock(Block):
"""Updates a personal label in Todoist using ID"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
label_id: str = SchemaField(description="ID of the label to update")
name: Optional[str] = SchemaField(
description="New name of the label", default=None
)
order: Optional[int] = SchemaField(description="Label order", default=None)
color: Optional[Colors] = SchemaField(
description="The color of the label icon", default=None
)
is_favorite: bool = SchemaField(
description="Whether the label is a favorite (true/false)", default=False
)
class Output(BlockSchema):
success: bool = SchemaField(description="Whether the update was successful")
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="8755614c-de14-11ef-9b56-32d3674e8b7e",
description="Updates a personal label in Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistUpdateLabelBlock.Input,
output_schema=TodoistUpdateLabelBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"label_id": "2156154810",
"name": "Updated Label",
"color": Colors.charcoal.value,
"order": 2,
"is_favorite": True,
},
test_credentials=TEST_CREDENTIALS,
test_output=[("success", True)],
test_mock={"update_label": lambda *args, **kwargs: True},
)
@staticmethod
def update_label(credentials: TodoistCredentials, label_id: str, **kwargs):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
api.update_label(label_id=label_id, **kwargs)
return True
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
label_args = {}
if input_data.name is not None:
label_args["name"] = input_data.name
if input_data.order is not None:
label_args["order"] = input_data.order
if input_data.color is not None:
label_args["color"] = input_data.color.value
if input_data.is_favorite is not None:
label_args["is_favorite"] = input_data.is_favorite
success = self.update_label(
credentials,
input_data.label_id,
**{k: v for k, v in label_args.items() if v is not None},
)
yield "success", success
except Exception as e:
yield "error", str(e)
class TodoistDeleteLabelBlock(Block):
"""Deletes a personal label in Todoist"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
label_id: str = SchemaField(description="ID of the label to delete")
class Output(BlockSchema):
success: bool = SchemaField(description="Whether the deletion was successful")
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="901b8f86-de14-11ef-98b8-32d3674e8b7e",
description="Deletes a personal label in Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistDeleteLabelBlock.Input,
output_schema=TodoistDeleteLabelBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"label_id": "2156154810",
},
test_credentials=TEST_CREDENTIALS,
test_output=[("success", True)],
test_mock={"delete_label": lambda *args, **kwargs: True},
)
@staticmethod
def delete_label(credentials: TodoistCredentials, label_id: str):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
success = api.delete_label(label_id=label_id)
return success
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
success = self.delete_label(credentials, input_data.label_id)
yield "success", success
except Exception as e:
yield "error", str(e)
class TodoistGetSharedLabelsBlock(Block):
"""Gets all shared labels from Todoist"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
class Output(BlockSchema):
labels: list = SchemaField(description="List of shared label names")
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="55fba510-de15-11ef-aed2-32d3674e8b7e",
description="Gets all shared labels from Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistGetSharedLabelsBlock.Input,
output_schema=TodoistGetSharedLabelsBlock.Output,
test_input={"credentials": TEST_CREDENTIALS_INPUT},
test_credentials=TEST_CREDENTIALS,
test_output=[("labels", ["Label1", "Label2", "Label3"])],
test_mock={
"get_shared_labels": lambda *args, **kwargs: [
"Label1",
"Label2",
"Label3",
]
},
)
@staticmethod
def get_shared_labels(credentials: TodoistCredentials):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
labels = api.get_shared_labels()
return labels
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
labels = self.get_shared_labels(credentials)
yield "labels", labels
except Exception as e:
yield "error", str(e)
class TodoistRenameSharedLabelsBlock(Block):
"""Renames all instances of a shared label"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
name: str = SchemaField(description="The name of the existing label to rename")
new_name: str = SchemaField(description="The new name for the label")
class Output(BlockSchema):
success: bool = SchemaField(description="Whether the rename was successful")
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="9d63ad9a-de14-11ef-ab3f-32d3674e8b7e",
description="Renames all instances of a shared label",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistRenameSharedLabelsBlock.Input,
output_schema=TodoistRenameSharedLabelsBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"name": "OldLabel",
"new_name": "NewLabel",
},
test_credentials=TEST_CREDENTIALS,
test_output=[("success", True)],
test_mock={"rename_shared_labels": lambda *args, **kwargs: True},
)
@staticmethod
def rename_shared_labels(credentials: TodoistCredentials, name: str, new_name: str):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
success = api.rename_shared_label(name=name, new_name=new_name)
return success
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
success = self.rename_shared_labels(
credentials, input_data.name, input_data.new_name
)
yield "success", success
except Exception as e:
yield "error", str(e)
class TodoistRemoveSharedLabelsBlock(Block):
"""Removes all instances of a shared label"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
name: str = SchemaField(description="The name of the label to remove")
class Output(BlockSchema):
success: bool = SchemaField(description="Whether the removal was successful")
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="a6c5cbde-de14-11ef-8863-32d3674e8b7e",
description="Removes all instances of a shared label",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistRemoveSharedLabelsBlock.Input,
output_schema=TodoistRemoveSharedLabelsBlock.Output,
test_input={"credentials": TEST_CREDENTIALS_INPUT, "name": "LabelToRemove"},
test_credentials=TEST_CREDENTIALS,
test_output=[("success", True)],
test_mock={"remove_shared_label": lambda *args, **kwargs: True},
)
@staticmethod
def remove_shared_label(credentials: TodoistCredentials, name: str):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
success = api.remove_shared_label(name=name)
return success
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
success = self.remove_shared_label(credentials, input_data.name)
yield "success", success
except Exception as e:
yield "error", str(e)

View File

@@ -0,0 +1,566 @@
from todoist_api_python.api import TodoistAPI
from typing_extensions import Optional
from backend.blocks.todoist._auth import (
TEST_CREDENTIALS,
TEST_CREDENTIALS_INPUT,
TodoistCredentials,
TodoistCredentialsField,
TodoistCredentialsInput,
)
from backend.blocks.todoist._types import Colors
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
class TodoistListProjectsBlock(Block):
"""Gets all projects for a Todoist user"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
class Output(BlockSchema):
names_list: list[str] = SchemaField(description="List of project names")
ids_list: list[str] = SchemaField(description="List of project IDs")
url_list: list[str] = SchemaField(description="List of project URLs")
complete_data: list[dict] = SchemaField(
description="Complete project data including all fields"
)
error: str = SchemaField(description="Error message if request failed")
def __init__(self):
super().__init__(
id="5f3e1d5b-6bc5-40e3-97ee-1318b3f38813",
description="Gets all projects and their details from Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistListProjectsBlock.Input,
output_schema=TodoistListProjectsBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
},
test_credentials=TEST_CREDENTIALS,
test_output=[
("names_list", ["Inbox"]),
("ids_list", ["220474322"]),
("url_list", ["https://todoist.com/showProject?id=220474322"]),
(
"complete_data",
[
{
"id": "220474322",
"name": "Inbox",
"url": "https://todoist.com/showProject?id=220474322",
}
],
),
],
test_mock={
"get_project_lists": lambda *args, **kwargs: (
["Inbox"],
["220474322"],
["https://todoist.com/showProject?id=220474322"],
[
{
"id": "220474322",
"name": "Inbox",
"url": "https://todoist.com/showProject?id=220474322",
}
],
None,
)
},
)
@staticmethod
def get_project_lists(credentials: TodoistCredentials):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
projects = api.get_projects()
names = []
ids = []
urls = []
complete_data = []
for project in projects:
names.append(project.name)
ids.append(project.id)
urls.append(project.url)
complete_data.append(project.__dict__)
return names, ids, urls, complete_data, None
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
names, ids, urls, data, error = self.get_project_lists(credentials)
if names:
yield "names_list", names
if ids:
yield "ids_list", ids
if urls:
yield "url_list", urls
if data:
yield "complete_data", data
except Exception as e:
yield "error", str(e)
class TodoistCreateProjectBlock(Block):
"""Creates a new project in Todoist"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
name: str = SchemaField(description="Name of the project", advanced=False)
parent_id: Optional[str] = SchemaField(
description="Parent project ID", default=None, advanced=True
)
color: Optional[Colors] = SchemaField(
description="Color of the project icon",
default=Colors.charcoal,
advanced=True,
)
is_favorite: bool = SchemaField(
description="Whether the project is a favorite",
default=False,
advanced=True,
)
view_style: Optional[str] = SchemaField(
description="Display style (list or board)", default=None, advanced=True
)
class Output(BlockSchema):
success: bool = SchemaField(description="Whether the creation was successful")
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="ade60136-de14-11ef-b5e5-32d3674e8b7e",
description="Creates a new project in Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistCreateProjectBlock.Input,
output_schema=TodoistCreateProjectBlock.Output,
test_input={"credentials": TEST_CREDENTIALS_INPUT, "name": "Test Project"},
test_credentials=TEST_CREDENTIALS,
test_output=[("success", True)],
test_mock={"create_project": lambda *args, **kwargs: (True)},
)
@staticmethod
def create_project(
credentials: TodoistCredentials,
name: str,
parent_id: Optional[str],
color: Optional[Colors],
is_favorite: bool,
view_style: Optional[str],
):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
params = {"name": name, "is_favorite": is_favorite}
if parent_id is not None:
params["parent_id"] = parent_id
if color is not None:
params["color"] = color.value
if view_style is not None:
params["view_style"] = view_style
api.add_project(**params)
return True
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
success = self.create_project(
credentials=credentials,
name=input_data.name,
parent_id=input_data.parent_id,
color=input_data.color,
is_favorite=input_data.is_favorite,
view_style=input_data.view_style,
)
yield "success", success
except Exception as e:
yield "error", str(e)
class TodoistGetProjectBlock(Block):
"""Gets details for a specific Todoist project"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
project_id: str = SchemaField(
description="ID of the project to get details for", advanced=False
)
class Output(BlockSchema):
project_id: str = SchemaField(description="ID of project")
project_name: str = SchemaField(description="Name of project")
project_url: str = SchemaField(description="URL of project")
complete_data: dict = SchemaField(
description="Complete project data including all fields"
)
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="b435b5ea-de14-11ef-8b51-32d3674e8b7e",
description="Gets details for a specific Todoist project",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistGetProjectBlock.Input,
output_schema=TodoistGetProjectBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"project_id": "2203306141",
},
test_credentials=TEST_CREDENTIALS,
test_output=[
("project_id", "2203306141"),
("project_name", "Shopping List"),
("project_url", "https://todoist.com/showProject?id=2203306141"),
(
"complete_data",
{
"id": "2203306141",
"name": "Shopping List",
"url": "https://todoist.com/showProject?id=2203306141",
},
),
],
test_mock={
"get_project": lambda *args, **kwargs: (
"2203306141",
"Shopping List",
"https://todoist.com/showProject?id=2203306141",
{
"id": "2203306141",
"name": "Shopping List",
"url": "https://todoist.com/showProject?id=2203306141",
},
)
},
)
@staticmethod
def get_project(credentials: TodoistCredentials, project_id: str):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
project = api.get_project(project_id=project_id)
return project.id, project.name, project.url, project.__dict__
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
project_id, project_name, project_url, data = self.get_project(
credentials=credentials, project_id=input_data.project_id
)
if project_id:
yield "project_id", project_id
if project_name:
yield "project_name", project_name
if project_url:
yield "project_url", project_url
if data:
yield "complete_data", data
except Exception as e:
yield "error", str(e)
class TodoistUpdateProjectBlock(Block):
"""Updates an existing project in Todoist"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
project_id: str = SchemaField(
description="ID of project to update", advanced=False
)
name: Optional[str] = SchemaField(
description="New name for the project", default=None, advanced=False
)
color: Optional[Colors] = SchemaField(
description="New color for the project icon", default=None, advanced=True
)
is_favorite: Optional[bool] = SchemaField(
description="Whether the project should be a favorite",
default=None,
advanced=True,
)
view_style: Optional[str] = SchemaField(
description="Display style (list or board)", default=None, advanced=True
)
class Output(BlockSchema):
success: bool = SchemaField(description="Whether the update was successful")
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="ba41a20a-de14-11ef-91d7-32d3674e8b7e",
description="Updates an existing project in Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistUpdateProjectBlock.Input,
output_schema=TodoistUpdateProjectBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"project_id": "2203306141",
"name": "Things To Buy",
},
test_credentials=TEST_CREDENTIALS,
test_output=[("success", True)],
test_mock={"update_project": lambda *args, **kwargs: (True)},
)
@staticmethod
def update_project(
credentials: TodoistCredentials,
project_id: str,
name: Optional[str],
color: Optional[Colors],
is_favorite: Optional[bool],
view_style: Optional[str],
):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
params = {}
if name is not None:
params["name"] = name
if color is not None:
params["color"] = color.value
if is_favorite is not None:
params["is_favorite"] = is_favorite
if view_style is not None:
params["view_style"] = view_style
api.update_project(project_id=project_id, **params)
return True
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
success = self.update_project(
credentials=credentials,
project_id=input_data.project_id,
name=input_data.name,
color=input_data.color,
is_favorite=input_data.is_favorite,
view_style=input_data.view_style,
)
yield "success", success
except Exception as e:
yield "error", str(e)
class TodoistDeleteProjectBlock(Block):
"""Deletes a project and all of its sections and tasks"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
project_id: str = SchemaField(
description="ID of project to delete", advanced=False
)
class Output(BlockSchema):
success: bool = SchemaField(description="Whether the deletion was successful")
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="c2893acc-de14-11ef-a113-32d3674e8b7e",
description="Deletes a Todoist project and all its contents",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistDeleteProjectBlock.Input,
output_schema=TodoistDeleteProjectBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"project_id": "2203306141",
},
test_credentials=TEST_CREDENTIALS,
test_output=[("success", True)],
test_mock={"delete_project": lambda *args, **kwargs: (True)},
)
@staticmethod
def delete_project(credentials: TodoistCredentials, project_id: str):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
success = api.delete_project(project_id=project_id)
return success
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
success = self.delete_project(
credentials=credentials, project_id=input_data.project_id
)
yield "success", success
except Exception as e:
yield "error", str(e)
class TodoistListCollaboratorsBlock(Block):
"""Gets all collaborators for a Todoist project"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
project_id: str = SchemaField(
description="ID of the project to get collaborators for", advanced=False
)
class Output(BlockSchema):
collaborator_ids: list[str] = SchemaField(
description="List of collaborator IDs"
)
collaborator_names: list[str] = SchemaField(
description="List of collaborator names"
)
collaborator_emails: list[str] = SchemaField(
description="List of collaborator email addresses"
)
complete_data: list[dict] = SchemaField(
description="Complete collaborator data including all fields"
)
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="c99c804e-de14-11ef-9f47-32d3674e8b7e",
description="Gets all collaborators for a specific Todoist project",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistListCollaboratorsBlock.Input,
output_schema=TodoistListCollaboratorsBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"project_id": "2203306141",
},
test_credentials=TEST_CREDENTIALS,
test_output=[
("collaborator_ids", ["2671362", "2671366"]),
("collaborator_names", ["Alice", "Bob"]),
("collaborator_emails", ["alice@example.com", "bob@example.com"]),
(
"complete_data",
[
{
"id": "2671362",
"name": "Alice",
"email": "alice@example.com",
},
{"id": "2671366", "name": "Bob", "email": "bob@example.com"},
],
),
],
test_mock={
"get_collaborators": lambda *args, **kwargs: (
["2671362", "2671366"],
["Alice", "Bob"],
["alice@example.com", "bob@example.com"],
[
{
"id": "2671362",
"name": "Alice",
"email": "alice@example.com",
},
{"id": "2671366", "name": "Bob", "email": "bob@example.com"},
],
)
},
)
@staticmethod
def get_collaborators(credentials: TodoistCredentials, project_id: str):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
collaborators = api.get_collaborators(project_id=project_id)
ids = []
names = []
emails = []
complete_data = []
for collaborator in collaborators:
ids.append(collaborator.id)
names.append(collaborator.name)
emails.append(collaborator.email)
complete_data.append(collaborator.__dict__)
return ids, names, emails, complete_data
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
ids, names, emails, data = self.get_collaborators(
credentials=credentials, project_id=input_data.project_id
)
if ids:
yield "collaborator_ids", ids
if names:
yield "collaborator_names", names
if emails:
yield "collaborator_emails", emails
if data:
yield "complete_data", data
except Exception as e:
yield "error", str(e)

View File

@@ -0,0 +1,306 @@
from todoist_api_python.api import TodoistAPI
from typing_extensions import Optional
from backend.blocks.todoist._auth import (
TEST_CREDENTIALS,
TEST_CREDENTIALS_INPUT,
TodoistCredentials,
TodoistCredentialsField,
TodoistCredentialsInput,
)
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
class TodoistListSectionsBlock(Block):
"""Gets all sections for a Todoist project"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
project_id: Optional[str] = SchemaField(
description="Optional project ID to filter sections"
)
class Output(BlockSchema):
names_list: list[str] = SchemaField(description="List of section names")
ids_list: list[str] = SchemaField(description="List of section IDs")
complete_data: list[dict] = SchemaField(
description="Complete section data including all fields"
)
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="d6a116d8-de14-11ef-a94c-32d3674e8b7e",
description="Gets all sections and their details from Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistListSectionsBlock.Input,
output_schema=TodoistListSectionsBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"project_id": "2203306141",
},
test_credentials=TEST_CREDENTIALS,
test_output=[
("names_list", ["Groceries"]),
("ids_list", ["7025"]),
(
"complete_data",
[
{
"id": "7025",
"project_id": "2203306141",
"order": 1,
"name": "Groceries",
}
],
),
],
test_mock={
"get_section_lists": lambda *args, **kwargs: (
["Groceries"],
["7025"],
[
{
"id": "7025",
"project_id": "2203306141",
"order": 1,
"name": "Groceries",
}
],
)
},
)
@staticmethod
def get_section_lists(
credentials: TodoistCredentials, project_id: Optional[str] = None
):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
sections = api.get_sections(project_id=project_id)
names = []
ids = []
complete_data = []
for section in sections:
names.append(section.name)
ids.append(section.id)
complete_data.append(section.__dict__)
return names, ids, complete_data
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
names, ids, data = self.get_section_lists(
credentials, input_data.project_id
)
if names:
yield "names_list", names
if ids:
yield "ids_list", ids
if data:
yield "complete_data", data
except Exception as e:
yield "error", str(e)
# Error in official todoist SDK. Will add this block using sync_api
# class TodoistCreateSectionBlock(Block):
# """Creates a new section in a Todoist project"""
# class Input(BlockSchema):
# credentials: TodoistCredentialsInput = TodoistCredentialsField([])
# name: str = SchemaField(description="Section name")
# project_id: str = SchemaField(description="Project ID this section should belong to")
# order: Optional[int] = SchemaField(description="Optional order among other sections", default=None)
# class Output(BlockSchema):
# success: bool = SchemaField(description="Whether section was successfully created")
# error: str = SchemaField(description="Error message if the request failed")
# def __init__(self):
# super().__init__(
# id="e3025cfc-de14-11ef-b9f2-32d3674e8b7e",
# description="Creates a new section in a Todoist project",
# categories={BlockCategory.PRODUCTIVITY},
# input_schema=TodoistCreateSectionBlock.Input,
# output_schema=TodoistCreateSectionBlock.Output,
# test_input={
# "credentials": TEST_CREDENTIALS_INPUT,
# "name": "Groceries",
# "project_id": "2203306141"
# },
# test_credentials=TEST_CREDENTIALS,
# test_output=[
# ("success", True)
# ],
# test_mock={
# "create_section": lambda *args, **kwargs: (
# {"id": "7025", "project_id": "2203306141", "order": 1, "name": "Groceries"},
# )
# },
# )
# @staticmethod
# def create_section(credentials: TodoistCredentials, name: str, project_id: str, order: Optional[int] = None):
# try:
# api = TodoistAPI(credentials.access_token.get_secret_value())
# section = api.add_section(name=name, project_id=project_id, order=order)
# return section.__dict__
# except Exception as e:
# raise e
# def run(
# self,
# input_data: Input,
# *,
# credentials: TodoistCredentials,
# **kwargs,
# ) -> BlockOutput:
# try:
# section_data = self.create_section(
# credentials,
# input_data.name,
# input_data.project_id,
# input_data.order
# )
# if section_data:
# yield "success", True
# except Exception as e:
# yield "error", str(e)
class TodoistGetSectionBlock(Block):
"""Gets a single section from Todoist by ID"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
section_id: str = SchemaField(description="ID of section to fetch")
class Output(BlockSchema):
id: str = SchemaField(description="ID of section")
project_id: str = SchemaField(description="Project ID the section belongs to")
order: int = SchemaField(description="Order of the section")
name: str = SchemaField(description="Name of the section")
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="ea5580e2-de14-11ef-a5d3-32d3674e8b7e",
description="Gets a single section by ID from Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistGetSectionBlock.Input,
output_schema=TodoistGetSectionBlock.Output,
test_input={"credentials": TEST_CREDENTIALS_INPUT, "section_id": "7025"},
test_credentials=TEST_CREDENTIALS,
test_output=[
("id", "7025"),
("project_id", "2203306141"),
("order", 1),
("name", "Groceries"),
],
test_mock={
"get_section": lambda *args, **kwargs: {
"id": "7025",
"project_id": "2203306141",
"order": 1,
"name": "Groceries",
}
},
)
@staticmethod
def get_section(credentials: TodoistCredentials, section_id: str):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
section = api.get_section(section_id=section_id)
return section.__dict__
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
section_data = self.get_section(credentials, input_data.section_id)
if section_data:
yield "id", section_data["id"]
yield "project_id", section_data["project_id"]
yield "order", section_data["order"]
yield "name", section_data["name"]
except Exception as e:
yield "error", str(e)
class TodoistDeleteSectionBlock(Block):
"""Deletes a section and all its tasks from Todoist"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
section_id: str = SchemaField(description="ID of section to delete")
class Output(BlockSchema):
success: bool = SchemaField(
description="Whether section was successfully deleted"
)
error: str = SchemaField(description="Error message if the request failed")
def __init__(self):
super().__init__(
id="f0e52eee-de14-11ef-9b12-32d3674e8b7e",
description="Deletes a section and all its tasks from Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistDeleteSectionBlock.Input,
output_schema=TodoistDeleteSectionBlock.Output,
test_input={"credentials": TEST_CREDENTIALS_INPUT, "section_id": "7025"},
test_credentials=TEST_CREDENTIALS,
test_output=[("success", True)],
test_mock={"delete_section": lambda *args, **kwargs: (True)},
)
@staticmethod
def delete_section(credentials: TodoistCredentials, section_id: str):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
success = api.delete_section(section_id=section_id)
return success
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
success = self.delete_section(credentials, input_data.section_id)
yield "success", success
except Exception as e:
yield "error", str(e)

View File

@@ -0,0 +1,660 @@
from datetime import datetime
from todoist_api_python.api import TodoistAPI
from todoist_api_python.models import Task
from typing_extensions import Optional
from backend.blocks.todoist._auth import (
TEST_CREDENTIALS,
TEST_CREDENTIALS_INPUT,
TodoistCredentials,
TodoistCredentialsField,
TodoistCredentialsInput,
)
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
class TodoistCreateTaskBlock(Block):
"""Creates a new task in a Todoist project"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
content: str = SchemaField(description="Task content", advanced=False)
description: Optional[str] = SchemaField(
description="Task description", default=None, advanced=False
)
project_id: Optional[str] = SchemaField(
description="Project ID this task should belong to",
default=None,
advanced=False,
)
section_id: Optional[str] = SchemaField(
description="Section ID this task should belong to",
default=None,
advanced=False,
)
parent_id: Optional[str] = SchemaField(
description="Parent task ID", default=None, advanced=True
)
order: Optional[int] = SchemaField(
description="Optional order among other tasks,[Non-zero integer value used by clients to sort tasks under the same parent]",
default=None,
advanced=True,
)
labels: Optional[list[str]] = SchemaField(
description="Task labels", default=None, advanced=True
)
priority: Optional[int] = SchemaField(
description="Task priority from 1 (normal) to 4 (urgent)",
default=None,
advanced=True,
)
due_date: Optional[datetime] = SchemaField(
description="Due date in YYYY-MM-DD format", advanced=True, default=None
)
deadline_date: Optional[datetime] = SchemaField(
description="Specific date in YYYY-MM-DD format relative to user's timezone",
default=None,
advanced=True,
)
assignee_id: Optional[str] = SchemaField(
description="Responsible user ID", default=None, advanced=True
)
duration_unit: Optional[str] = SchemaField(
description="Task duration unit (minute/day)", default=None, advanced=True
)
duration: Optional[int] = SchemaField(
description="Task duration amount, You need to selecct the duration unit first",
depends_on=["duration_unit"],
default=None,
advanced=True,
)
class Output(BlockSchema):
id: str = SchemaField(description="Task ID")
url: str = SchemaField(description="Task URL")
complete_data: dict = SchemaField(
description="Complete task data as dictionary"
)
error: str = SchemaField(description="Error message if request failed")
def __init__(self):
super().__init__(
id="fde4f458-de14-11ef-bf0c-32d3674e8b7e",
description="Creates a new task in a Todoist project",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistCreateTaskBlock.Input,
output_schema=TodoistCreateTaskBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"content": "Buy groceries",
"project_id": "2203306141",
"priority": 4,
},
test_credentials=TEST_CREDENTIALS,
test_output=[
("id", "2995104339"),
("url", "https://todoist.com/showTask?id=2995104339"),
(
"complete_data",
{
"id": "2995104339",
"project_id": "2203306141",
"url": "https://todoist.com/showTask?id=2995104339",
},
),
],
test_mock={
"create_task": lambda *args, **kwargs: (
"2995104339",
"https://todoist.com/showTask?id=2995104339",
{
"id": "2995104339",
"project_id": "2203306141",
"url": "https://todoist.com/showTask?id=2995104339",
},
)
},
)
@staticmethod
def create_task(credentials: TodoistCredentials, content: str, **kwargs):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
task = api.add_task(content=content, **kwargs)
task_dict = Task.to_dict(task)
return task.id, task.url, task_dict
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
due_date = (
input_data.due_date.strftime("%Y-%m-%d")
if input_data.due_date
else None
)
deadline_date = (
input_data.deadline_date.strftime("%Y-%m-%d")
if input_data.deadline_date
else None
)
task_args = {
"description": input_data.description,
"project_id": input_data.project_id,
"section_id": input_data.section_id,
"parent_id": input_data.parent_id,
"order": input_data.order,
"labels": input_data.labels,
"priority": input_data.priority,
"due_date": due_date,
"deadline_date": deadline_date,
"assignee_id": input_data.assignee_id,
"duration": input_data.duration,
"duration_unit": input_data.duration_unit,
}
id, url, complete_data = self.create_task(
credentials,
input_data.content,
**{k: v for k, v in task_args.items() if v is not None},
)
yield "id", id
yield "url", url
yield "complete_data", complete_data
except Exception as e:
yield "error", str(e)
class TodoistGetTasksBlock(Block):
"""Get active tasks from Todoist"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
project_id: Optional[str] = SchemaField(
description="Filter tasks by project ID", default=None, advanced=False
)
section_id: Optional[str] = SchemaField(
description="Filter tasks by section ID", default=None, advanced=True
)
label: Optional[str] = SchemaField(
description="Filter tasks by label name", default=None, advanced=True
)
filter: Optional[str] = SchemaField(
description="Filter by any supported filter, You can see How to use filters or create one of your one here - https://todoist.com/help/articles/introduction-to-filters-V98wIH",
default=None,
advanced=True,
)
lang: Optional[str] = SchemaField(
description="IETF language tag for filter language", default=None
)
ids: Optional[list[str]] = SchemaField(
description="List of task IDs to retrieve", default=None, advanced=False
)
class Output(BlockSchema):
ids: list[str] = SchemaField(description="Task IDs")
urls: list[str] = SchemaField(description="Task URLs")
complete_data: list[dict] = SchemaField(
description="Complete task data as dictionary"
)
error: str = SchemaField(description="Error message if request failed")
def __init__(self):
super().__init__(
id="0b706e86-de15-11ef-a113-32d3674e8b7e",
description="Get active tasks from Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistGetTasksBlock.Input,
output_schema=TodoistGetTasksBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"project_id": "2203306141",
},
test_credentials=TEST_CREDENTIALS,
test_output=[
("ids", ["2995104339"]),
("urls", ["https://todoist.com/showTask?id=2995104339"]),
(
"complete_data",
[
{
"id": "2995104339",
"project_id": "2203306141",
"url": "https://todoist.com/showTask?id=2995104339",
"is_completed": False,
}
],
),
],
test_mock={
"get_tasks": lambda *args, **kwargs: [
{
"id": "2995104339",
"project_id": "2203306141",
"url": "https://todoist.com/showTask?id=2995104339",
"is_completed": False,
}
]
},
)
@staticmethod
def get_tasks(credentials: TodoistCredentials, **kwargs):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
tasks = api.get_tasks(**kwargs)
return [Task.to_dict(task) for task in tasks]
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
task_filters = {
"project_id": input_data.project_id,
"section_id": input_data.section_id,
"label": input_data.label,
"filter": input_data.filter,
"lang": input_data.lang,
"ids": input_data.ids,
}
tasks = self.get_tasks(
credentials, **{k: v for k, v in task_filters.items() if v is not None}
)
yield "ids", [task["id"] for task in tasks]
yield "urls", [task["url"] for task in tasks]
yield "complete_data", tasks
except Exception as e:
yield "error", str(e)
class TodoistGetTaskBlock(Block):
"""Get an active task from Todoist"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
task_id: str = SchemaField(description="Task ID to retrieve")
class Output(BlockSchema):
project_id: str = SchemaField(description="Project ID containing the task")
url: str = SchemaField(description="Task URL")
complete_data: dict = SchemaField(
description="Complete task data as dictionary"
)
error: str = SchemaField(description="Error message if request failed")
def __init__(self):
super().__init__(
id="16d7dc8c-de15-11ef-8ace-32d3674e8b7e",
description="Get an active task from Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistGetTaskBlock.Input,
output_schema=TodoistGetTaskBlock.Output,
test_input={"credentials": TEST_CREDENTIALS_INPUT, "task_id": "2995104339"},
test_credentials=TEST_CREDENTIALS,
test_output=[
("project_id", "2203306141"),
("url", "https://todoist.com/showTask?id=2995104339"),
(
"complete_data",
{
"id": "2995104339",
"project_id": "2203306141",
"url": "https://todoist.com/showTask?id=2995104339",
},
),
],
test_mock={
"get_task": lambda *args, **kwargs: {
"project_id": "2203306141",
"id": "2995104339",
"url": "https://todoist.com/showTask?id=2995104339",
}
},
)
@staticmethod
def get_task(credentials: TodoistCredentials, task_id: str):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
task = api.get_task(task_id=task_id)
return Task.to_dict(task)
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
task_data = self.get_task(credentials, input_data.task_id)
if task_data:
yield "project_id", task_data["project_id"]
yield "url", task_data["url"]
yield "complete_data", task_data
except Exception as e:
yield "error", str(e)
class TodoistUpdateTaskBlock(Block):
"""Updates an existing task in Todoist"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
task_id: str = SchemaField(description="Task ID to update")
content: str = SchemaField(description="Task content", advanced=False)
description: Optional[str] = SchemaField(
description="Task description", default=None, advanced=False
)
project_id: Optional[str] = SchemaField(
description="Project ID this task should belong to",
default=None,
advanced=False,
)
section_id: Optional[str] = SchemaField(
description="Section ID this task should belong to",
default=None,
advanced=False,
)
parent_id: Optional[str] = SchemaField(
description="Parent task ID", default=None, advanced=True
)
order: Optional[int] = SchemaField(
description="Optional order among other tasks,[Non-zero integer value used by clients to sort tasks under the same parent]",
default=None,
advanced=True,
)
labels: Optional[list[str]] = SchemaField(
description="Task labels", default=None, advanced=True
)
priority: Optional[int] = SchemaField(
description="Task priority from 1 (normal) to 4 (urgent)",
default=None,
advanced=True,
)
due_date: Optional[datetime] = SchemaField(
description="Due date in YYYY-MM-DD format", advanced=True, default=None
)
deadline_date: Optional[datetime] = SchemaField(
description="Specific date in YYYY-MM-DD format relative to user's timezone",
default=None,
advanced=True,
)
assignee_id: Optional[str] = SchemaField(
description="Responsible user ID", default=None, advanced=True
)
duration_unit: Optional[str] = SchemaField(
description="Task duration unit (minute/day)", default=None, advanced=True
)
duration: Optional[int] = SchemaField(
description="Task duration amount, You need to selecct the duration unit first",
depends_on=["duration_unit"],
default=None,
advanced=True,
)
class Output(BlockSchema):
success: bool = SchemaField(description="Whether the update was successful")
error: str = SchemaField(description="Error message if request failed")
def __init__(self):
super().__init__(
id="1eee6d32-de15-11ef-a2ff-32d3674e8b7e",
description="Updates an existing task in Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistUpdateTaskBlock.Input,
output_schema=TodoistUpdateTaskBlock.Output,
test_input={
"credentials": TEST_CREDENTIALS_INPUT,
"task_id": "2995104339",
"content": "Buy Coffee",
},
test_credentials=TEST_CREDENTIALS,
test_output=[("success", True)],
test_mock={"update_task": lambda *args, **kwargs: True},
)
@staticmethod
def update_task(credentials: TodoistCredentials, task_id: str, **kwargs):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
is_success = api.update_task(task_id=task_id, **kwargs)
return is_success
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
due_date = (
input_data.due_date.strftime("%Y-%m-%d")
if input_data.due_date
else None
)
deadline_date = (
input_data.deadline_date.strftime("%Y-%m-%d")
if input_data.deadline_date
else None
)
task_updates = {}
if input_data.content is not None:
task_updates["content"] = input_data.content
if input_data.description is not None:
task_updates["description"] = input_data.description
if input_data.project_id is not None:
task_updates["project_id"] = input_data.project_id
if input_data.section_id is not None:
task_updates["section_id"] = input_data.section_id
if input_data.parent_id is not None:
task_updates["parent_id"] = input_data.parent_id
if input_data.order is not None:
task_updates["order"] = input_data.order
if input_data.labels is not None:
task_updates["labels"] = input_data.labels
if input_data.priority is not None:
task_updates["priority"] = input_data.priority
if due_date is not None:
task_updates["due_date"] = due_date
if deadline_date is not None:
task_updates["deadline_date"] = deadline_date
if input_data.assignee_id is not None:
task_updates["assignee_id"] = input_data.assignee_id
if input_data.duration is not None:
task_updates["duration"] = input_data.duration
if input_data.duration_unit is not None:
task_updates["duration_unit"] = input_data.duration_unit
self.update_task(
credentials,
input_data.task_id,
**{k: v for k, v in task_updates.items() if v is not None},
)
yield "success", True
except Exception as e:
yield "error", str(e)
class TodoistCloseTaskBlock(Block):
"""Closes a task in Todoist"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
task_id: str = SchemaField(description="Task ID to close")
class Output(BlockSchema):
success: bool = SchemaField(
description="Whether the task was successfully closed"
)
error: str = SchemaField(description="Error message if request failed")
def __init__(self):
super().__init__(
id="29fac798-de15-11ef-b839-32d3674e8b7e",
description="Closes a task in Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistCloseTaskBlock.Input,
output_schema=TodoistCloseTaskBlock.Output,
test_input={"credentials": TEST_CREDENTIALS_INPUT, "task_id": "2995104339"},
test_credentials=TEST_CREDENTIALS,
test_output=[("success", True)],
test_mock={"close_task": lambda *args, **kwargs: True},
)
@staticmethod
def close_task(credentials: TodoistCredentials, task_id: str):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
is_success = api.close_task(task_id=task_id)
return is_success
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
is_success = self.close_task(credentials, input_data.task_id)
yield "success", is_success
except Exception as e:
yield "error", str(e)
class TodoistReopenTaskBlock(Block):
"""Reopens a task in Todoist"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
task_id: str = SchemaField(description="Task ID to reopen")
class Output(BlockSchema):
success: bool = SchemaField(
description="Whether the task was successfully reopened"
)
error: str = SchemaField(description="Error message if request failed")
def __init__(self):
super().__init__(
id="2e6bf6f8-de15-11ef-ae7c-32d3674e8b7e",
description="Reopens a task in Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistReopenTaskBlock.Input,
output_schema=TodoistReopenTaskBlock.Output,
test_input={"credentials": TEST_CREDENTIALS_INPUT, "task_id": "2995104339"},
test_credentials=TEST_CREDENTIALS,
test_output=[
("success", True),
],
test_mock={"reopen_task": lambda *args, **kwargs: (True)},
)
@staticmethod
def reopen_task(credentials: TodoistCredentials, task_id: str):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
is_success = api.reopen_task(task_id=task_id)
return is_success
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
is_success = self.reopen_task(credentials, input_data.task_id)
yield "success", is_success
except Exception as e:
yield "error", str(e)
class TodoistDeleteTaskBlock(Block):
"""Deletes a task in Todoist"""
class Input(BlockSchema):
credentials: TodoistCredentialsInput = TodoistCredentialsField([])
task_id: str = SchemaField(description="Task ID to delete")
class Output(BlockSchema):
success: bool = SchemaField(
description="Whether the task was successfully deleted"
)
error: str = SchemaField(description="Error message if request failed")
def __init__(self):
super().__init__(
id="33c29ada-de15-11ef-bcbb-32d3674e8b7e",
description="Deletes a task in Todoist",
categories={BlockCategory.PRODUCTIVITY},
input_schema=TodoistDeleteTaskBlock.Input,
output_schema=TodoistDeleteTaskBlock.Output,
test_input={"credentials": TEST_CREDENTIALS_INPUT, "task_id": "2995104339"},
test_credentials=TEST_CREDENTIALS,
test_output=[
("success", True),
],
test_mock={"delete_task": lambda *args, **kwargs: (True)},
)
@staticmethod
def delete_task(credentials: TodoistCredentials, task_id: str):
try:
api = TodoistAPI(credentials.access_token.get_secret_value())
is_success = api.delete_task(task_id=task_id)
return is_success
except Exception as e:
raise e
def run(
self,
input_data: Input,
*,
credentials: TodoistCredentials,
**kwargs,
) -> BlockOutput:
try:
is_success = self.delete_task(credentials, input_data.task_id)
yield "success", is_success
except Exception as e:
yield "error", str(e)

View File

@@ -92,7 +92,8 @@ class TwitterPostTweetBlock(Block):
attachment: Union[Media, DeepLink, Poll, Place, Quote] | None = SchemaField(
discriminator="discriminator",
description="Additional tweet data (media, deep link, poll, place or quote)",
advanced=True,
advanced=False,
default=Media(discriminator="media"),
)
exclude_reply_user_ids: Optional[List[str]] = SchemaField(

View File

@@ -1,5 +1,7 @@
from typing import TYPE_CHECKING
from backend.integrations.oauth.todoist import TodoistOAuthHandler
from .github import GitHubOAuthHandler
from .google import GoogleOAuthHandler
from .linear import LinearOAuthHandler
@@ -19,6 +21,7 @@ HANDLERS_BY_NAME: dict["ProviderName", type["BaseOAuthHandler"]] = {
NotionOAuthHandler,
TwitterOAuthHandler,
LinearOAuthHandler,
TodoistOAuthHandler,
]
}
# --8<-- [end:HANDLERS_BY_NAMEExample]

View File

@@ -0,0 +1,81 @@
import urllib.parse
from typing import ClassVar, Optional
import requests
from backend.data.model import OAuth2Credentials, ProviderName
from backend.integrations.oauth.base import BaseOAuthHandler
class TodoistOAuthHandler(BaseOAuthHandler):
PROVIDER_NAME = ProviderName.TODOIST
DEFAULT_SCOPES: ClassVar[list[str]] = [
"task:add",
"data:read",
"data:read_write",
"data:delete",
"project:delete",
]
AUTHORIZE_URL = "https://todoist.com/oauth/authorize"
TOKEN_URL = "https://todoist.com/oauth/access_token"
def __init__(self, client_id: str, client_secret: str, redirect_uri: str):
self.client_id = client_id
self.client_secret = client_secret
self.redirect_uri = redirect_uri
def get_login_url(
self, scopes: list[str], state: str, code_challenge: Optional[str]
) -> str:
params = {
"client_id": self.client_id,
"scope": ",".join(self.DEFAULT_SCOPES),
"state": state,
}
return f"{self.AUTHORIZE_URL}?{urllib.parse.urlencode(params)}"
def exchange_code_for_tokens(
self, code: str, scopes: list[str], code_verifier: Optional[str]
) -> OAuth2Credentials:
"""Exchange authorization code for access tokens"""
data = {
"client_id": self.client_id,
"client_secret": self.client_secret,
"code": code,
"redirect_uri": self.redirect_uri,
}
response = requests.post(self.TOKEN_URL, data=data)
response.raise_for_status()
tokens = response.json()
response = requests.post(
"https://api.todoist.com/sync/v9/sync",
headers={"Authorization": f"Bearer {tokens['access_token']}"},
data={"sync_token": "*", "resource_types": '["user"]'},
)
response.raise_for_status()
user_info = response.json()
user_email = user_info["user"].get("email")
return OAuth2Credentials(
provider=self.PROVIDER_NAME,
title=None,
username=user_email,
access_token=tokens["access_token"],
refresh_token=None,
access_token_expires_at=None,
refresh_token_expires_at=None,
scopes=scopes,
)
def _refresh_tokens(self, credentials: OAuth2Credentials) -> OAuth2Credentials:
# Todoist does not support token refresh
return credentials
def revoke_tokens(self, credentials: OAuth2Credentials) -> bool:
return False

View File

@@ -33,5 +33,6 @@ class ProviderName(str, Enum):
SLANT3D = "slant3d"
SMTP = "smtp"
TWITTER = "twitter"
TODOIST = "todoist"
UNREAL_SPEECH = "unreal_speech"
# --8<-- [end:ProviderName]

View File

@@ -320,6 +320,9 @@ class Secrets(UpdateTrackingModel["Secrets"], BaseSettings):
linear_client_id: str = Field(default="", description="Linear client ID")
linear_client_secret: str = Field(default="", description="Linear client secret")
todoist_client_id: str = Field(default="", description="Todoist client ID")
todoist_client_secret: str = Field(default="", description="Todoist client secret")
stripe_api_key: str = Field(default="", description="Stripe API Key")
stripe_webhook_secret: str = Field(default="", description="Stripe Webhook Secret")

View File

@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.
# This file is automatically @generated by Poetry 2.0.0 and should not be changed by hand.
[[package]]
name = "aio-pika"
@@ -4296,6 +4296,21 @@ files = [
doc = ["reno", "sphinx"]
test = ["pytest", "tornado (>=4.5)", "typeguard"]
[[package]]
name = "todoist-api-python"
version = "2.1.7"
description = "Official Python SDK for the Todoist REST API."
optional = false
python-versions = "<4.0,>=3.9"
groups = ["main"]
files = [
{file = "todoist_api_python-2.1.7-py3-none-any.whl", hash = "sha256:278bfe851b9bd19bde5ff5de09d813d671ef7310ba55e1962131fca5b59bb735"},
{file = "todoist_api_python-2.1.7.tar.gz", hash = "sha256:84934a19ccd83fb61010a8126362a5d7d6486c92454c111307ba55bc74903f5c"},
]
[package.dependencies]
requests = ">=2.32.3,<3.0.0"
[[package]]
name = "tomli"
version = "2.2.1"
@@ -5035,4 +5050,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.1"
python-versions = ">=3.10,<3.13"
content-hash = "7d37edf4fd4e1935811629fef29d463fa61f904d520aa497de2969fadeba92ec"
content-hash = "9a40ee57d7440f352b0ac0a138fa26d05cd8eadb0cf7d3a0f995fb3c36a2fa7c"

View File

@@ -55,7 +55,8 @@ sqlalchemy = "^2.0.36"
psycopg2-binary = "^2.9.10"
google-cloud-storage = "^2.18.2"
launchdarkly-server-sdk = "^9.8.0"
mem0ai = "^0.1.48"
mem0ai = "^0.1.44"
todoist-api-python = "^2.1.7"
moviepy = "^2.1.2"
[tool.poetry.group.dev.dependencies]

View File

@@ -85,6 +85,7 @@ export const providerIcons: Record<
unreal_speech: fallbackIcon,
exa: fallbackIcon,
hubspot: FaHubspot,
todoist: fallbackIcon,
};
// --8<-- [end:ProviderIconsEmbed]

View File

@@ -46,6 +46,7 @@ const providerDisplayNames: Record<CredentialsProviderName, string> = {
replicate: "Replicate",
revid: "Rev.ID",
twitter: "Twitter",
todoist: "Todoist",
unreal_speech: "Unreal Speech",
} as const;
// --8<-- [end:CredentialsProviderNames]

View File

@@ -141,6 +141,7 @@ export const PROVIDER_NAMES = {
REDDIT: "reddit",
REVID: "revid",
UNREAL_SPEECH: "unreal_speech",
TODOIST: "todoist",
} as const;
// --8<-- [end:BlockIOCredentialsSubSchema]

View File

@@ -134,7 +134,7 @@ Below is a comprehensive list of all available blocks, categorized by their prim
|------------|-------------|
| [Twitter Post Tweet](twitter/twitter.md#twitter-post-tweet-block) | Creates a tweet on Twitter with text content and optional attachments including media, polls, quotes, or deep links |
| [Twitter Delete Tweet](twitter/twitter.md#twitter-delete-tweet-block) | Deletes a specified tweet using its tweet ID |
| [Twitter Search Recent](twitter/twitter.md#twitter-search-recent-block) | Searches for tweets matching specified criteria with options for filtering and pagination |
| [Twitter Search Recent Tweets](twitter/twitter.md#twitter-search-recent-tweets-block) | Searches for tweets matching specified criteria with options for filtering and pagination |
| [Twitter Get Quote Tweets](twitter/twitter.md#twitter-get-quote-tweets-block) | Gets tweets that quote a specified tweet ID with options for pagination and filtering |
| [Twitter Retweet](twitter/twitter.md#twitter-retweet-block) | Creates a retweet of a specified tweet using its tweet ID |
| [Twitter Remove Retweet](twitter/twitter.md#twitter-remove-retweet-block) | Removes an existing retweet of a specified tweet |
@@ -167,4 +167,31 @@ Below is a comprehensive list of all available blocks, categorized by their prim
| Twitter Send Direct Message | Working... Sends a direct message to a specified user |
| Twitter Create DM Conversation | Working... Creates a new direct message conversation |
## Todoist Integration
| Block Name | Description |
|------------|-------------|
| [Todoist Create Label](todoist.md#todoist-create-label) | Creates a new label in Todoist |
| [Todoist List Labels](todoist.md#todoist-list-labels) | Retrieves all personal labels from Todoist |
| [Todoist Get Label](todoist.md#todoist-get-label) | Retrieves a specific label by ID |
| [Todoist Create Task](todoist.md#todoist-create-task) | Creates a new task in Todoist |
| [Todoist Get Tasks](todoist.md#todoist-get-tasks) | Retrieves active tasks from Todoist |
| [Todoist Update Task](todoist.md#todoist-update-task) | Updates an existing task |
| [Todoist Close Task](todoist.md#todoist-close-task) | Completes/closes a task |
| [Todoist Reopen Task](todoist.md#todoist-reopen-task) | Reopens a completed task |
| [Todoist Delete Task](todoist.md#todoist-delete-task) | Permanently deletes a task |
| [Todoist List Projects](todoist.md#todoist-list-projects) | Retrieves all projects from Todoist |
| [Todoist Create Project](todoist.md#todoist-create-project) | Creates a new project in Todoist |
| [Todoist Get Project](todoist.md#todoist-get-project) | Retrieves details for a specific project |
| [Todoist Update Project](todoist.md#todoist-update-project) | Updates an existing project |
| [Todoist Delete Project](todoist.md#todoist-delete-project) | Deletes a project and its contents |
| [Todoist List Collaborators](todoist.md#todoist-list-collaborators) | Retrieves collaborators on a project |
| [Todoist List Sections](todoist.md#todoist-list-sections) | Retrieves sections from Todoist |
| [Todoist Get Section](todoist.md#todoist-get-section) | Retrieves details for a specific section |
| [Todoist Delete Section](todoist.md#todoist-delete-section) | Deletes a section and its tasks |
| [Todoist Create Comment](todoist.md#todoist-create-comment) | Creates a new comment on a task or project |
| [Todoist Get Comments](todoist.md#todoist-get-comments) | Retrieves all comments for a task or project |
| [Todoist Get Comment](todoist.md#todoist-get-comment) | Retrieves a specific comment by ID |
| [Todoist Update Comment](todoist.md#todoist-update-comment) | Updates an existing comment |
| [Todoist Delete Comment](todoist.md#todoist-delete-comment) | Deletes a comment |
This comprehensive list covers all the blocks available in AutoGPT. Each block is designed to perform a specific task, and they can be combined to create powerful, automated workflows. For more detailed information on each block, click on its name to view the full documentation.

View File

@@ -0,0 +1,722 @@
# Todoist Blocks
## Todoist Create Label
### What it is
A block that creates a new label in Todoist.
### What it does
Creates a new label in Todoist with specified name, order, color and favorite status.
### How it works
It takes label details as input, connects to Todoist API, creates the label and returns the created label's details.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Name | Name of the label |
| Order | Optional label order |
| Color | Optional color of the label icon |
| Is Favorite | Whether label is marked as favorite |
### Outputs
| Output | Description |
|--------|-------------|
| ID | ID of the created label |
| Name | Name of the label |
| Color | Color of the label |
| Order | Label order |
| Is Favorite | Favorite status |
| Error | Error message if request failed |
### Possible use case
Creating new labels to organize and categorize tasks in Todoist.
---
## Todoist List Labels
### What it is
A block that retrieves all personal labels from Todoist.
### What it does
Fetches all personal labels from the user's Todoist account.
### How it works
Connects to Todoist API using provided credentials and retrieves all labels.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
### Outputs
| Output | Description |
|--------|-------------|
| Labels | List of complete label data |
| Label IDs | List of label IDs |
| Label Names | List of label names |
| Error | Error message if request failed |
### Possible use case
Getting an overview of all labels to organize tasks or find specific labels.
---
## Todoist Get Label
### What it is
A block that retrieves a specific label by ID.
### What it does
Fetches details of a specific label using its ID.
### How it works
Uses the label ID to retrieve label details from Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Label ID | ID of label to retrieve |
### Outputs
| Output | Description |
|--------|-------------|
| ID | Label ID |
| Name | Label name |
| Color | Label color |
| Order | Label order |
| Is Favorite | Favorite status |
| Error | Error message if request failed |
### Possible use case
Looking up details of a specific label for editing or verification.
---
## Todoist Create Task
### What it is
A block that creates a new task in Todoist.
### What it does
Creates a new task with specified content, description, project assignment and other optional parameters.
### How it works
Takes task details and creates a new task via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Content | Task content |
| Description | Optional task description |
| Project ID | Optional project to add task to |
| Section ID | Optional section to add task to |
| Parent ID | Optional parent task ID |
| Order | Optional task order |
| Labels | Optional task labels |
| Priority | Optional priority (1-4) |
| Due Date | Optional due date |
| Deadline Date | Optional deadline date |
| Assignee ID | Optional assignee |
| Duration Unit | Optional duration unit |
| Duration | Optional duration amount |
### Outputs
| Output | Description |
|--------|-------------|
| ID | Created task ID |
| URL | Task URL |
| Complete Data | Complete task data |
| Error | Error message if request failed |
### Possible use case
Creating new tasks with full customization of parameters.
---
## Todoist Get Tasks
### What it is
A block that retrieves active tasks from Todoist.
### What it does
Fetches tasks based on optional filters like project, section, label etc.
### How it works
Queries Todoist API with provided filters to get matching tasks.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Project ID | Optional filter by project |
| Section ID | Optional filter by section |
| Label | Optional filter by label |
| Filter | Optional custom filter string |
| Lang | Optional filter language |
| IDs | Optional specific task IDs |
### Outputs
| Output | Description |
|--------|-------------|
| IDs | List of task IDs |
| URLs | List of task URLs |
| Complete Data | Complete task data |
| Error | Error message if request failed |
### Possible use case
Retrieving tasks matching specific criteria for review or processing.
---
## Todoist Update Task
### What it is
A block that updates an existing task.
### What it does
Updates specified fields of an existing task.
### How it works
Takes task ID and updated fields, applies changes via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Task ID | ID of task to update |
| Content | New task content |
| Description | New description |
| Project ID | New project ID |
| Section ID | New section ID |
| Parent ID | New parent task ID |
| Order | New order |
| Labels | New labels |
| Priority | New priority |
| Due Date | New due date |
| Deadline Date | New deadline date |
| Assignee ID | New assignee |
| Duration Unit | New duration unit |
| Duration | New duration |
### Outputs
| Output | Description |
|--------|-------------|
| Success | Whether update succeeded |
| Error | Error message if failed |
### Possible use case
Modifying task details like due dates, priority etc.
---
## Todoist Close Task
### What it is
A block that completes/closes a task.
### What it does
Marks a task as complete in Todoist.
### How it works
Uses task ID to mark it complete via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Task ID | ID of task to close |
### Outputs
| Output | Description |
|--------|-------------|
| Success | Whether task was closed |
| Error | Error message if failed |
### Possible use case
Marking tasks as done in automated workflows.
---
## Todoist Reopen Task
### What it is
A block that reopens a completed task.
### What it does
Marks a completed task as active again.
### How it works
Uses task ID to reactivate via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Task ID | ID of task to reopen |
### Outputs
| Output | Description |
|--------|-------------|
| Success | Whether task was reopened |
| Error | Error message if failed |
### Possible use case
Reactivating tasks that were closed accidentally or need to be repeated.
---
## Todoist Delete Task
### What it is
A block that permanently deletes a task.
### What it does
Removes a task completely from Todoist.
### How it works
Uses task ID to delete via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Task ID | ID of task to delete |
### Outputs
| Output | Description |
|--------|-------------|
| Success | Whether deletion succeeded |
| Error | Error message if failed |
### Possible use case
Removing unwanted or obsolete tasks from the system.
---
## Todoist List Projects
### What it is
A block that retrieves all projects from Todoist.
### What it does
Fetches all projects and their details from a user's Todoist account.
### How it works
Connects to Todoist API using provided credentials and retrieves all projects.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
### Outputs
| Output | Description |
|--------|-------------|
| Names List | List of project names |
| IDs List | List of project IDs |
| URL List | List of project URLs |
| Complete Data | Complete project data |
| Error | Error message if request failed |
### Possible use case
Getting an overview of all projects for organization or automation.
---
## Todoist Create Project
### What it is
A block that creates a new project in Todoist.
### What it does
Creates a new project with specified name, parent project, color and other settings.
### How it works
Takes project details and creates via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Name | Name of the project |
| Parent ID | Optional parent project ID |
| Color | Optional color of project icon |
| Is Favorite | Whether project is favorite |
| View Style | Display style (list/board) |
### Outputs
| Output | Description |
|--------|-------------|
| Success | Whether creation succeeded |
| Error | Error message if failed |
### Possible use case
Creating new projects programmatically for workflow automation.
---
## Todoist Get Project
### What it is
A block that retrieves details for a specific project.
### What it does
Fetches complete details of a single project by ID.
### How it works
Uses project ID to retrieve details via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Project ID | ID of project to get |
### Outputs
| Output | Description |
|--------|-------------|
| Project ID | ID of the project |
| Project Name | Name of the project |
| Project URL | URL of the project |
| Complete Data | Complete project data |
| Error | Error message if failed |
### Possible use case
Looking up project details for verification or editing.
---
## Todoist Update Project
### What it is
A block that updates an existing project.
### What it does
Updates specified fields of an existing project.
### How it works
Takes project ID and updated fields, applies via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Project ID | ID of project to update |
| Name | New project name |
| Color | New color for icon |
| Is Favorite | New favorite status |
| View Style | New display style |
### Outputs
| Output | Description |
|--------|-------------|
| Success | Whether update succeeded |
| Error | Error message if failed |
### Possible use case
Modifying project settings or reorganizing projects.
---
## Todoist Delete Project
### What it is
A block that deletes a project and its contents.
### What it does
Permanently removes a project including sections and tasks.
### How it works
Uses project ID to delete via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Project ID | ID of project to delete |
### Outputs
| Output | Description |
|--------|-------------|
| Success | Whether deletion succeeded |
| Error | Error message if failed |
### Possible use case
Removing completed or obsolete projects.
---
## Todoist List Collaborators
### What it is
A block that retrieves collaborators on a project.
### What it does
Fetches all collaborators and their details for a specific project.
### How it works
Uses project ID to get collaborator list via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Project ID | ID of project to check |
### Outputs
| Output | Description |
|--------|-------------|
| Collaborator IDs | List of collaborator IDs |
| Collaborator Names | List of collaborator names |
| Collaborator Emails | List of collaborator emails |
| Complete Data | Complete collaborator data |
| Error | Error message if failed |
### Possible use case
Managing project sharing and collaboration.
---
## Todoist List Sections
### What it is
A block that retrieves sections from Todoist.
### What it does
Fetches all sections, optionally filtered by project.
### How it works
Connects to Todoist API to retrieve sections list.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Project ID | Optional project filter |
### Outputs
| Output | Description |
|--------|-------------|
| Names List | List of section names |
| IDs List | List of section IDs |
| Complete Data | Complete section data |
| Error | Error message if failed |
### Possible use case
Getting section information for task organization.
---
## Todoist Get Section
### What it is
A block that retrieves details for a specific section.
### What it does
Fetches complete details of a single section by ID.
### How it works
Uses section ID to retrieve details via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Section ID | ID of section to get |
### Outputs
| Output | Description |
|--------|-------------|
| ID | Section ID |
| Project ID | Parent project ID |
| Order | Section order |
| Name | Section name |
| Error | Error message if failed |
### Possible use case
Looking up section details for task management.
---
## Todoist Delete Section
### What it is
A block that deletes a section and its tasks.
### What it does
Permanently removes a section including all tasks.
### How it works
Uses section ID to delete via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Section ID | ID of section to delete |
### Outputs
| Output | Description |
|--------|-------------|
| Success | Whether deletion succeeded |
| Error | Error message if failed |
### Possible use case
Removing unused sections or reorganizing projects.
---
## Todoist Create Comment
### What it is
A block that creates a new comment on a Todoist task or project.
### What it does
Creates a comment with specified content on either a task or project.
### How it works
Takes comment content and task/project ID, creates comment via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Content | Comment content |
| ID Type | Task ID or Project ID to comment on |
| Attachment | Optional file attachment |
### Outputs
| Output | Description |
|--------|-------------|
| ID | ID of created comment |
| Content | Comment content |
| Posted At | Comment timestamp |
| Task ID | Associated task ID |
| Project ID | Associated project ID |
| Error | Error message if request failed |
### Possible use case
Adding notes and comments to tasks or projects automatically.
---
## Todoist Get Comments
### What it is
A block that retrieves all comments for a task or project.
### What it does
Fetches all comments associated with a specific task or project.
### How it works
Uses task/project ID to get comments list via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| ID Type | Task ID or Project ID to get comments for |
### Outputs
| Output | Description |
|--------|-------------|
| Comments | List of comments |
| Error | Error message if request failed |
### Possible use case
Reviewing comment history on tasks or projects.
---
## Todoist Get Comment
### What it is
A block that retrieves a specific comment by ID.
### What it does
Fetches details of a single comment using its ID.
### How it works
Uses comment ID to retrieve details via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Comment ID | ID of comment to retrieve |
### Outputs
| Output | Description |
|--------|-------------|
| Content | Comment content |
| ID | Comment ID |
| Posted At | Comment timestamp |
| Project ID | Associated project ID |
| Task ID | Associated task ID |
| Attachment | Optional file attachment |
| Error | Error message if request failed |
### Possible use case
Looking up specific comment details for reference.
---
## Todoist Update Comment
### What it is
A block that updates an existing comment.
### What it does
Updates the content of a specific comment.
### How it works
Takes comment ID and new content, updates via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Comment ID | ID of comment to update |
| Content | New content for the comment |
### Outputs
| Output | Description |
|--------|-------------|
| Success | Whether update succeeded |
| Error | Error message if request failed |
### Possible use case
Modifying existing comments to fix errors or update information.
---
## Todoist Delete Comment
### What it is
A block that deletes a comment.
### What it does
Permanently removes a comment from a task or project.
### How it works
Uses comment ID to delete via Todoist API.
### Inputs
| Input | Description |
|-------|-------------|
| Credentials | Todoist API credentials |
| Comment ID | ID of comment to delete |
### Outputs
| Output | Description |
|--------|-------------|
| Success | Whether deletion succeeded |
| Error | Error message if request failed |
### Possible use case
Removing outdated or incorrect comments from tasks/projects.

View File

@@ -122,22 +122,22 @@ It uses the Twitter API (Tweepy) to fetch quote tweets for a given tweet ID, han
| max_results | Maximum number of results to return (max 100) |
| exclude | Types of tweets to exclude |
| pagination_token | Token for getting next page of results |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter/twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter/twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
|--------|-------------|
| ids | List of quote tweet IDs |
| texts | List of quote tweet text contents |
| next_token | Token for retrieving next page [more info](twitter/twitter.md#common-output). |
| next_token | Token for retrieving next page [more info](twitter.md#common-output). |
| data | Complete tweet data |
| included | Additional requested data [more info](twitter/twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter/twitter.md#common-output). |
| included | Additional requested data [more info](twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter.md#common-output). |
| error | Error message if request failed |
### Possible use case
@@ -219,12 +219,12 @@ It uses the Twitter API (Tweepy) to fetch retweeter information for a given twee
| tweet_id | ID of the tweet to get retweeters for |
| max_results | Maximum number of results per page (1-100) |
| pagination_token | Token for getting next page of results |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter/twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter/twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -234,8 +234,8 @@ It uses the Twitter API (Tweepy) to fetch retweeter information for a given twee
| usernames | List of usernames who retweeted |
| next_token | Token for retrieving next page |
| data | Complete user data |
| included | Additional requested data [more info](twitter/twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter/twitter.md#common-output). |
| included | Additional requested data [more info](twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter.md#common-output). |
| error | Error message if request failed |
### Possible use case
@@ -261,12 +261,12 @@ It queries the Twitter API (Tweepy) with the provided user ID to fetch tweets me
| user_id | ID of user to get mentions for |
| max_results | Number of results per page (5-100) |
| pagination_token | Token for getting next page of results |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter/twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter/twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -277,8 +277,8 @@ It queries the Twitter API (Tweepy) with the provided user ID to fetch tweets me
| userNames | List of usernames who mentioned target user |
| next_token | Token for retrieving next page |
| data | Complete tweet data |
| included | Additional requested data [more info](twitter/twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter/twitter.md#common-output). |
| included | Additional requested data [more info](twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter.md#common-output). |
| error | Error message if request failed |
### Possible use case
@@ -303,12 +303,12 @@ It uses the Twitter API (Tweepy) to fetch tweets from the home timeline, handlin
| credentials | Twitter API credentials with required scopes |
| max_results | Number of results per page (5-100) |
| pagination_token | Token for getting next page of results |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter/twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter/twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -319,8 +319,8 @@ It uses the Twitter API (Tweepy) to fetch tweets from the home timeline, handlin
| userNames | List of usernames who authored tweets |
| next_token | Token for retrieving next page |
| data | Complete tweet data |
| included | Additional requested data [more info](twitter/twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter/twitter.md#common-output). |
| included | Additional requested data [more info](twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter.md#common-output). |
| error | Error message if request failed |
### Possible use case
@@ -346,12 +346,12 @@ It uses the Twitter API (Tweepy) to fetch tweets from a specified user's timelin
| user_id | ID of user to get tweets from |
| max_results | Number of results per page (5-100) |
| pagination_token | Token for getting next page of results |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter/twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter/twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -362,8 +362,8 @@ It uses the Twitter API (Tweepy) to fetch tweets from a specified user's timelin
| userNames | List of usernames who authored tweets |
| next_token | Token for retrieving next page |
| data | Complete tweet data |
| included | Additional requested data [more info](twitter/twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter/twitter.md#common-output). |
| included | Additional requested data [more info](twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter.md#common-output). |
| error | Error message if request failed |
### Possible use case
@@ -387,12 +387,12 @@ It uses the Twitter API (Tweepy) to fetch a single tweet by its ID, handling aut
|-------|-------------|
| credentials | Twitter API credentials with required scopes |
| tweet_id | ID of the tweet to fetch |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter/twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter/twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -402,8 +402,8 @@ It uses the Twitter API (Tweepy) to fetch a single tweet by its ID, handling aut
| userId | ID of tweet author |
| userName | Username of tweet author |
| data | Complete tweet data |
| included | Additional requested data [more info](twitter/twitter.md#common-output). |
| meta | Tweet metadata [more info](twitter/twitter.md#common-output). |
| included | Additional requested data [more info](twitter.md#common-output). |
| meta | Tweet metadata [more info](twitter.md#common-output). |
| error | Error message if request failed |
### Possible use case
@@ -427,12 +427,12 @@ It uses the Twitter API (Tweepy) to batch fetch tweets by their IDs, handling au
|-------|-------------|
| credentials | Twitter API credentials with required scopes |
| tweet_ids | List of tweet IDs to fetch (max 100) |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter/twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter/twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -442,8 +442,8 @@ It uses the Twitter API (Tweepy) to batch fetch tweets by their IDs, handling au
| userIds | List of tweet author IDs |
| userNames | List of tweet author usernames |
| data | Complete tweet data array |
| included | Additional requested data [more info](twitter/twitter.md#common-output). |
| meta | Tweet metadata [more info](twitter/twitter.md#common-output). |
| included | Additional requested data [more info](twitter.md#common-output). |
| meta | Tweet metadata [more info](twitter.md#common-output). |
| error | Error message if request failed |
### Possible use case
@@ -497,9 +497,9 @@ It uses the Twitter API (Tweepy) to fetch information about users who liked a gi
| tweet_id | ID of tweet to get liking users for |
| max_results | Maximum number of results to return (1-100) |
| pagination_token | Token for getting next page of results |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -508,8 +508,8 @@ It uses the Twitter API (Tweepy) to fetch information about users who liked a gi
| username | List of usernames who liked |
| next_token | Token for retrieving next page |
| data | Complete user data |
| included | Additional requested data [more info](twitter/twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter/twitter.md#common-output). |
| included | Additional requested data [more info](twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter.md#common-output). |
| error | Error message if request failed |
### Possible use case
@@ -535,12 +535,12 @@ It uses the Twitter API (Tweepy) to fetch tweets liked by a given user ID, handl
| user_id | ID of user to get liked tweets for |
| max_results | Maximum number of results per page (5-100) |
| pagination_token | Token for getting next page of results |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter/twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter/twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -551,8 +551,8 @@ It uses the Twitter API (Tweepy) to fetch tweets liked by a given user ID, handl
| userNames | List of tweet author usernames |
| next_token | Token for retrieving next page |
| data | Complete tweet data |
| included | Additional requested data [more info](twitter/twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter/twitter.md#common-output). |
| included | Additional requested data [more info](twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter.md#common-output). |
| error | Error message if request failed |
### Possible use case
@@ -689,12 +689,12 @@ It uses the Twitter API (Tweepy) to fetch bookmarked tweets, handling pagination
| credentials | Twitter API credentials with required scopes |
| max_results | Maximum number of results per page (1-100) |
| pagination_token | Token for getting next page of results |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter/twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter/twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -705,8 +705,8 @@ It uses the Twitter API (Tweepy) to fetch bookmarked tweets, handling pagination
| userName | List of tweet author usernames |
| next_token | Token for retrieving next page |
| data | Complete tweet data |
| included | Additional requested data [more info](twitter/twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter/twitter.md#common-output). |
| included | Additional requested data [more info](twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter.md#common-output). |
| error | Error message if request failed |
### Possible use case
@@ -906,9 +906,9 @@ It uses the Twitter API (Tweepy) to fetch followers for a given user ID, handlin
| target_user_id | ID of user to get followers for |
| max_results | Maximum number of results per page (1-1000) |
| pagination_token | Token for getting next page of results |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -917,8 +917,8 @@ It uses the Twitter API (Tweepy) to fetch followers for a given user ID, handlin
| usernames | List of follower usernames |
| next_token | Token for retrieving next page |
| data | Complete user data |
| includes | Additional requested data [more info](twitter/twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter/twitter.md#common-output). |
| includes | Additional requested data [more info](twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter.md#common-output). |
| error | Error message if request failed |
### Possible use case
@@ -944,9 +944,9 @@ It uses the Twitter API (Tweepy) to fetch following list for a given user ID, ha
| target_user_id | ID of user to get following list for |
| max_results | Maximum number of results per page (1-1000) |
| pagination_token | Token for getting next page of results |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -955,8 +955,8 @@ It uses the Twitter API (Tweepy) to fetch following list for a given user ID, ha
| usernames | List of following usernames |
| next_token | Token for retrieving next page |
| data | Complete user data |
| includes | Additional requested data [more info](twitter/twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter/twitter.md#common-output). |
| includes | Additional requested data [more info](twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter.md#common-output). |
| error | Error message if request failed |
### Possible use case
@@ -1009,9 +1009,9 @@ It uses the Twitter API (Tweepy) to fetch muted users, handling authentication a
| credentials | Twitter API credentials with required scopes |
| max_results | Maximum results per page (1-1000, default 10) |
| pagination_token | Token for getting next/previous page |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -1073,9 +1073,9 @@ It uses the Twitter API (Tweepy) to fetch user data for a single user identified
|-------|-------------|
| credentials | Twitter API credentials with required scopes |
| identifier | User identifier (either user ID or username) |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -1108,9 +1108,9 @@ It uses the Twitter API (Tweepy) to batch fetch user data for multiple users ide
|-------|-------------|
| credentials | Twitter API credentials with required scopes |
| identifier | List of user identifiers (either user IDs or usernames, max 100) |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -1145,9 +1145,9 @@ It uses the Twitter API (Tweepy) to search for Spaces matching the query paramet
| query | Search term to find in Space titles |
| max_results | Maximum number of results to return (1-100, default 10) |
| state | Type of Spaces to return (live, scheduled, or all) |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| space_fields | Space-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| space_fields | Space-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -1182,9 +1182,9 @@ It uses the Twitter API (Tweepy) to batch fetch Space data for multiple Spaces,
|-------|-------------|
| credentials | Twitter API credentials with required scopes |
| identifier | Choice of lookup by Space IDs or creator user IDs |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| space_fields | Space-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| space_fields | Space-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -1216,9 +1216,9 @@ It uses the Twitter API (Tweepy) to fetch Space data for a single Space ID, hand
|-------|-------------|
| credentials | Twitter API credentials with required scopes |
| space_id | ID of Space to retrieve |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| space_fields | Space-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| space_fields | Space-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -1251,8 +1251,8 @@ It uses the Twitter API (Tweepy) to fetch buyer information for a Space, handlin
|-------|-------------|
| credentials | Twitter API credentials with required scopes |
| space_id | ID of Space to get buyers for |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -1284,12 +1284,12 @@ It uses the Twitter API (Tweepy) to fetch tweets from a Space, handling authenti
|-------|-------------|
| credentials | Twitter API credentials with required scopes |
| space_id | ID of Space to get tweets for |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter/twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter/twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -1322,9 +1322,9 @@ It uses the Twitter API (Tweepy) to fetch list data for a single list ID, handli
|-------|-------------|
| credentials | Twitter API credentials with required scopes |
| list_id | ID of the Twitter List to retrieve |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| list_fields | List-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| list_fields | List-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -1361,9 +1361,9 @@ It uses the Twitter API (Tweepy) to fetch owned lists for a given user ID, handl
| user_id | ID of user whose Lists to retrieve |
| max_results | Maximum results per page (1-100, default 10) |
| pagination_token | Token for getting next page |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| list_fields | List-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| list_fields | List-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -1532,12 +1532,12 @@ It uses the Twitter API (Tweepy) to fetch tweets from a specified List, handling
| list_id | ID of the List to get tweets from |
| max_results | Maximum number of results per page (1-100, default 10) |
| pagination_token | Token for getting next page of results |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter/twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter/twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter/twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| media_fields | Media-related fields to include [more info](twitter.md#common-input). |
| place_fields | Location-related fields to include [more info](twitter.md#common-input). |
| poll_fields | Poll-related fields to include [more info](twitter.md#common-input). |
| tweet_fields | Tweet-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -1546,8 +1546,8 @@ It uses the Twitter API (Tweepy) to fetch tweets from a specified List, handling
| texts | List of tweet text contents |
| next_token | Token for retrieving next page |
| data | Complete tweet data array |
| included | Additional requested data [more info](twitter/twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter/twitter.md#common-output). |
| included | Additional requested data [more info](twitter.md#common-output). |
| meta | Pagination and result metadata [more info](twitter.md#common-output). |
| error | Error message if request failed |
### Possible use case
@@ -1715,9 +1715,9 @@ It uses the Twitter API (Tweepy) to fetch pinned Lists data, handling authentica
| Input | Description |
|-------|-------------|
| credentials | Twitter API credentials with required scopes |
| expansions | Additional data fields to include [more info](twitter/twitter.md#common-input). |
| list_fields | List-specific fields to include [more info](twitter/twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter/twitter.md#common-input). |
| expansions | Additional data fields to include [more info](twitter.md#common-input). |
| list_fields | List-specific fields to include [more info](twitter.md#common-input). |
| user_fields | User-related fields to include [more info](twitter.md#common-input). |
### Outputs
| Output | Description |
@@ -1905,3 +1905,62 @@ Information about Twitter users
- For user data in Tweets, add `expansions=Author_User_ID` and appropriate `user_fields`.
- Data returned under `includes` helps cross-reference expanded data objects with their parent entities using IDs.
## Common Output
The Twitter API returns standardized response elements across many endpoints. Here are the common output fields you'll encounter:
### data
The primary data requested in the response
| Field | Description |
|-------|-------------|
| ID | Unique identifier for the object |
| Type | Type of object (tweet, user, etc) |
| Properties | Object-specific fields like text for tweets |
### includes
Additional expanded data objects referenced in the primary data
| Field | Description |
|-------|-------------|
| Tweets | Full tweet objects that were referenced |
| Users | User profile data for authors/mentions |
| Places | Location data for geo-tagged content |
| Media | Details about attached photos/videos |
| Polls | Information about embedded polls |
### meta
Metadata about the response and pagination
| Field | Description |
|-------|-------------|
| Result_Count | Number of items returned |
| Next_Token | Token to get next page of results |
| Previous_Token | Token to get previous page |
| Newest_ID | Most recent ID in results |
| Oldest_ID | Oldest ID in results |
| Total_Tweet_Count | Total matching tweets (search) |
### errors
Details about any errors that occurred
| Field | Description |
|-------|-------------|
| Title | Brief error description |
| Detail | Detailed error message |
| Type | Error category/classification |
| Status | HTTP status code |
### Non-paginated responses
For single-object lookups:
- data: Contains requested object
- includes: Referenced objects
- errors: Any errors encountered
### Paginated responses
For multi-object lookups:
- data: Array of objects
- includes: Referenced objects
- meta: Pagination details
- errors: Any errors encountered