Python script: Insert link to random note (via Joplin API)

Hi all,

Here is a simple python script that I wrote using the foxmask joplin-api package. It does...

  1. Search for a template sentence (here: "Random note") in a specific note
  2. Replace the next line in the note with a link to a random note

I run the script once per day to update my Joplin "Dashboard" note. Reviewing random old notes helps me remembering stuff, and recognizing connections between topics.

Explanatory screenshot:

import asyncio
from joplin_api import JoplinApi
import random
import json

# CHANGE THOSE SETTINGS
joplin = JoplinApi(token='813f1dd7df3c2dba6a85702e1ed2cd141ed56482bf2ba24ef2847537523ee784fe019e0c30350a03ebfcb0890ef39d7e761189cf740e01638ad966c1889f6b48')
note_id = '572977ec0734468cbc2a33d6143a1453'

async def get_notes():
    result = await joplin.get_notes()
    return result.content

async def get_note(id):
    result = await joplin.get_note(id)
    return result.content

async def update_note(id, data):
    await joplin.update_note(id, **data)
    return None

def update_line(string, search_string, inserted_string):
    lines = string.split('\n')
    for i, line in enumerate(lines):
        if search_string in line:
            lines[i+1] = inserted_string
    new_string = "\n".join(lines)
    return new_string

def get_random_id():
    bytestring = loop.run_until_complete(get_notes())
    notes = json.loads(bytestring)  # convert bytestring to json / dictionary
    random_note = random.choice(notes)  # Choose random note
    return random_note['id'], random_note['title']  # Return id and title


loop = asyncio.get_event_loop()
rnd_id, rnd_title = get_random_id()

# Retrieve note data
bytestring = loop.run_until_complete(get_note(note_id))
data = json.loads(bytestring)   # Convert bytestring to json

# Change body
data['body'] = update_line(data['body'], 'Random note:', '[{:s}](:/{:s})'.format(rnd_title, rnd_id))
loop.run_until_complete(update_note(note_id, data))

Some remarks:

  • You need to modify the first lines (token & note ID) to your needs
  • Joplin needs to be running while running the script
  • For some reason, the script does delete the tags of the note
  • Use with caution! I'm not a developer, and have certainly neglected some edge-cases. Make backups before playing around with it!

Update 02/2021: Script does not work currently, probably due to recent changes of the API (pagination).

1 Like

You might want to try the one below.
It works on my machine and does not remove the tags on the note.

import json
import random
import requests

# CHANGE THE TWO SETTING BELOW
TOKEN = "f11db775b76e0f80ab39aa98df83f5856a095c283c886a3d6e0df74de331fef82ab16919be0ce46daa1da6108c983dfd1a02bf5139ab920ac19507aa8dc7e885"
NOTE_ID = "96ef3ee70aeb48c396c2f155f075b354"

# This one doesn't need changing
NOTES_ENDPOINT = "http://localhost:41184/notes"


def update_line(string, search_string, inserted_string):
    lines = string.split('\n')
    for i in range(len(lines)):
        if search_string in lines[i]:
            lines[i + 1] = inserted_string
    new_string = "\n".join(lines)
    return new_string


def get_note_body():
    return requests.get('{}/{}/?fields=body&token={}'.format(NOTES_ENDPOINT, NOTE_ID, TOKEN)).json()["body"]


def get_all_note_ids(page=0):
    res = requests.get('{}?order_by=title&limit=100&page={}&token={}'.format(NOTES_ENDPOINT, page, TOKEN))
    note_ids = []
    notes = res.json()["items"]
    for note in notes:
        note_ids.append((note.get("id"), note.get("title")))
    if res.json()["has_more"]:
        note_ids.extend(get_all_note_ids(page + 1))
    return note_ids


rnd_id, rnd_title = random.choice(get_all_note_ids())
current_body = get_note_body()
new_body = update_line(current_body, 'Random note:', '[{:s}](:/{:s})'.format(rnd_title, rnd_id))
if new_body is not None:
    # Only update if the new body is not empty (just a safeguard)
    requests.put('{}/{}?token={}'.format(NOTES_ENDPOINT, NOTE_ID, TOKEN),
                 data='{{ "body" : {} }}'.format(json.dumps(new_body)))