Skip to content

Instantly share code, notes, and snippets.

@nightscape
Forked from dklawren/Todoist.md
Last active May 9, 2025 16:11
Show Gist options
  • Save nightscape/4c36944dc94eac8153496df00d567953 to your computer and use it in GitHub Desktop.
Save nightscape/4c36944dc94eac8153496df00d567953 to your computer and use it in GitHub Desktop.
source gistUrl gistFile
github-gist
Todoist.md

Todoist

Important Links

Configuration

You need to add a developer API key value to able to access your Todoist data. You can get one at https://app.todoist.com/app/settings/integrations/developer.

config.set('todoistToken', '<your_api_key>')

Templates

-- Renders a Todoist task object
templates.todoistTaskItem = template.new([==[
* **${project}** ${content} ([todoist](https://app.todoist.com/app/task/${id}))
]==])
-- Renders a Todoist project object
templates.todoistProjectItem = template.new([==[
* **${id}** ${name} ([todoist](https://app.todoist.com/app/project/${id}?fromV1Id=true))
]==])

Utility Functions and Setup

Todoist = {}
Todoist.projectIdMap = {}

function Todoist.apiCall(path, method, body)
  local token = config.get("todoistToken")
  if not token then
    error("todoistToken config not set")
  end
  return http.request("https://api.todoist.com/api/v1/" .. path, {
    method = method,
    headers = {
      Authorization = "Bearer " .. token,
      ["Content-Type"] = "application/json",
      Accept = "application/json"
    },
    body = body
  })
end

function Todoist.getProjectIdMap()
  if #Todoist.projectIdMap > 0 then
    return Todoist.projectIdMap
  end
  local resp = Todoist.apiCall("projects", "GET")
  if not resp.ok then
    error("Failed to get Todoist projects, see console for error")
    js.log("Error", resp)
    return
  end
  for _, item in ipairs(resp.body.results) do
    Todoist.projectIdMap[item.id] = item.name
  end
  return Todoist.projectIdMap
end

function Todoist.getProjectList()
  local projectIdMap = Todoist.getProjectIdMap()
  local projectList = {}
  for id, name in pairs(projectIdMap) do
    table.insert(projectList, {id = id, name = name})
  end
  return projectList
end

Get List of Tasks

The filter value is the same as what you would use for save filters in todoist itself such as (today | overdue).

function Todoist.getTasks(filter)
  local path = "tasks/filter?query=" .. js.window.encodeURIComponent(filter)
  local resp = Todoist.apiCall(path, "GET")
  if not resp.ok then
    error("Failed to get Todoist tasks, see console for error")
    js.log("Error", resp)
    return
  end
  local projectIdMap = Todoist.getProjectIdMap()
  local tasks = {}
  for _, item in ipairs(resp.body.results) do
    taskData = {}
    for key, value in pairs(item) do
      taskData[key] = value
    end
    taskData.project = projectIdMap[taskData.project_id]
    table.insert(tasks, taskData)
  end
  return tasks
end

Adding a Task

This creates a new global command to add a task to your Todoist Inbox. Any highlighted text will be included in the input.

command.define {
  name = "Todoist: Create Inbox Task",
  run = function()
    -- If there is selected text then prefill in the prompt
    local text = editor.getText()
    local selection = editor.getSelection()
    if selection.from != selection.to then
      text = text.substring(selection.from, selection.to)
    else
      text = nil;
    end

    local task = editor.prompt("Add to Todoist Inbox:", text)
    if not task then
      return
    end

    -- Find the project id for Inbox
    local inboxId = nil
    local projectIdMap = Todoist.getProjectIdMap()
    for id, name in pairs(projectIdMap) do
      if name == "Inbox" then
        inboxId = id
      end
    end

    local resp = Todoist.apiCall("tasks", "POST",
      { content = task, project_id = inboxId})

    editor.flashNotification("New task added to Todoist Inbox")  
  end
}

Examples Queries

List of Projects

${template.each(Todoist.getProjectList(), templates.todoistProjectItem)}

Get Tasks Matching Filter

${template.each(Todoist.getTasks("#Inbox"), templates.todoistTaskItem)}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment