Skip to content

Commit

Permalink
feat: supporting pushing all wallabag entries to readwise reader
Browse files Browse the repository at this point in the history
  • Loading branch information
rwxd committed Feb 23, 2023
1 parent 8bbd489 commit ec275ae
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 2 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ Run continuously and push new annotations to Readwise every 60 minutes.
wallabag2readwise daemon --wait-time 60
```

#### Import all Wallabag entries to Readwise reader

```bash
wallabag2readwise reader
```

### Configuration

Get a new Readwise API Token from <https://readwise.io/access_token>.
Expand Down
30 changes: 28 additions & 2 deletions wallabag2readwise/cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import typer
from wallabag2readwise.misc import push_annotations
from wallabag2readwise.readwise import ReadwiseConnector
from wallabag2readwise.readwise import ReadwiseConnector, ReadwiseReaderConnector

from wallabag2readwise.wallabag import WallabagConnector
from time import sleep
Expand Down Expand Up @@ -46,7 +46,7 @@ def daemon(
wait_time: int = typer.Option(
60, help='time to wait between runs in minutes', envvar='WAIT_TIME'
),
):
) -> None:

console.print(f'> Starting daemon with {wait_time} minutes wait time')
while True:
Expand All @@ -64,6 +64,32 @@ def daemon(
sleep(wait_time * 60)


@app.command()
def reader(
wallabag_url: str = typer.Option(
..., envvar='WALLABAG_URL', help='url to your wallabag instance'
),
wallabag_user: str = typer.Option(..., envvar='WALLABAG_USER'),
wallabag_password: str = typer.Option(..., envvar='WALLABAG_PASSWORD', prompt=True),
wallabag_client_id: str = typer.Option(..., envvar='WALLABAG_CLIENT_ID'),
wallabag_client_secret: str = typer.Option(..., envvar='WALLABAG_CLIENT_SECRET'),
readwise_token: str = typer.Option(..., envvar='READWISE_TOKEN', prompt=True),
):
console.print(f'> Starting readwise reader import')
wallabag = WallabagConnector(
wallabag_url,
wallabag_user,
wallabag_password,
wallabag_client_id,
wallabag_client_secret,
)
readwise = ReadwiseReaderConnector(readwise_token)
wallabag_entries = wallabag.get_entries()
for entry in wallabag_entries:
console.print(f'=> Importing {entry.title}')
readwise.create(entry.url, tags=[t.label for t in entry.tags])


@app.command()
def version():
console.print(importlib.metadata.version('wallabag2readwise'))
59 changes: 59 additions & 0 deletions wallabag2readwise/readwise.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from ratelimit import limits, RateLimitException, sleep_and_retry
from backoff import on_exception, expo
from time import sleep
from dataclasses import dataclass

from wallabag2readwise.models import (
WallabagAnnotation,
Expand Down Expand Up @@ -173,3 +174,61 @@ def new_highlights(
note=item.text,
category='articles',
)


class ReadwiseReaderConnector:
def __init__(
self,
token: str,
):
self.token = token
self.url = 'https://readwise.io/api/v3'

@property
def _session(self) -> requests.Session:
session = requests.Session()
session.headers.update(
{
'Accept': 'application/json',
'Authorization': f'Token {self.token}',
}
)
return session

@on_exception(expo, RateLimitException, max_tries=8)
@sleep_and_retry
@limits(calls=20, period=60)
def _request(
self, method: str, endpoint: str, params: dict = {}, data: dict = {}
) -> requests.Response:
url = self.url + endpoint
logger.debug(f'Calling "{method}" on "{url}" with params: {params}')
response = self._session.request(method, url, params=params, json=data)
while response.status_code == 429:
seconds = int(response.headers['Retry-After'])
logger.warning(f'Rate limited by Readwise, retrying in {seconds} seconds')
sleep(seconds)
response = self._session.request(method, url, params=params, data=data)
response.raise_for_status()
return response

def get(self, endpoint: str, params: dict = {}) -> requests.Response:
logger.debug(f'Getting "{endpoint}" with params: {params}')
return self._request('GET', endpoint, params=params)

def post(self, endpoint: str, data: dict = {}) -> requests.Response:
url = self.url + endpoint
logger.debug(f'Posting "{url}" with data: {data}')
response = self._request('POST', endpoint, data=data)
response.raise_for_status()
return response

def create(self, url: str, saved_using: str = 'wallabag', tags: list[str] = []):
_ = self.post(
'/save/',
{
'url': url,
'saved_using': saved_using,
'tags': tags,
},
)

0 comments on commit ec275ae

Please sign in to comment.