expanding section 6, adding botsin.space screenshots, edited the bot-example
This commit is contained in:
parent
73e2839f3c
commit
2c958be0a8
@ -1,9 +1,9 @@
|
||||
Title: Introduction - Digital Infrapuncture
|
||||
Slug: 01-s1-introduction-digital-infrapuncture
|
||||
Date: 2020-11-01 12:00
|
||||
Summary: "Digital infrapuncture" is a speculative term that can help reframe the perception of the stress points that an infrastructure could have.
|
||||
Summary: *Digital infrapuncture* is a speculative term that can help reframe the perception of the stress points that an infrastructure could have.
|
||||
|
||||
"Digital infrapuncture" is a speculative term that can help reframe the perception of the stress points that an infrastructure could have. In a talk she presented in 2016 called *Identifying the point of it all: Towards a Model of "Digital Infrapuncture"*[^DigitalInfrapuncture], Verhoeven develops this concept in relation to the field of digital humanities.
|
||||
*Digital infrapuncture* is a speculative term that can help reframe the perception of the stress points that an infrastructure could have. In a talk she presented in 2016 called *Identifying the point of it all: Towards a Model of "Digital Infrapuncture"*[^DigitalInfrapuncture], Verhoeven develops this concept in relation to the field of digital humanities.
|
||||
|
||||
Informed by the work of scholar Nowviskie (Nowviskie 2015)[^Nowviskie], Verhoeven asks for a rethinking of digital infrastructures in terms of capacity and care, by *"developing an appreciation for where it hurts, where the sense of pain is in the worlds that we inhabit and study"* and creating small scale interventions which can enkindle transformation on a larger scale.
|
||||
|
||||
|
@ -1,148 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Defaulting to user installation because normal site-packages is not writeable\n",
|
||||
"Requirement already satisfied: Mastodon.py in /home/mb/.local/lib/python3.7/site-packages (1.5.1)\n",
|
||||
"Requirement already satisfied: python-magic in /usr/local/lib/python3.7/dist-packages (from Mastodon.py) (0.4.15)\n",
|
||||
"Requirement already satisfied: pytz in /home/mb/.local/lib/python3.7/site-packages (from Mastodon.py) (2020.1)\n",
|
||||
"Requirement already satisfied: python-dateutil in /home/mb/.local/lib/python3.7/site-packages (from Mastodon.py) (2.8.1)\n",
|
||||
"Requirement already satisfied: blurhash>=1.1.4 in /home/mb/.local/lib/python3.7/site-packages (from Mastodon.py) (1.1.4)\n",
|
||||
"Requirement already satisfied: requests>=2.4.2 in /usr/lib/python3/dist-packages (from Mastodon.py) (2.21.0)\n",
|
||||
"Requirement already satisfied: decorator>=4.0.0 in /usr/lib/python3/dist-packages (from Mastodon.py) (4.3.0)\n",
|
||||
"Requirement already satisfied: six in /usr/lib/python3/dist-packages (from Mastodon.py) (1.12.0)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"!{sys.executable} -m pip install Mastodon.py"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from mastodon import Mastodon"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Register your app! This only needs to be done once. Uncomment the code and substitute in your information.\n",
|
||||
"\n",
|
||||
"# Mastodon.create_app(\n",
|
||||
"# 'bagofwords',\n",
|
||||
"# api_base_url = 'https://botsin.space',\n",
|
||||
"# to_file = 'mastodon-bot.secret'\n",
|
||||
"#)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'9goIgIuH7OOdJJP5CzBgNQ0s8_ZoGuK1_8hA1h3dTxk'"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Then login. This can be done every time, or use persisted.\n",
|
||||
"\n",
|
||||
"mastodon = Mastodon(\n",
|
||||
" client_id = 'mastodon-bot.secret',\n",
|
||||
" api_base_url = 'https://botsin.space'\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"mastodon.log_in(\n",
|
||||
" 'mail@manettaberends.nl',\n",
|
||||
" 'language',\n",
|
||||
" to_file = 'mastodon-bot-usercred.secret'\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "MastodonAPIError",
|
||||
"evalue": "('Mastodon API returned error', 403, 'Forbidden', 'Your login is missing a confirmed e-mail address')",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[0;31mMastodonAPIError\u001b[0m Traceback (most recent call last)",
|
||||
"\u001b[0;32m<ipython-input-8-4d950a9b40ce>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mapi_base_url\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'https://botsin.space'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m )\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0mmastodon\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtoot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Testing to toot for the very very first time, excitingly using #mastodonpy !'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
|
||||
"\u001b[0;32m<decorator-gen-186>\u001b[0m in \u001b[0;36mtoot\u001b[0;34m(self, status)\u001b[0m\n",
|
||||
"\u001b[0;32m~/.local/lib/python3.7/site-packages/mastodon/Mastodon.py\u001b[0m in \u001b[0;36mwrapper\u001b[0;34m(function, self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 100\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mmajor\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmastodon_major\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mminor\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmastodon_minor\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mpatch\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmastodon_patch\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 101\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mMastodonVersionError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Version check failed (Need version \"\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mversion\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\", patch is \"\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmastodon_patch\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\")\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 102\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfunction\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 103\u001b[0m \u001b[0mfunction\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__doc__\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfunction\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__doc__\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\"\\n\\n *Added: Mastodon v\"\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mcreated_ver\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\", last changed: Mastodon v\"\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mlast_changed_ver\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\"*\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 104\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdecorate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfunction\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwrapper\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
|
||||
"\u001b[0;32m~/.local/lib/python3.7/site-packages/mastodon/Mastodon.py\u001b[0m in \u001b[0;36mtoot\u001b[0;34m(self, status)\u001b[0m\n\u001b[1;32m 1785\u001b[0m \u001b[0mReturns\u001b[0m \u001b[0ma\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m`\u001b[0m\u001b[0mtoot\u001b[0m \u001b[0mdict\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0m_\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mthe\u001b[0m \u001b[0mnew\u001b[0m \u001b[0mstatus\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1786\u001b[0m \"\"\"\n\u001b[0;32m-> 1787\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstatus_post\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstatus\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1788\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1789\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0mapi_version\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"1.0.0\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"2.8.0\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m__DICT_VERSION_STATUS\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
|
||||
"\u001b[0;32m<decorator-gen-185>\u001b[0m in \u001b[0;36mstatus_post\u001b[0;34m(self, status, in_reply_to_id, media_ids, sensitive, visibility, spoiler_text, language, idempotency_key, content_type, scheduled_at, poll, quote_id)\u001b[0m\n",
|
||||
"\u001b[0;32m~/.local/lib/python3.7/site-packages/mastodon/Mastodon.py\u001b[0m in \u001b[0;36mwrapper\u001b[0;34m(function, self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 100\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mmajor\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmastodon_major\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mminor\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmastodon_minor\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mpatch\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmastodon_patch\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 101\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mMastodonVersionError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Version check failed (Need version \"\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mversion\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\", patch is \"\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmastodon_patch\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\")\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 102\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfunction\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 103\u001b[0m \u001b[0mfunction\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__doc__\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfunction\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__doc__\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\"\\n\\n *Added: Mastodon v\"\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mcreated_ver\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\", last changed: Mastodon v\"\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mlast_changed_ver\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\"*\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 104\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdecorate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfunction\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwrapper\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
|
||||
"\u001b[0;32m~/.local/lib/python3.7/site-packages/mastodon/Mastodon.py\u001b[0m in \u001b[0;36mstatus_post\u001b[0;34m(self, status, in_reply_to_id, media_ids, sensitive, visibility, spoiler_text, language, idempotency_key, content_type, scheduled_at, poll, quote_id)\u001b[0m\n\u001b[1;32m 1774\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1775\u001b[0m \u001b[0mparams\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__generate_params\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mparams_initial\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m'idempotency_key'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1776\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__api_request\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'POST'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'/api/v1/statuses'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mheaders\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mheaders\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0muse_json\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0muse_json\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1777\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1778\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0mapi_version\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"1.0.0\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"2.8.0\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m__DICT_VERSION_STATUS\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
|
||||
"\u001b[0;32m~/.local/lib/python3.7/site-packages/mastodon/Mastodon.py\u001b[0m in \u001b[0;36m__api_request\u001b[0;34m(self, method, endpoint, params, files, headers, access_token_override, base_url_override, do_ratelimiting, use_json, parse)\u001b[0m\n\u001b[1;32m 3427\u001b[0m \u001b[0mresponse_object\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstatus_code\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3428\u001b[0m \u001b[0mresponse_object\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreason\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 3429\u001b[0;31m error_msg)\n\u001b[0m\u001b[1;32m 3430\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3431\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mparse\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
|
||||
"\u001b[0;31mMastodonAPIError\u001b[0m: ('Mastodon API returned error', 403, 'Forbidden', 'Your login is missing a confirmed e-mail address')"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# To post, create an actual API instance.\n",
|
||||
"\n",
|
||||
"mastodon = Mastodon(\n",
|
||||
" access_token = 'mastodon-bot-usercred.secret',\n",
|
||||
" api_base_url = 'https://botsin.space'\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"mastodon.toot('Testing to toot for the very very first time, excitingly using #mastodonpy !')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": 4
|
||||
}
|
@ -1,213 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Defaulting to user installation because normal site-packages is not writeable\n",
|
||||
"Requirement already satisfied: Mastodon.py in /home/mb/.local/lib/python3.7/site-packages (1.5.1)\n",
|
||||
"Requirement already satisfied: python-magic in /usr/local/lib/python3.7/dist-packages (from Mastodon.py) (0.4.15)\n",
|
||||
"Requirement already satisfied: pytz in /home/mb/.local/lib/python3.7/site-packages (from Mastodon.py) (2020.1)\n",
|
||||
"Requirement already satisfied: python-dateutil in /home/mb/.local/lib/python3.7/site-packages (from Mastodon.py) (2.8.1)\n",
|
||||
"Requirement already satisfied: blurhash>=1.1.4 in /home/mb/.local/lib/python3.7/site-packages (from Mastodon.py) (1.1.4)\n",
|
||||
"Requirement already satisfied: requests>=2.4.2 in /usr/lib/python3/dist-packages (from Mastodon.py) (2.21.0)\n",
|
||||
"Requirement already satisfied: decorator>=4.0.0 in /usr/lib/python3/dist-packages (from Mastodon.py) (4.3.0)\n",
|
||||
"Requirement already satisfied: six in /usr/lib/python3/dist-packages (from Mastodon.py) (1.12.0)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"!{sys.executable} -m pip install Mastodon.py"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from mastodon import Mastodon"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"instance = 'INSERTYOURINSTANCEURLHERE'\n",
|
||||
"username = 'INSERTEMAILADDRESSHERE'\n",
|
||||
"password = 'INSERTPASSWORDSHERE'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"('ijslOh1kdBmHnlycZUhP6bpB0wXayAXqZ_Qr4DMB09Y',\n",
|
||||
" '-TZBLBW2XOrwOoCb2mbIWdxALRS8QiHIgIosrq2PqsE')"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Register your app! This only needs to be done once. Uncomment the code and substitute in your information.\n",
|
||||
"\n",
|
||||
"Mastodon.create_app(\n",
|
||||
" 'bots-as-infrapuncture',\n",
|
||||
" api_base_url = instance,\n",
|
||||
" to_file = 'mastodon-bot.secret'\n",
|
||||
")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'497bMScwM_Z1goP0Q_lNUsmbmbYByw3XXb-6KBxt-K8'"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Then login. This can be done every time, or use persisted.\n",
|
||||
"\n",
|
||||
"mastodon = Mastodon(\n",
|
||||
" client_id = 'mastodon-bot.secret',\n",
|
||||
" api_base_url = instance\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"mastodon.log_in(\n",
|
||||
" username,\n",
|
||||
" password,\n",
|
||||
" to_file = 'mastodon-bot-usercred.secret'\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'id': 104964779309814302,\n",
|
||||
" 'created_at': datetime.datetime(2020, 10, 2, 10, 43, 46, 538000, tzinfo=tzutc()),\n",
|
||||
" 'in_reply_to_id': None,\n",
|
||||
" 'in_reply_to_account_id': None,\n",
|
||||
" 'sensitive': False,\n",
|
||||
" 'spoiler_text': '',\n",
|
||||
" 'visibility': 'public',\n",
|
||||
" 'language': 'en',\n",
|
||||
" 'uri': 'https://post.lurk.org/users/manetta/statuses/104964779309814302',\n",
|
||||
" 'url': 'https://post.lurk.org/@manetta/104964779309814302',\n",
|
||||
" 'replies_count': 0,\n",
|
||||
" 'reblogs_count': 0,\n",
|
||||
" 'favourites_count': 0,\n",
|
||||
" 'local_only': False,\n",
|
||||
" 'activity_pub_type': '',\n",
|
||||
" 'favourited': False,\n",
|
||||
" 'reblogged': False,\n",
|
||||
" 'muted': False,\n",
|
||||
" 'bookmarked': False,\n",
|
||||
" 'pinned': False,\n",
|
||||
" 'content': '<p>Testing to toot for the very very first time, excitingly using <a href=\"https://post.lurk.org/tags/mastodonpy\" class=\"mention hashtag\" rel=\"tag\">#<span>mastodonpy</span></a> !</p>',\n",
|
||||
" 'reblog': None,\n",
|
||||
" 'application': {'name': 'bagofwords', 'website': None},\n",
|
||||
" 'account': {'id': 4791,\n",
|
||||
" 'username': 'manetta',\n",
|
||||
" 'acct': 'manetta',\n",
|
||||
" 'display_name': 'manetta',\n",
|
||||
" 'locked': False,\n",
|
||||
" 'bot': False,\n",
|
||||
" 'discoverable': False,\n",
|
||||
" 'group': False,\n",
|
||||
" 'created_at': datetime.datetime(2018, 5, 2, 18, 32, 50, 698000, tzinfo=tzutc()),\n",
|
||||
" 'note': '<p>software studies networks publishing (<a href=\"https://post.lurk.org/tags/nobot\" class=\"mention hashtag\" rel=\"tag\">#<span>nobot</span></a>)</p>',\n",
|
||||
" 'url': 'https://post.lurk.org/@manetta',\n",
|
||||
" 'avatar': 'https://post.lurk.org/system/accounts/avatars/000/004/791/original/9ce0ebe273ada8aa.jpg?1584822874',\n",
|
||||
" 'avatar_static': 'https://post.lurk.org/system/accounts/avatars/000/004/791/original/9ce0ebe273ada8aa.jpg?1584822874',\n",
|
||||
" 'header': 'https://post.lurk.org/system/accounts/headers/000/004/791/original/5e99159ef049b6dc.png?1584823076',\n",
|
||||
" 'header_static': 'https://post.lurk.org/system/accounts/headers/000/004/791/original/5e99159ef049b6dc.png?1584823076',\n",
|
||||
" 'followers_count': 134,\n",
|
||||
" 'following_count': 175,\n",
|
||||
" 'statuses_count': 154,\n",
|
||||
" 'last_status_at': datetime.datetime(2020, 10, 2, 0, 0),\n",
|
||||
" 'emojis': [],\n",
|
||||
" 'fields': []},\n",
|
||||
" 'media_attachments': [],\n",
|
||||
" 'mentions': [],\n",
|
||||
" 'tags': [{'name': 'mastodonpy',\n",
|
||||
" 'url': 'https://post.lurk.org/tags/mastodonpy'}],\n",
|
||||
" 'emojis': [],\n",
|
||||
" 'card': None,\n",
|
||||
" 'poll': None}"
|
||||
]
|
||||
},
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# To post, create an actual API instance.\n",
|
||||
"\n",
|
||||
"mastodon = Mastodon(\n",
|
||||
" access_token = 'mastodon-bot-usercred.secret',\n",
|
||||
" api_base_url = instance\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"mastodon.toot('Testing to toot for the very very first time, excitingly using #mastodonpy !')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": 4
|
||||
}
|
@ -1,249 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Making a Twitter Bot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This Jupyter Notebooks shows the basics of making a Twitter bot with Python.\n",
|
||||
"\n",
|
||||
"This is a *very* heavily modified version of a [tutorial posted on the CUNY Academic Commons Blog](https://emerging.commons.gc.cuny.edu/2013/10/making-twitter-bot-python-tutorial/) by [Robin Camilla Davis](https://twitter.com/robincamille). I have taken some of the content and structure of the static blog post and ported it into a Jupyter Notebook Python environment. \n",
|
||||
"\n",
|
||||
"Here is Robin's introduction:\n",
|
||||
"\n",
|
||||
"> If there’s one thing this budding computational linguist finds delightful, it’s computers that talk to us. From [SmarterChild](http://en.wikipedia.org/wiki/SmarterChild) to [horse_ebooks](http://www.robincamille.com/2013-09-25-horse_ebooks/) to [Beetlejuice](http://muffinlabs.com/content/twitter-bot-info.html), I love the weirdness of machines that seem to have a voice, especially when it’s a Twitter bot that adds its murmur to a tweetstream of accounts mostly run by other humans.\n",
|
||||
"\n",
|
||||
"> As fun midnight project a few weeks ago, I cobbled together [@MechanicalPoe](https://twitter.com/MechanicalPoe), a Twitter bot that tweets Poe works line by line on the hour from a long .txt file. This slow-tweeting of text is by no means new—[@SlowDante](https://twitter.com/slowdante) is pretty popular, and so is[@CDarwin](http://www.metaburbia.com/darwin/), among many others. In case you want to make your own, here are the quick ‘n’ easy steps I took. This is just one way of doing it—shop around and see what others have done, too.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Contents\n",
|
||||
"\n",
|
||||
"* [Steps Towards an Ecology of Bots](#Steps-Towards-an-Ecology-of-Bots)\n",
|
||||
"* [Building a Basic Bot](#Building-a-Basic-Bot)\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Steps Towards an Ecology of Bots"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Creating a Twitter bot means creating a Twitter account AND a Twitter app. The account will belong to the bot and the app will be the mechanism you use to access the Twitter API and control the bot **WITH PYTHON.**\n",
|
||||
"\n",
|
||||
"Creating a Twitter bot is somewhat complicated because of quirks in the way Twitter manages verification. If you don’t already have a Twitter account, the process of creating a Twitter bot is pretty painless. If you already have a Twitter account, you have to do the *Twitter verification dance*. Managing more than one Twitter account is a huge *pain in the ass* because you need a unique email address and mobile phone number for each account. \n",
|
||||
"\n",
|
||||
"**Step 0)** *Note: This step is only for people who already have Twitter accounts.* Log into Twitter and visit [your account's mobile devices settings page](https://twitter.com/settings/devices). If you have a phone number associated with that account click “Delete my phone” to remove your mobile from number from this account. You are going to need that phone number for creating your bot account. Alternatively, you can get a free phone number from [Google Voice](http://voice.google.com) and use that for all the mobile phone verification BS.\n",
|
||||
"\n",
|
||||
"You are also going to need another email address for your new Twitter account. Twitter won’t let you create multiple accounts with the same email address. If you have a 2nd email address you’ll need to use it in step 1 below. If you don’t have another email address it is easy to [create one with gmail](https://accounts.google.com/signup). Alternatively, you can fake a new unique email address with Gmail by adding a `+` and something after your username. For example: `myemail+sometext@gmail.com`. Any messages addressed to this email will still show up in your inbox, but it behaves like a different email address. Give it a try!\n",
|
||||
"\n",
|
||||
"**Step 1)** Create a new Twitter account by visiting the [Twitter signup page](https://twitter.com/signup). This will be the Twitter account for your bot, so pick your username wisely. You will need to pick a name that hasn’t already been selected. Twitter *should* ask you for a mobile phone verification when you are creating the account, if it doesn’t you’ll have to add the phone number to your profile in [the new account's mobile devices settings page](https://twitter.com/settings/devices).\n",
|
||||
"\n",
|
||||
"**Step 2)** OK. Now that you have a Twitter **account** for your bot, you need to create a Twitter **application** so you can access the Twitter API **WITH PYTHON**. Twitter doesn’t allow people to connect to their API willy-nilly. Creating applications is Twitter's way of tracking (and controlling) who is programmatically accessing their network and data. In the world of big social media sites, heavily regulated access to APIs, networks, and data is a fact of life. \n",
|
||||
"\n",
|
||||
"Fortunately, creating an application on Twitter is very easy. Visit Twitter’s [Application Management page](https://apps.twitter.com/) and click the “Create New App” button. You’ll be shown the form shown below. Pick a name for your application and a description, this information is just for you it should be named something different (and more descriptive) than the account you just created. For the website, put the name of the course `http://annettevee.com/2015fall_computationalmedia/` into the website field.\n",
|
||||
"\n",
|
||||
"![New App information](images/create-application.png)\n",
|
||||
"\n",
|
||||
"**Step 3)** The last step for this part of the Twitter bot making process is to create the Access Token so your Python code can access the API. With your app created, you should see a dashboard page will all kinds of technical jargon about your Twitter app. You can ignore most of it for now and instead focus on the tab that says “Keys and Access Tokens.” The keys and access tokens behave like passwords so your computer can talk to Twitter’s computer. Twitter uses a protocol called [OAuth](http://oauth.net) for [“providing authorized access to its API](https://dev.twitter.com/oauth). You don’t really need to wor\n",
|
||||
"\n",
|
||||
"- Click the \"Keys and Access Tokens” tab\n",
|
||||
"- Click the “Create my access token button” at the bottom of the page.\n",
|
||||
"\n",
|
||||
"Once you have created your access tokens there will be **four** pieces of information you will need to put into your Python script so it can access the Twitter API:\n",
|
||||
"\n",
|
||||
"1. Consumer Key\n",
|
||||
"2. Consumer Secret\n",
|
||||
"3. Access Token\n",
|
||||
"4. Access Token Secret\n",
|
||||
"\n",
|
||||
"![Keys and Access Tokens Dashboard](images/access-keys.png)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Some other things to consider:\n",
|
||||
"\n",
|
||||
"- When you create a Twitter App you agree to the [developer agreement and policy](https://dev.twitter.com/overview/terms/agreement-and-policy) which has some complete bullshit within it. Basically, Twitter reserves the right to lock down your application and your account if their machine learning algorithms detect what they think is bad behavior.\n",
|
||||
"- Creating to many Accounts can lock-out your mobile phone number and prevent you from creating new accounts or apps. This comes from **personal experience** and there is NO INDICATION if and when that happens. AND Twitter support is TOTALLY WORTHLESS. This is when creating another cell phone number with Google Voice is helpful.\n",
|
||||
"- You are not alone in thinking this is a HUGE PAIN IN THE ASS.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Building a Basic Bot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"If you skim the code below, you'll notice *there isn't a lot of code* that is because a bulk of the work is being done by a Python library called [Tweepy](http://tweepy.readthedocs.org/en/latest/). Tweepy handles all the annoying plumbing work involved with connecting to Twitter and accessing specific functions like authentication, search or updating your status. You could access the Twitter API directly by issuing raw HTTP request to Twitters [REST endpoints](https://dev.twitter.com/rest/public), but why do that when someone has already a nice and easy to use interface to Twitter?\n",
|
||||
"\n",
|
||||
"The code below is for a very simple Twitter bot that just Tweets lines of Edgar Allen Poe's poetry. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Boilerplate"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Import the python libraries we need to \n",
|
||||
"import time\n",
|
||||
"import tweepy\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Be sure to paste your keys and tokens in here because it won't work otherwise!\n",
|
||||
"CONSUMER_KEY = \"INSERT KEY HERE\"\n",
|
||||
"CONSUMER_SECRET = \"INSERT SECRET HERE\"\n",
|
||||
"ACCESS_KEY = \"INSERT ACCESS KEY\"\n",
|
||||
"ACCESS_SECRET = \"INSERT SECRET HERE\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### The Oauth Dance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This is the code the initates a connection to Twitter via the API using the Keys and Access Tokens we created earlier. Once we've gotten through the \"Oauth Dance\" we'll have an object, `the_twitter_api` that we can use to programmatically perform actions on Twitter. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Send our keys and tokens to Twitter\n",
|
||||
"credentials = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)\n",
|
||||
"credentials.set_access_token(ACCESS_KEY, ACCESS_SECRET)\n",
|
||||
"\n",
|
||||
"# Authenticate with Twitter to get access\n",
|
||||
"the_twitter_api = tweepy.API(credentials)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Tweeting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We are going to make a bot that tweets lines from classic texts. \n",
|
||||
"\n",
|
||||
"> I chose some texts from [Project Gutenberg](http://www.gutenberg.org/) and copied them into separate .txt files. (Maybe don’t choose a long-winded writer.) I ran a script over them to split them up by sentence and mark sentences longer than 140 characters. ([Link to chunking script.](https://github.com/robincamille/kicks-and-giggles/blob/master/poe-twitterbot-chunker.py)) There are other scripts to break up long sentences intelligently, but I wanted to exert some editorial control over where the splits occurred in the texts, so the script I wrote writes ‘SPLIT’ next to long sentences to alert me as I went over the ~600 lines by hand. I copied my chunked texts into one .txt file and marked the beginnings and ends of each individual text. ([Link to the finalized .txt file.](https://github.com/robincamille/kicks-and-giggles/blob/master/poe-lines.txt))\n",
|
||||
"\n",
|
||||
"So I am going to cheat a little bit here and just use the data files Robin already prepared. I downloaded Robin's pre-prepared text into a file in the `data/` directory.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# open Robin's Edgar Allen Poe data file and read every line into memory\n",
|
||||
"with open('data/thats-so-raven.txt','r') as filename:\n",
|
||||
" lines = filename.readlines()\n",
|
||||
"\n",
|
||||
"# Tweet each line, then wait one minute and tweet another.\n",
|
||||
"# Note: this design means the bot runs continuously\n",
|
||||
"for line in lines: \n",
|
||||
" the_twitter_api.update_status(status=line)\n",
|
||||
" print(line)\n",
|
||||
" time.sleep(60) # Sleep for 1 minute"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"That's it you're done! That's all it takes to create a very simple Twitter bot."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Next Steps"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Here are some other bot examples to play with:\n",
|
||||
"* Copora Tutorial Bot - A bot that uses [Corpora](https://github.com/dariusk/corpora) to generate text to tweet. [TODO] \n",
|
||||
"* Mayor of Pittsburgh Bot - A bot that translates the Mayor's tweets into Pittsburghese. [TODO]\n",
|
||||
"\n",
|
||||
"To make your own bot, you can start with this [python code for a very minimal bot](minimal-bot.ipynb) and modify it to cause whatever havoc or happiness you'd like.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### Deploying you Botnet\n",
|
||||
"\n",
|
||||
"Making a bot is the easy part, making it run all the time requires a server that can continually run the bots. We have been creating and modifying these bots in Jupyter Notebooks which are fun and easy to use, but not great for deploying bots in the wild.\n",
|
||||
"\n",
|
||||
"If you have a server, the best way to deploy a bot you've written in a notebook is to save the Notebook as a python script (File -> Download As -> Python (.py) and run the scrip or set up a [CRON JOB](http://www.unixgeeks.org/security/newbie/unix/cron-1.html) to execute the python script periodically (if you've written the script to just execute one and quit rather than run continuously)."
|
||||
]
|
||||
}
|
||||
],
|
||||
"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.4.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
@ -3,14 +3,14 @@ Slug: 01-s6-introduction
|
||||
Date: 2020-11-01 12:00
|
||||
Summary: Start of the excercise.
|
||||
|
||||
Describe the scene in a few sentences:
|
||||
In this section we will make and run a bot!
|
||||
|
||||
* What is the platform?
|
||||
* Who is the group?
|
||||
* What is the role of the bot?
|
||||
First we will look into the materiality of a bot: How is it operating? What code is needed to make one? And how does it connect to an infrastructure, both in a technical and dialogical way?
|
||||
|
||||
Describe the characters that take part of the conversation in one line.
|
||||
To dive into the computational side of making bots, we will discuss a couple of basic features of programming logic, such as loops, if/else statements and variabled. It will help us to get to know the language and mechanisms in which these automated agents are written.
|
||||
|
||||
Start writing a dialogue script.
|
||||
We will look at the code of a bot by XXX, to study how other people operate them.
|
||||
|
||||
You can use the following document to depart from: [insert link to document here].
|
||||
Lastly, we will finish this module by running a bot ourselves.
|
||||
|
||||
Ready? :-)
|
@ -1,16 +0,0 @@
|
||||
Title: Bot example
|
||||
Slug: 02-s6-bot-example
|
||||
Date: 2020-11-01 12:01
|
||||
Summary: Continuation of the excercise.
|
||||
|
||||
Describe the scene in a few sentences:
|
||||
|
||||
* What is the platform?
|
||||
* Who is the group?
|
||||
* What is the role of the bot?
|
||||
|
||||
Describe the characters that take part of the conversation in one line.
|
||||
|
||||
Start writing a dialogue script.
|
||||
|
||||
You can use the following document to depart from: [insert link to document here].
|
@ -0,0 +1,4 @@
|
||||
Title: Programming Logic
|
||||
Slug: 02-s6-step-2
|
||||
Date: 2020-11-01 12:01
|
||||
Summary: Loops, if/else statements, variables and more.
|
@ -0,0 +1,6 @@
|
||||
Title: A bot example
|
||||
Slug: 03-s6-step-3
|
||||
Date: 2020-11-01 12:01
|
||||
Summary: Exploring the code of XXX
|
||||
|
||||
|
@ -1,17 +0,0 @@
|
||||
Title: Situated code
|
||||
Slug: 03-s6-situated-code
|
||||
Date: 2020-11-01 12:02
|
||||
Summary: Last step of the excercise.
|
||||
|
||||
Describe the scene in a few sentences:
|
||||
|
||||
* What is the platform?
|
||||
* Who is the group?
|
||||
* What is the role of the bot?
|
||||
|
||||
Describe the characters that take part of the conversation in one line.
|
||||
|
||||
Start writing a dialogue script.
|
||||
|
||||
You can use the following document to depart from: [insert link to document here].
|
||||
|
@ -0,0 +1,24 @@
|
||||
Title: Make a bot
|
||||
Slug: 04-s6-step-4
|
||||
Date: 2020-11-01 12:04
|
||||
Summary: How to make a bot?
|
||||
|
||||
For this exercise we will make use of the digital infrastructure of the *Fediverse*. Darius Kazemi introduces this federated network in his video contribution ([*Watch it again here*](09-s3-question-5.html)).
|
||||
|
||||
The Fediverse has grown into a social media space that is currently used by more then 5 million people. In contrast to social media platforms as Twitter, Instagram or Facebook, it consists of a whole range of different *kind of* social media: blogs, micro-bloggin, photo-sharing, video-sharing, link-sharing, etc.
|
||||
|
||||
The federated structure of the Fediverse is an interesting infrastructure to work with in this exercise, in two ways.
|
||||
|
||||
The Fediverse is designed for all sorts of different kinds of *formats* of publishing. This choice for multiplicity is furthermore reflected in the way the Fediverse is governed: instead of being a top-down governed platform, it is a federated network of networks. Each network is being shaped by an owner. Which is sometimes an individual, sometimes a group of peers that share the same interest and other times by an organisation. The owner of a network decides about the rules: who can publish? What kind of material can be published?
|
||||
|
||||
To dive into bot making, we will that is specially prepared for this module. It is written in the programming language *Python* and is saved as a *Jupyter Notebooks*, which makes it possible to run this code from the web![^botexample]
|
||||
|
||||
Let's make a bot!
|
||||
|
||||
Please follow the link to proceed:
|
||||
|
||||
<https://mybinder.org/v2/git/https%3A%2F%2Fgit.vvvvvvaria.org%2Fmb%2Fbots-as-digital-infrapunctures/master?filepath=content%2Fbot-example%2Fmastodon-bot.ipynb>
|
||||
|
||||
# Footnotes
|
||||
|
||||
[^botexample]: The bot-example is kindly hosted by *MyBinder* (<https://mybinder.org>). [EXPAND]
|
@ -4,30 +4,28 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Mastodon-bot Example"
|
||||
"# Mastodon bot Example"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This Mastodon bot example is part of the module *Bots as Infrapuncture*. :-)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"(explain a bit how Jupyter notebooks work)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Defaulting to user installation because normal site-packages is not writeable\n",
|
||||
"Requirement already satisfied: Mastodon.py in /home/mb/.local/lib/python3.7/site-packages (1.5.1)\n",
|
||||
"Requirement already satisfied: six in /usr/lib/python3/dist-packages (from Mastodon.py) (1.12.0)\n",
|
||||
"Requirement already satisfied: requests>=2.4.2 in /usr/lib/python3/dist-packages (from Mastodon.py) (2.21.0)\n",
|
||||
"Requirement already satisfied: python-dateutil in /home/mb/.local/lib/python3.7/site-packages (from Mastodon.py) (2.8.1)\n",
|
||||
"Requirement already satisfied: blurhash>=1.1.4 in /home/mb/.local/lib/python3.7/site-packages (from Mastodon.py) (1.1.4)\n",
|
||||
"Requirement already satisfied: python-magic in /usr/local/lib/python3.7/dist-packages (from Mastodon.py) (0.4.15)\n",
|
||||
"Requirement already satisfied: decorator>=4.0.0 in /usr/lib/python3/dist-packages (from Mastodon.py) (4.3.0)\n",
|
||||
"Requirement already satisfied: pytz in /home/mb/.local/lib/python3.7/site-packages (from Mastodon.py) (2020.1)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"!{sys.executable} -m pip install Mastodon.py"
|
||||
@ -35,12 +33,12 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from mastodon import Mastodon\n",
|
||||
"# https://github.com/halcy/Mastodon.py"
|
||||
"from time import sleep\n",
|
||||
"from mastodon import Mastodon # https://github.com/halcy/Mastodon.py"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -49,24 +47,27 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"instance = 'INSERTYOURINSTANCEURLHERE'\n",
|
||||
"username = 'INSERTEMAILADDRESSHERE'\n",
|
||||
"password = 'INSERTPASSWORDSHERE'"
|
||||
"instance = 'https://botsin.space'\n",
|
||||
"username = 'info@varia.zone'\n",
|
||||
"password = 'INSERTPASSWORDHERE'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 20,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Register your app! This only needs to be done once. Uncomment the code and substitute in your information.\n",
|
||||
"# Register the bot as an application.\n",
|
||||
"\n",
|
||||
"Mastodon.create_app(\n",
|
||||
" 'bots-as-infrapuncture',\n",
|
||||
" api_base_url = instance,\n",
|
||||
" to_file = 'mastodon-bot.secret'\n",
|
||||
")\n"
|
||||
"# This only needs to be done once!\n",
|
||||
"# (we already did it :--))\n",
|
||||
"\n",
|
||||
"# Mastodon.create_app(\n",
|
||||
"# 'bots-as-infrapunctures',\n",
|
||||
"# api_base_url = instance,\n",
|
||||
"# to_file = 'mastodon-bot.secret'\n",
|
||||
"# )"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -75,7 +76,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Then login. This can be done every time, or use persisted.\n",
|
||||
"# First login.\n",
|
||||
"\n",
|
||||
"mastodon = Mastodon(\n",
|
||||
" client_id = 'mastodon-bot.secret',\n",
|
||||
@ -90,19 +91,10 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# To post, create an actual API instance.\n",
|
||||
"\n",
|
||||
"mastodon = Mastodon(\n",
|
||||
" access_token = 'mastodon-bot-usercred.secret',\n",
|
||||
" api_base_url = instance\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"mastodon.toot('Testing to toot for the very very first time, excitingly using #mastodonpy !')"
|
||||
"Now we will run the bot!!"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -110,7 +102,26 @@
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
"source": [
|
||||
"# Now let's run the bot!\n",
|
||||
"\n",
|
||||
"mastodon = Mastodon(\n",
|
||||
" access_token = 'mastodon-bot-usercred.secret',\n",
|
||||
" api_base_url = instance\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"toots = [\n",
|
||||
" 'Sentence 1',\n",
|
||||
" 'Sentence 2',\n",
|
||||
" 'Sentence 3',\n",
|
||||
" 'Sentence 4',\n",
|
||||
" 'Sentence 5'\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"for toot in toots:\n",
|
||||
" mastodon.toot(toot)\n",
|
||||
" sleep(300) # 5 minutes"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
BIN
content/images/botsin.space-intro-1.png
Normal file
BIN
content/images/botsin.space-intro-1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 77 KiB |
BIN
content/images/botsin.space-intro-2.png
Normal file
BIN
content/images/botsin.space-intro-2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 164 KiB |
BIN
content/images/botsin.space-intro-3.png
Normal file
BIN
content/images/botsin.space-intro-3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 101 KiB |
@ -2,7 +2,7 @@
|
||||
|
||||
body{
|
||||
margin:2em auto;
|
||||
max-width: 980px;
|
||||
max-width: 1100px;
|
||||
font-family: 'Basker';
|
||||
font-size: 16pt;
|
||||
line-height: 1.6;
|
||||
@ -28,17 +28,17 @@ nav#menu{
|
||||
margin:0 0 0 -10em;
|
||||
}
|
||||
nav#menu li{
|
||||
width: 200px;
|
||||
height:8em;
|
||||
margin:1em 0;
|
||||
padding:1em;
|
||||
font-size: 13pt;
|
||||
line-height: 1.4;
|
||||
background-color: #ec25ec;
|
||||
color:white;
|
||||
border-radius: 1em;
|
||||
margin:1em 0;
|
||||
padding:1em;
|
||||
list-style: none;
|
||||
display: inline-block;
|
||||
width: 225px;
|
||||
height:7em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
nav#menu li.active{
|
||||
@ -56,19 +56,32 @@ section#content{
|
||||
margin: 4em 0;
|
||||
}
|
||||
article{
|
||||
margin: 0 0 2em;
|
||||
margin: 3em 0 2em 0;
|
||||
padding: 2em 3em 0;
|
||||
border-top: 1px solid;
|
||||
}
|
||||
article:first-child{
|
||||
background-color: beige;
|
||||
padding:2em 3em;
|
||||
padding-bottom: 3em;
|
||||
margin-bottom: -3em;
|
||||
}
|
||||
article:first-child:before{
|
||||
content: "Start of Section";
|
||||
display: block;
|
||||
margin:0 auto;
|
||||
position: relative;
|
||||
font-style: italic;
|
||||
article button {
|
||||
width: auto;
|
||||
top:1em;
|
||||
margin:1.5em 0;
|
||||
padding:0.5em 1.5em;
|
||||
background-color: magenta;
|
||||
border:1px solid magenta;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
article button a{
|
||||
font-family: monospace;
|
||||
text-decoration: none !important;
|
||||
color: white !important;
|
||||
}
|
||||
article h2.page-title a{
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* big pink boxes for the section titles */
|
||||
h1.category{
|
||||
|
@ -5,12 +5,18 @@
|
||||
|
||||
<section>
|
||||
{% for article in articles_page.object_list|sort(attribute="slug")%}
|
||||
<article class="hentry">
|
||||
<article class="page">
|
||||
<header>
|
||||
<h2 class="entry-title"><a href="{{ SITEURL }}/{{ article.url }}" rel="bookmark" title="Permalink to {{ article.title|striptags }}">{{ article.title }}</a></h2>
|
||||
<h2 class="page-title">{{ article.title }}</h2>
|
||||
</header>
|
||||
<div class="entry-content"> {{ article.summary }} </div>
|
||||
<footer class="post-info"></footer>
|
||||
<footer class="page-footer">
|
||||
{% if loop.index == 1 %}
|
||||
<button><a href="{{ SITEURL }}/{{ article.url }}" title="Permalink to {{ article.title|striptags }}">Start this section</a></button>
|
||||
{% else %}
|
||||
<button><a href="{{ SITEURL }}/{{ article.url }}" title="Permalink to {{ article.title|striptags }}">Go to step {{ loop.index }}</a></button>
|
||||
{% endif %}
|
||||
</footer>
|
||||
</article></li>
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
Loading…
Reference in New Issue
Block a user