Draft to Readwise?

Hello,

I use Drafts a lot to create Notes by dictaction when hearing Audible in the car / walking around. I would love to put those Drafts to Readwise. Readwise is a service that reminds you with daily reminders on things you marked in Kindle and so on. Main thing is to remember them better. So my question is if anybody has already built a connection to readwise? I looked here in the forum, google and action directory, but didn’t find anything.

They also have an api.

Thanks in advance
demaya

It seems pretty straight forward. I’ve put together a quick action that will send either the current selection, or if there is no selection, the entire draft, to your Readwise account. It just sends the text as a basic highlight (which turns out to be a quote). To send it as something else, just modify the action to parse whatever structured data you have in your draft and send that data via the JSON properties specified in the API documentation (link in original post above).

As you might expect, you do need a Readwise API access token to make use of the action, but you should be promoted for it in first use, then after that it is stored in Draft’s usual credentials store.

1 Like

Well done! Thanks. I will have a look at it.

Hi @sylumer, I have tried to modify your script, but am having some weird results. When I run the below script, I get a green Drafts success message, but my console.log is empty (despite the logging in my script) and nothing is showing up to Readwise. I ran your unmodified script with success, so I’m not sure what’s going on. Any debugging tips? Thanks!

// readwise

function postToReadwise(pTitle,pAuthor,pText,pURL,pNote,pType,pDate)
{
	const BASEURL = "https://readwise.io/api/v2/highlights/";
	
	let credReadwise = Credential.create("Readwise", "Highlight surfacing service.");
	credReadwise.addPasswordField("token", "API Token");
	credReadwise.authorize();

	let httpMain = HTTP.create();
	let respMain = httpMain.request(
	{
		"url" : BASEURL,
		"method" : "POST",
		"data": 
		{
			"highlights" : [{
                "text" : pText,
                "title": pTitle,
                "author":pAuthor,
                "source_url":pURL,
                "note":pNote,
                "source_type":pType,
                "highlighted_at": pDate
            }]
		},
		"headers" :
		{
			"Authorization" : `Token ${credReadwise.getValue("token")}`
		}
	});

    if (respMain.success) {
        console.log("yep");
        return true;
    }
	else
	{
		console.log(`[${respMain.statusCode}] ${respMain.error}`);
		return false;
	}
}

let t = draft.processTemplate("[[display_title]]");
let h = draft.processTemplate("[[line|3..]]");

var p = Prompt.create();
p.title="Parse draft for readwise";
p.addTextField("pTitle","Title",t);
p.addTextField("pAuthor","Author","");
p.addTextView("pText","Highlight",h,{"height":150});
p.addTextField("pURL","URL","");
p.addTextField("pNote","Note","");
p.addSelect("pType","Type",["book","article","podcast"],[""],false);
p.addButton("OK");
if(p.show()){
    console.log(p.fieldValues["pTitle"],p.fieldValues["pAuthor"],p.fieldValues["pText"],p.fieldValues["pURL"],p.fieldValues["pNote"],p.fieldValues["pType"],draft.createdAt.toISOString());
    postToReadwise(p.fieldValues["pTitle"],p.fieldValues["pAuthor"],p.fieldValues["pText"],p.fieldValues["pURL"],p.fieldValues["pNote"],p.fieldValues["pType"],draft.createdAt.toISOString());
}
else
{
    context.cancel();
}
~~~~

t looks as though you are always passing in content in every field. Is there always something in every field? I don’t think the API like’s null/blank data, and, not being a user myself (I just tried the free trial), does it make sense for all of the data to be populated at any point?

I was able to get it accepting content by simplifying the highlights and having fewer data items. I would suggest starting with just the mandatory field and then building it up one at a time, checking each as you go.

For the response, I made the following amendment just to get a visual cue on success/failure and any failure message.

    if (respMain.success) {
		app.displayInfoMessage("Success");
        console.log("yep");
        return true;
    }
	else
	{
		app.displayWarningMessage("Failure");
		alert(`[${respMain.statusCode}] ${respMain.error}`);
		console.log(`[${respMain.statusCode}] ${respMain.error}`);
		return false;
	}

I would note that the testing I’ve done with it this evening has given quite variable results. While I appreciate that the system does do deduplication, and I have had successes that have produced content in Readwise, I have also had successes against unique simple content that has not produced content. Honestly, I’m not particularly impressed with my experience of the API to this point.

Basically, I was trying to send “Type” without converting to String first (Drafts provides the response of a “select” prompt as an array). Whoops!

let pT = p.fieldValues["pType"].toString();
        postToReadwise(p.fieldValues["pTitle"],p.fieldValues["pAuthor"],p.fieldValues["pText"],p.fieldValues["pURL"],pT,draft.createdAt.toISOString());

Hey guys,

I am working on an updated script based on the great work of @sylumer. I want to get all books and then let the user choose where to add the note…

function getBooksFromReadwise()
{
	const BASEURL = "https://readwise.io/api/v2/books/";

	let credReadwise = Credential.create("Readwise", "Highlight surfacing service.");
	credReadwise.addPasswordField("token", "API Token");
	credReadwise.authorize();

	let httpMain = HTTP.create();
	let respMain = httpMain.request(
	{
		"url" : BASEURL,
        "method" : "GET",
		"headers" :
		{
			"Authorization" : `Token ${credReadwise.getValue("token")}`
		}
	});

    if(respMain.success) {
        let responseData = respMain.responseData;
        let responseText = respMain.responseText;
        editor.setText(responseData);

    } else {
        console.log(`[${respMain.statusCode}] ${respMain.error}`);
    }


}

The problem is that I get a proper Text via respMain.responseText; but no JSON / Object via respMain.responseData;. If I get the manual right there should be a proper answer.

All I get via reponseData is [object Foundation.__NSSwiftData]. Would be great to get a small kickstart again. Thanks

Kind regards
demaya

You will probably need to parse the JSON response yourself from the responseText. Try:


// parse response JSON to object
let responseData = JSON.parse(response.responseText);

Thanks for the hint @agiletortoise. I just finished a early alpha of the new script. It shows all books listed in Readwise as a list you can choose from. There is a little work to do, but it works in most cases. https://actions.getdrafts.com/a/1fl

@agiletortoise one more question: I tried a few things but books with a longer title (e.g. Everything is fuck*d: text text text) is only transmitted as everything before :. Is this a bug / thing I should address at Readwise side? I tried to encode, but then the title comes fully encoded on Readwise.

Could you dump the responseText you are getting back as an example? You could insert this to put in the clipboard when the script is run:

app.setClipboard(responseText);

I could take a look and see if there’s some disagreement on parsing.

@demaya I am getting wonky results with your Readwise action.

Draft text selected to push to Readwise is not settling into the selected book…

Instead Readwise is duplicating the book title with “%” between spaces and adding the selected Draft text to the new book.

Any thoughts on a fix?

1 Like

I can confirm the behaviour regarding the second book with percentages in between.

My Readwise trial expires months ago, so I can’t check anything on this, but looking at the “alpha” action that @demaya posted, my guess would be the JSON setting of the title. The text of the title is being escaped, which sounds exactly like what has been observed.

"title": escape(selectedBook.toString())

I wouldn’t expect to have to escape the text (and it is a deprecated JavaScript function in any case), so perhaps just try removing the escape function ocurrence(s), and see what effect that has.

Hey guys, been pretty stressful last months. Just wanna let you know that I am investigating and trying my best to make it work :slight_smile: Thanks for patience. Will update you here! Kind regards

1 Like

Short update. I am in contact with the Readwise team. They updated some stuff in the API for us so we can use Readwise much better. Working on an update of the Action in the next few days.

5 Likes

Hello folks,

I just finished an update for the Drafts 2 Readwise Action. From now on you can add your Drafts to Readwise while selecting from a comfortable menu with the last added items from Drafts or create a new book in Readwise.

Update is pushed to the action directory. Feedback is warmly welcome.

@jaymf & @woody please have a look too!

Kind regards

3 Likes

@demaya awesome of you to update this.

I am still experiencing the same error despite updating to the action you posted a few hours ago.

Here is the result in Readwise:

Testing%20to%20Readwise%20

Let me know how else I can assist.

this is amazing! thanks for this!

I was wondering if there was a way to push the draft tags to readwise as tags as well?

@demaya I’m still getting the same error - where spaces are filled with placeholders %% even with the latest action update 12 hours ago… am I the only one?

I will investigate the issue. I now have a contact to the API developers of Readwise.

@jaymf can you share your locale settings (like language & regional settings), just for research purposes. Thanks