Raymond Ferguson
Devendor Tech
Published in
3 min readAug 10, 2018

--

Medium Command Line Client

Medium Command Line Client

medium_cli.py is command line client for interacting with medium.com.

Summary

Medium command line client allows you to push html, markdown, or restructured text files to medium.com

HTML rendering is fairly limited by supported tags at medium, but the cli at least preserves line breaks in code/pre tags.

Markdown is probably the most reliable since it is also very limited in what is supported.

Restructured text is handled by munging out some unsupported tags and mangling things like ordered lists that allow multi-part list items in rst, but have no counterpart in medium.

Installation

Install requirements and set executable bit. .. code-block:

user@myhost$ git clone https://github.com/devendor/medium_cli.git
user@myhost$ cd medium_cli
user@myhost$ pip install -r requirements.txt
user@myhost$ chmod +x medium_cli.py

Make an initial request to get the the config template (~/.medium) .. code-block:

user@myhost$ ./medium_cli.py
You must specify one of --list --user or file to post
Usage: Allows post of html, markdown, or rst files to medium.

usage: medium_cli.py [options] [file]

Options:
-h, --help show this help message and exit
-c CODE, --code=CODE code from redirect url after approval
-u, --user print user info
-l, --list-publications
list-publications
-t TITLE, --title=TITLE
article title
-a PUBLICATION, --authors=PUBLICATION
show contributor info for pub
-p PUB, --pub=PUB posts to publication
-r URL, --ref-url=URL
canonicalUrl. Ref if originally posted
elsewhere
-k, --keep-tmpfiles Keep /tmp/article.rst and /tmp/article.html
tmp files
when processing rst
user@myhost$ ./medium_cli.py -u
Error Config file not found: /home/rferguson/.medium

# Config file example
[medium]
client_id=supplied_when_registering_app
client_secret=supplied_when_registering_app
redirect_url=http://192.0.2.1/must_match_registered_url
state=canBeAnything

Traceback (most recent call last):
File "/home/rferguson/bin/medium_cli.py", line 73, in <module>
os.exit(1)
AttributeError: 'module' object has no attribute 'exit'

On medium.com settings, scroll down to developer applications, and register an application. You can pick any redirect_url.

Fill in a config file with the client_id, and client_secret returned from medium.com along with the redirect_url you used when requesting it.

user@myhost$ cat <<'EOF'>~/.medium
[medium]
client_id=supplied_when_registering_app
client_secret=supplied_when_registering_app
redirect_url=http://192.0.2.1/must_match_registered_url
state=eipaik2ieMei0iemoun1queik9leixae
EOF

Once you have a config, just make another request to get an authorization url. .. code-block:

user@myhost$ medium_cli.py -u
Authorized the app by following the url, and passing the code= value in the redirect url to --code to generate a new bearer token

https://medium.com/m/oauth/authorize?scope=...

Follow that url in a browser, click the authorize button on medium.com, and make note of the code=…. value in the url your are redirected to.

Make a request and provide that initial authorization code to receive a bearer token. .. code-block:

user@myhost$ medium_cli.py -c 1f127f985cfe -u
{
"username": "Ray.Ferguson",
"url": "https://medium.com/@Ray.Ferguson",
"imageUrl": "https://cdn-images-1...",
"id": "1ea052e3e51b23b17fbb...",
"name": "Raymond Ferguson"
}

Once a bearer token is established, it is stored and kept up to date in ~/.medium_bearer allowing you to use the cli without passing a new code.

user@myhost$ ./medium_cli.py -u
{
"username": "Ray.Ferguson",
"url": "https://medium.com/@Ray.Ferguson",
"imageUrl": "https://cdn-images-...",
"id": "1ea052e3e51b23b17fbbb0825cc6...",
"name": "Raymond Ferguson"
}

Usage Notes

To post to a publication, you need the publication id and permissions on the publication.

user@myhost$ ./medium_cli.py -l
[
{
"url": "https://medium.com/devendor-tech",
"imageUrl": "https://cdn-images-...",
"description": "Technical publications from Devendor Tech",
"id": "71cdf2d7072c",
"name": "Devendor Tech"
},
{
"url": "https://medium.com/free-code-camp",
"imageUrl": "https://cdn-images-1.medium.com/fit...",
"description": "Our community publishes stories ...",
"id": "336d898217ee",
"name": "freeCodeCamp"
}
]

Inbound file type is determined by extension.

*.md=markdown
*.rst=restructured text
*.*=anything else is assumed to be html

Example

me@home$ ./medium_cli.py -p 71cdf2d7072c \
-r
https://github.com/devendor/medium_cli \
-t “Medium Command Line Client” README.rst

posting to publication
{
“publicationId”: “71cdf2d7072c”,
“canonicalUrl”: “https://github.com/devendor/medium_cli",
“license”: “”,
“title”: “Medium Command Line Client”,
“url”: “https://medium.com/@Ray.Ferguson/5dc3940b5780",
“tags”: [],
“authorId”: “1ea052e3e51b23b17f...”,
“publishStatus”: “draft”,
“licenseUrl”: “https://medium.com/policy/9db0094a1e0f",
“id”: “5dc3940b5780”
}

Related Projects

  • medium-sdk-python provides the base of my inline class. It looks like they have several pull reauests that try to contribute additional features but nobodies rolling them in so I decided to embed my own.
  • medium-sdk-docs provides api information.

--

--