Todoist Integration

It would be great to be able to choose one of my custom Filters (not just Inbox or Today) in my todist account and have it display the items I need to work on.

Thanks!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.

I would love to see my Today tasks, it that possible?

This thing is so cool!

1 Like

So a friend and I have worked out most of a simple Todoist Integration.

Below is what we have come up with. It finds the most overdue task and adds the date in red (to match the Todoist colouring). Once, you no longer have any overdue tasks, it just shows the task.

If you want to try it out, grab your API Token from the integrations panel of Todoist on a browser: todoist.com/app/settings/integrations.

Copy this code as a text file, save with a .star extension.

Replace the place holder text with you API Token and run it per the instructions on the Pixlet Github docs:
github.com/tidbyt/pixlet#push-to-a-tidbyt

Hopefully, we can work together to make it reliable, add features like respecting priority flags and even add it as a community app.

load("render.star", "render")
load("http.star", "http")
load("encoding/base64.star", "base64")
load("cache.star", "cache")
load("encoding/json.star", "json")
load("time.star", "time")

TODOIST_API_BASE_URL = "https://api.todoist.com/rest/v1"

TODOIST_API_TASKS_URL = TODOIST_API_BASE_URL + "/tasks?filter=(overdue|today)"

TODOIST_API_TOKEN = "ADDTOKEN HERE!!!"

MODEL_KEY_TEXT = "text"
MODEL_KEY_DUE = "due"

CACHE_KEY_MODEL = "todoist_model"

def dateStringToTime(dateString):
    return time.parse_time(dateString, "2006-01-02")

def renderDate(dateString):
    return dateStringToTime(dateString).format("02-Jan")

def isOverdue(date):
    current = time.now()
    currentDay = time.time(year = current.year, month = current.month, day = current.day)
    return date < currentDay

def main():
    # Download tasks
    resp = http.get(TODOIST_API_TASKS_URL, headers={"Authorization": "Bearer " + TODOIST_API_TOKEN})
    if resp.status_code != 200:
        fail("Get tasks failed with status %d", resp.status_code)
    parsed = resp.json()

    # Compute model to display
    model = None
    for task in parsed:
        due = dateStringToTime(task["due"]["date"])
        thisModel = { MODEL_KEY_TEXT: task["content"] }
        if isOverdue(due):
            thisModel.update([(MODEL_KEY_DUE, task["due"]["date"])])
        if model == None:
            model = thisModel
            continue
        if model.get(MODEL_KEY_DUE) == None:
            if thisModel.get(MODEL_KEY_DUE) != None:
                model = thisModel
                continue
        else:
            if due < dateStringToTime(model[MODEL_KEY_DUE]):
                model = thisModel
                continue
    if model == None:
        model = { MODEL_KEY_TEXT: "No more tasks!" }

    # Render model
    children = [
        render.WrappedText(
            content = model[MODEL_KEY_TEXT]
        )
    ]
    if model.get(MODEL_KEY_DUE) != None:
        children.append(
          render.Row(
            children = [
                render.Text(
                    content = renderDate(model.get(MODEL_KEY_DUE)),
                    color = "#f00",
                    font = "CG-pixel-4x5-mono"
                )
            ],
            expanded = True,
            main_align = "end",
          )
        )
    return render.Root(
        child = render.Column(
            children = children,
            expanded = True,
            main_align = "space_between",
            cross_align = "center"
        )
    )