Action

Todoist Bulk Add (No Prompt)

Posted by @msouza91, Last update 16 days ago

DISCLAIMER: I don’t do JS, so please go easy on me, I just tried to combine some ideas and after some LLM shenanigans I got this working. I’m sure there are better ways to do this, but it solves my issue.

The idea is to just format the tasks specially multiple ones in the same project/label without having to pick for each one.

The labels have to be setup previously to be correctly placed on tasks, but that is alright.

This script is a Todoist task creation utility that processes a text draft to create multiple tasks with advanced formatting options.

Core Features

Project Assignment
- Tasks can be assigned to projects using #projectname
- Default project can be set using #projectname on a separate line
- All subsequent tasks use the default project until changed

Label Management
- Default labels can be set using @label1,label2 on a separate line
- Individual tasks can override defaults with @label syntax
- When a task has explicit labels, they take precedence over defaults

Task Parameters
Each task can include additional parameters after – symbols:
- –note: Adds a description to the task
- –due: Sets the due date/time
- –pri: Sets priority (1-4)

Language Support
- Default language for due dates can be set using ++XX format (e.g., ++en)

Task Format

Tasks follow this structure:

[task content] #project @labels --parameter value

Example:

Changing the due language:

++pt
@work,personal
Buy groceries @shopping --pri 1
Ligar para John @calls #personal --pri 4 --due hoje --note Remember to discuss project timeline
Marcar dentista @health --pri 2 --due amanhã
Revisar Documentos @work #personal --pri 3 --due "Proxima segunda"

Using default language:

@work,personal
Buy groceries @shopping --pri 1
Call John @calls #personal --pri 4 --due tod--note Remember to discuss project timeline
Schedule dentist @health --pri 2 --due tomorrow
Review documents @work #personal --pri 3 --due "next monday"

Credit to @agiletortoise for Todoist integration guidance and @davenichols for parameter-based quick add implementation.

Steps

  • script

    let createTasks = () => {
      let todoist = Todoist.create();
      let content = draft.content;
    
      if (content.length == 0) {
        alert("Draft is blank");
        return false;
      }
    
      // Get all projects for ID lookup
      let projects = todoist.getProjects();
      let projectMap = {};
      if (projects) {
        projects.forEach(p => {
          projectMap[p.name.toLowerCase()] = p.id;
        });
      }
    
      // Get all labels for ID lookup
      let labels = todoist.getLabels();
      let labelMap = {};
      if (labels) {
        labels.forEach(l => {
          labelMap[l.name.toLowerCase()] = l.name;
        });
      }
    
      let newDraft = "";
      let defaultProject = "";
      let defaultLang = "";
      let defaultLabels = [];
      let lines = content.split("\n");
    
      for (let line of lines) {
        if (line.length == 0) {
          newDraft += "\n";
          continue;
        }
    
        // Check for default project setting
        let proj = line.match(/^#(\S+)\s*/m);
        if (proj) {
          defaultProject = proj[1].toLowerCase();
          newDraft += line + "\n";
          continue;
        }
    
        // Check for default language setting
        let lang = line.match(/^\+\+(\w{2})\s*/m);
        if (lang) {
          defaultLang = lang[1].toLowerCase();
          newDraft += line + "\n";
          continue;
        }
    
        // Check for default labels setting
        let labelLine = line.match(/^@(\S+)\s*/m);
        if (labelLine) {
          defaultLabels = labelLine[1].toLowerCase().split(',');
          newDraft += line + "\n";
          continue;
        }
    
        let parts = line.split("--");
        let mainText = parts[0];
    
        // Handle project in content
        let projectInContent = mainText.match(/#(\S+)/);
        let projectName = projectInContent ? projectInContent[1].toLowerCase() : defaultProject;
    
        // Handle labels in content
        let labelsInContent = mainText.match(/@(\S+)/g);
        let taskLabels = [];
    
        // Only use default labels if no explicit labels are present
        if (labelsInContent) {
          labelsInContent.forEach(label => {
            taskLabels.push(label.slice(1).toLowerCase());
          });
        } else {
          taskLabels = defaultLabels.slice();
        }
    
        // Clean up the main content
        mainText = mainText.replace(/#(\S+)/, '').replace(/@(\S+)/g, '').trim();
    
        let params = {
          "content": mainText
        };
    
        // Add project if specified
        if (projectName && projectMap[projectName]) {
          params["project_id"] = projectMap[projectName];
        }
    
        // Add labels if specified
        if (taskLabels.length > 0) {
          let labelNames = taskLabels
            .filter(label => labelMap[label])
            .map(label => labelMap[label]);
          if (labelNames.length > 0) {
            params["labels"] = labelNames;
          }
        }
    
        // Add default language if specified
        if (defaultLang) {
          params["due_lang"] = defaultLang;
        }
    
        // Handle additional parameters
        if (parts.length > 1) {
          for (let i = 1; i < parts.length; i++) {
            let opt = parts[i].split(" ", 1);
            let value = parts[i].slice(String(opt).length).trim();
    
            switch (String(opt)) {
              case 'note':
                params['description'] = value;
                break;
              case 'due':
                params['due_string'] = value;
                break;
              case 'pri':
                let inputPriority = parseInt(value);
                if (inputPriority >= 1 && inputPriority <= 4) {
                  params['priority'] = 5 - inputPriority;
                }
                break;
            }
          }
        }
    
        console.log("Creating task with params:", JSON.stringify(params, null, 2));
    
        let result = todoist.createTask(params);
    
        if (result) {
          newDraft += line + " - OK\n";
          console.log("Task created:", JSON.stringify(result, null, 2));
        }
        else {
          newDraft += line + " - Failed\n";
          console.log("Failed to create task");
        }
      }
    
      editor.setText(newDraft);
      return true;
    }
    
    if (!createTasks()) {
      context.fail();
    }

Options

  • After Success Trash
    Notification Info
    Log Level Info
Items available in the Drafts Directory are uploaded by community members. Use appropriate caution reviewing downloaded items before use.