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.