{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Working with the etherpad API\n", "\n", "We have a local etherpad running on the soupboat! We have access to the \"APIKEY\" (basically just a single password for all users to share that gives access to the [API](https://en.wikipedia.org/wiki/API), or Application Programming Interface. This API is available \"over HTTP\" or as a [\"REST service\"](https://en.wikipedia.org/wiki/Representational_state_transfer) ... basically this means that the API is functions that you call via loading specific URLs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using the etherpad HTTP api: \n", "https://etherpad.org/doc/v1.8.4/#index_http_api\n", "\n", "The pad API is here:\n", "\n", "Via this URL you can see what version of the API the pad supports...\n", "\n", "https://hub.xpub.nl/soupboat/pad/api/\n", "\n", "the version then goes into the URL:\n", "\n", "https://hub.xpub.nl/soupboat/pad/api/1.2.15/listAllPads\n", "\n", "aha, but you need to include the API KEY! (this is what prevents people from spamming your pad)\n", "\n", "https://hub.xpub.nl/soupboat/pad/api/1.2.15/listAllPads?apikey=1234567\n", "\n", "but that's not the good one, let's let python make a good URL:\n", "\n" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [], "source": [ "with open(\"/opt/etherpad/APIKEY.txt\") as f:\n", " api_key = f.read().strip()" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "029ff291f1256972b277789579ce5e29e83b6bbd52e319fe49af198c35013e9b\n" ] } ], "source": [ "print(api_key)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**It's important this APIKEY is never made public,** if it's published then anyone has access to the API, and can (for instance) write a script to delete all pads, or (perhaps more likely) create random pads with links (how search engines are manipulated). For this reason, it's not advisable (though it's technically possible) to call the API directly from javascript) ... Need to think of a workaround.\n" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [], "source": [ "api_url = \"https://hub.xpub.nl/soupboat/pad/api/1.2.15/\"" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "u = f\"{api_url}listAllPads?apikey={api_key}\"" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "https://hub.xpub.nl/soupboat/pad/api/1.2.15/listAllPads?apikey=029ff291f1256972b277789579ce5e29e83b6bbd52e319fe49af198c35013e9b\n" ] } ], "source": [ "print (u)" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [], "source": [ "from urllib.request import urlopen\n", "from urllib.parse import urlencode\n", "import json" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'code': 0, 'message': 'ok', 'data': {'padIDs': ['???', 'CcnxKtsc6MB14S964ZHI', 'Japoco', 'XNf5qq9A85k3S1kpvh2I', 'ahahahahah', 'api', 'blibli', \"don't_see_this_please\", 'garside', 'helloXPUBIES', 'hellohellohellocheckcheck', 'input', 'input_etc', 'isitworking', 'mbm2LakBJiPBIc9t5Pqy', 'meh', 'nameofapad', 'newest_padddd', 'noooooooooo', 'output', 'output_etc', 'paddy', 'test', 'test123', 'this-is-a-new-pad-eheh', 'wooow', 'xpubpizza', 'yeeeees', 'yesssss']}}\n" ] } ], "source": [ "# the steps\n", "data = {}\n", "data['apikey'] = api_key\n", "# turn dict into url-encoded string, e.g. {\"text: \"hey there\"} becomes 'text=hey%20there'\n", "data = urlencode(data)\n", "# turn the str into bytes (urlopen requires this)\n", "data = data.encode()\n", "# use urlopen to contact the server, using data makes it a POST\n", "resp = urlopen(f\"{api_url}listAllPads\", data=data)\n", "# json load can deal with any \"file-like\" object like the response from urlopen\n", "data = json.load(resp)\n", "print (data)" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'code': 0, 'message': 'ok', 'data': {'padIDs': ['???', 'CcnxKtsc6MB14S964ZHI', 'Japoco', 'XNf5qq9A85k3S1kpvh2I', 'ahahahahah', 'api', 'blibli', \"don't_see_this_please\", 'garside', 'helloXPUBIES', 'hellohellohellocheckcheck', 'input', 'input_etc', 'isitworking', 'mbm2LakBJiPBIc9t5Pqy', 'meh', 'nameofapad', 'newest_padddd', 'noooooooooo', 'output', 'output_etc', 'paddy', 'test', 'test123', 'this-is-a-new-pad-eheh', 'wooow', 'xpubpizza', 'yeeeees', 'yesssss']}}\n" ] } ], "source": [ "# do all the steps above as a one-liner (no intermediate variables)\n", "data = json.load(urlopen(f\"{api_url}listAllPads\", data=urlencode({'apikey': api_key}).encode()))\n", "print (data)" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [], "source": [ "# wrap in a convenient function\n", "def ep (api_fn, api_url, api_key, **data):\n", " data['apikey'] = api_key\n", " return json.load(urlopen(f\"{api_url}{api_fn}\", data=urlencode(data).encode()))" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'code': 0,\n", " 'message': 'ok',\n", " 'data': {'padIDs': ['???',\n", " 'CcnxKtsc6MB14S964ZHI',\n", " 'Japoco',\n", " 'XNf5qq9A85k3S1kpvh2I',\n", " 'ahahahahah',\n", " 'api',\n", " 'blibli',\n", " \"don't_see_this_please\",\n", " 'garside',\n", " 'helloXPUBIES',\n", " 'hellohellohellocheckcheck',\n", " 'input',\n", " 'input_etc',\n", " 'isitworking',\n", " 'mbm2LakBJiPBIc9t5Pqy',\n", " 'meh',\n", " 'nameofapad',\n", " 'newest_padddd',\n", " 'noooooooooo',\n", " 'output',\n", " 'output_etc',\n", " 'paddy',\n", " 'test',\n", " 'test123',\n", " 'this-is-a-new-pad-eheh',\n", " 'wooow',\n", " 'xpubpizza',\n", " 'yeeeees',\n", " 'yesssss']}}" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ep(\"listAllPads\", api_url, api_key)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A really useful API call is [appendText](https://etherpad.org/doc/v1.8.4/#index_appendtext_padid_text), that let's you add text to the end of an (already existing) pad.\n" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [], "source": [ "for iteration in range(10):\n", " ep(\"appendText\", api_url, api_key, padID=\"test\", text=\"YOU ARE NOT ALONE\\n\")" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'code': 1, 'message': 'padID does already exist', 'data': None}" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ep(\"createPad\", api_url, api_key, padID=\"test\")" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'code': 0,\n", " 'message': 'ok',\n", " 'data': {'text': \"Welcome to Etherpad!\\n\\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\\n\\nGet involved with Etherpad at https://etherpad.org\\nHello I'm Matin\\nhi\\n hi\\nPizza\\nHi Naami\\nThis is your computer taking over\\nYOU ARE NOT ALONE\\nYOU ARE NOT ALONE\\nYOU ARE NOT ALONE\\nYOU ARE NOT ALONE\\nYOU ARE NOT ALONE\\nYOU ARE NOT ALONE\\nYOU ARE NOT ALONE\\nYOU ARE NOT ALONE\\nYOU ARE NOT ALONE\\nYOU ARE NOT ALONE\\nHiHi\\n\"}}" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ep(\"getText\", api_url, api_key, padID=\"test\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "https://hub.xpub.nl/soupboat/pad/p/test" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from time import sleep\n", "for i in range(5):\n", " sleep(5)\n", " ep(\"appendText\", api_url, api_key, padID=\"test\", text=\"Hello from python\\n\")" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'code': 0, 'message': 'ok', 'data': None}" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ep(\"setText\", api_url, api_key, padID=\"test\", text=\"YOU ARE REALLY NOT ALONE\\n\")" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'code': 0, 'message': 'ok', 'data': None}" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ep(\"deletePad\", api_url, api_key, padID=\"test\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Making a pad chatbot" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [], "source": [ "padtext = ep(\"getText\", api_url, api_key, padID=\"test\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from time import sleep\n", "\n", "while True:\n", " print('hey')\n", " sleep(5)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [], "source": [ "import pypandoc" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "
\n", " \n", " \n", " \n", "my string that i want to convert to html
\n", "\n", "\n", "\n" ] } ], "source": [ "output = pypandoc.convert_text(\n", " 'my string that i want to convert to html', \n", " 'html', \n", " format='md', \n", " extra_args=['--standalone', '--css=style.css']\n", ")\n", "print(output)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 5 }