{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Mastodon bot Example" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This Mastodon bot example is part of the module *Bots as Digital Infrapunctures* done by Cristina Cochior and Manetta Berends in the proximity of [Varia](https://varia.zone/en/). The module is produced in the context of the course *Data-driven research and digital tools* at the [Department of Media & Culture, Utrecht University](https://www.uu.nl/en/organisation/department-of-media-and-culture-studies) in collaboration with [Dr. Karin van Es](https://www.karinvanes.net) and [Creative Coding Utrecht](https://creativecodingutrecht.nl/)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now find ourselves in a Jupyter Notebook — an open-source web application that allows you to create and share documents that contain live code, equations, visualizations and narrative text. This notebook gives us the possibility to write comments alongside the code, and to write run code without installing the dependencies on our own computers.\n", "\n", "We're going to be using the [Python](https://www.python.org/) programming language for this example.\n", "\n", "In order to execute a block of code, click the *Run* button from the toolbox on the top right corner. Up until the section \"Tooting time!\" every block needs to be run in the order they are layed out in.\n", "\n", "You can change the code blocks by double clicking the block and you can run them in order to see the effects.\n", "\n", "The bot we will be tooting on the Mastodon instance [botsin.space](https://botsin.space/about), a space dedicated fully to bot users. \n", "You can find the @infrapunctures bot [here](https://botsin.space/@infrapunctures)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Import the Mastodon library" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We start by importing a Python code library that is written for Mastodon. You can find more information about it on its [code respository](https://github.com/halcy/Mastodon.py)." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Requirement already satisfied: Mastodon.py in /home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/lib/python3.7/site-packages (1.5.1)\n", "Requirement already satisfied: python-magic in /home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/lib/python3.7/site-packages (from Mastodon.py) (0.4.18)\n", "Requirement already satisfied: requests>=2.4.2 in /home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/lib/python3.7/site-packages (from Mastodon.py) (2.24.0)\n", "Requirement already satisfied: blurhash>=1.1.4 in /home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/lib/python3.7/site-packages (from Mastodon.py) (1.1.4)\n", "Requirement already satisfied: decorator>=4.0.0 in /home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/lib/python3.7/site-packages (from Mastodon.py) (4.4.2)\n", "Requirement already satisfied: python-dateutil in /home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/lib/python3.7/site-packages (from Mastodon.py) (2.8.1)\n", "Requirement already satisfied: six in /home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/lib/python3.7/site-packages (from Mastodon.py) (1.15.0)\n", "Requirement already satisfied: pytz in /home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/lib/python3.7/site-packages (from Mastodon.py) (2020.1)\n", "Requirement already satisfied: chardet<4,>=3.0.2 in /home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/lib/python3.7/site-packages (from requests>=2.4.2->Mastodon.py) (3.0.4)\n", "Requirement already satisfied: certifi>=2017.4.17 in /home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/lib/python3.7/site-packages (from requests>=2.4.2->Mastodon.py) (2020.6.20)\n", "Requirement already satisfied: idna<3,>=2.5 in /home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/lib/python3.7/site-packages (from requests>=2.4.2->Mastodon.py) (2.10)\n", "Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /home/ccl/Documents/bots-as-digital-infrapunctures/bots-venv/lib/python3.7/site-packages (from requests>=2.4.2->Mastodon.py) (1.25.10)\n" ] } ], "source": [ "import sys\n", "!{sys.executable} -m pip install Mastodon.py" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "from mastodon import Mastodon " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Configuring the infrastructure" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With the code below we are choosing which Mastodon instance we want to use." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "instance = 'https://botsin.space'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Starting the bot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We have saved the user credentials in a file called 'mastodon-bot-usercred.secret' which contains a token code for indentifying our bot and the name of the instance. In the code below we access this secret file in order to get authorisation to post on the bot's behalf." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "mastodon = Mastodon(\n", " access_token = 'mastodon-bot-usercred.secret',\n", " api_base_url = instance\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Tooting time! \n", "\n", "The following code blocks will already allow us to toot! Tooting in Mastodon vernacular means posting. \n", "\n", "Each block will toot something else. You can make changes to the variables in order to toot something else." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is the simplest command you need to run to make a toot:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'id': 105197319551754193,\n", " 'created_at': datetime.datetime(2020, 11, 12, 12, 21, 48, 992000, 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://botsin.space/users/infrapunctures/statuses/105197319551754193',\n", " 'url': 'https://botsin.space/@infrapunctures/105197319551754193',\n", " 'replies_count': 0,\n", " 'reblogs_count': 0,\n", " 'favourites_count': 0,\n", " 'favourited': False,\n", " 'reblogged': False,\n", " 'muted': False,\n", " 'bookmarked': False,\n", " 'pinned': False,\n", " 'content': '
testing tooting
',\n", " 'reblog': None,\n", " 'application': {'name': 'bots-as-infrapunctures', 'website': None},\n", " 'account': {'id': 259694,\n", " 'username': 'infrapunctures',\n", " 'acct': 'infrapunctures',\n", " 'display_name': '',\n", " 'locked': False,\n", " 'bot': False,\n", " 'discoverable': None,\n", " 'group': False,\n", " 'created_at': datetime.datetime(2020, 10, 2, 12, 51, 39, 454000, tzinfo=tzutc()),\n", " 'note': '',\n", " 'url': 'https://botsin.space/@infrapunctures',\n", " 'avatar': 'https://botsin.space/avatars/original/missing.png',\n", " 'avatar_static': 'https://botsin.space/avatars/original/missing.png',\n", " 'header': 'https://botsin.space/headers/original/missing.png',\n", " 'header_static': 'https://botsin.space/headers/original/missing.png',\n", " 'followers_count': 2,\n", " 'following_count': 1,\n", " 'statuses_count': 6,\n", " 'last_status_at': datetime.datetime(2020, 11, 12, 0, 0),\n", " 'emojis': [],\n", " 'fields': []},\n", " 'media_attachments': [],\n", " 'mentions': [],\n", " 'tags': [],\n", " 'emojis': [],\n", " 'card': None,\n", " 'poll': None}" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mastodon.toot('testing tooting')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the code below we tell the bot to go through the list and toot each element every 5 minutes." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "ename": "NameError", "evalue": "name 'mastodon' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "\u001b[0;32mRosa likes lychees
',\n", " 'reblog': None,\n", " 'application': {'name': 'bots-as-infrapunctures', 'website': None},\n", " 'account': {'id': 259694,\n", " 'username': 'infrapunctures',\n", " 'acct': 'infrapunctures',\n", " 'display_name': '',\n", " 'locked': False,\n", " 'bot': False,\n", " 'discoverable': None,\n", " 'group': False,\n", " 'created_at': datetime.datetime(2020, 10, 2, 12, 51, 39, 454000, tzinfo=tzutc()),\n", " 'note': '',\n", " 'url': 'https://botsin.space/@infrapunctures',\n", " 'avatar': 'https://botsin.space/avatars/original/missing.png',\n", " 'avatar_static': 'https://botsin.space/avatars/original/missing.png',\n", " 'header': 'https://botsin.space/headers/original/missing.png',\n", " 'header_static': 'https://botsin.space/headers/original/missing.png',\n", " 'followers_count': 2,\n", " 'following_count': 1,\n", " 'statuses_count': 8,\n", " 'last_status_at': datetime.datetime(2020, 11, 12, 0, 0),\n", " 'emojis': [],\n", " 'fields': []},\n", " 'media_attachments': [],\n", " 'mentions': [],\n", " 'tags': [],\n", " 'emojis': [],\n", " 'card': None,\n", " 'poll': None}" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import random\n", "\n", "names = ['Rosa', 'Luxembourg', 'Parks']\n", "objects = ['oranges', 'mangos', 'lychees']\n", "actions = ['likes', 'dislikes', 'is indifferent to']\n", "\n", "n = random.choice(names)\n", "o = random.choice(objects)\n", "a = random.choice(actions)\n", "\n", "separator = ' '\n", "\n", "ourtoot = n + separator + a + separator + o\n", "mastodon.toot(ourtoot)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "date.today() returns the day of the week as an integer, where Monday is 0 and Sunday is 6. \n", "\n", "The code bellow will make your bot toot the first sentence if it is a work day and \"the second sentence if it is the weekend. " ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "from datetime import date\n", "\n", "day = date.today().weekday()\n", "\n", "if (day != 5 & day != 6):\n", " mastodon.toot(\"When we have no memory or little imagination of an alternative to a life centered on work, there are few incentives to reflect on why we work as we do and what we might wish to do instead.\")\n", "else:\n", " mastodon.toot(\"I would prefer not to.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Make your own bot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These have been only a few things you can try out that are meant to illustrate some of the programming logic we mentioned in the module. \n", "\n", "If you would like to make your own bot from scratch, you can remove the '#' from every line below and save it as a new file. The \"#\" turns them into comments instead of executable lines, so they will not run. These only need to be run once which for this module had been done in preparation. The code registers the bot as an application and saves the details as a .secret plain text file." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# instance = 'URL'\n", "# username = 'USERNAME'\n", "# password = 'PASSWORD'" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Mastodon.create_app(\n", "# 'bots-as-infrapunctures',\n", "# api_base_url = instance,\n", "# to_file = 'mastodon-bot.secret'\n", "# )" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 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", "# )" ] } ], "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 }