Getting Started¶
This project requires a running instance of Mastodon, a federated open source social media platform.
Mastodon developers offer a solid docker-compose option for ready to go configuration.
Therefore a cloud instance is required ex. Google Cloud, Amazon Web Services, Linode, DigitalOcean
We recommend an Ubuntu 20.04 LTS machine with 2 GB of RAM and min 20 GB of disk space.
Note
A domain name is also required for production. It can be acquired with GitHub Student Developer Pack. See further
.tech domains are recommended.
Install Docker¶
Second step is to install Docker Engine on Ubuntu.
Set up the repository¶
Update the apt package index and install packages to allow apt to use a repository over HTTPS:
sudo apt-get update sudo apt-get install \ apt-transport-https \ ca-certificates \ curl \ gnupg \ lsb-release
Add Docker’s official GPG key:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
Use the following command to set up the stable repository.
echo \ "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Install Docker Engine¶
Update the apt package index, and install the latest version of Docker Engine and containerd
sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io
Verify that Docker Engine is installed correctly by running the hello-world image
sudo docker run hello-world
Install Docker Compose¶
Install Compose on Linux systems¶
Run this command to download the current stable release of Docker Compose:
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
Apply executable permissions to the binary
sudo chmod +x /usr/local/bin/docker-compose
Test the installation.
docker-compose --version
If any problems with these steps read further: Install Docker Engine on Ubuntu
Manage Docker as a non-root user¶
To create the docker group and add your user:
Create the
dockergroup.sudo groupadd docker
Add your user to the
dockergroup.sudo usermod -aG docker $USER
Log out and log back in so that your group membership is re-evaluated.
If testing on a virtual machine, it may be necessary to restart the virtual machine for changes to take effect.
On Linux, you can also run the following command to activate the changes to groups:
newgrp docker
Verify that you can run
dockercommands withoutsudodocker run hello-world
Mastodon Docker Setup¶
Setting up¶
Launch a console terminal on the remote machine.
Clone Mastodon’s repository.
# Clone mastodon to ~/mastodon directory
git clone https://github.com/mastodon/mastodon.git mastodon
# Change directory to ~/mastodon
cd ~/mastodon
# Checkout to the latest stable branch
git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)
Installing Docker containers¶
Docker Images
You can use a prebuilt Docker image. Images are available from Docker Hub:
Mastodon Docker Image
Docker Compose and and environment files¶
Use these pre-configured files to avoid any problems. Github
Building the app¶
Copy the files .env.production and .env.db that have been provided to the /mastodon directory.
Then change the docker-compose.yml file with provided one.
In env.production file replace LOCAL_DOMAIN with your own domain name.
To create an account for admin run the followings:
docker-compose run --rm web bundle exec rake mastodon:setup
This is an interactive wizard that will guide you through the basic and necessary options and generate new app secrets.
App secrets part did not worked for me before so we generated them beforehand.
Enter the Fully Qualified Domain Name (FQDN) of your mastodon instance.
Select if you want a Single User instance (not recommended, but if you prefer, use that).
Obviously, you are running mastodon in a docker instance, so type Y (or hit return, as it’s the default)
The PostgreSQL host is
db, the port is5432(again, default), the database ismastodon_production, the database user ismastodonand the password is the one fromdocker-compose.yml.The redis server is
redis, the port is6379and the password is empty.If you want to store uploaded files on the cloud, enter Y here – I haven’t tested that, but I expect you need an S3 or other cloud storage for that.
If you want to send emails from the local machine, enter
Y– considering we’re in a docker environment, I have only used my local STMP server in FQDN form, notlocalhost. Enter port, user and password for SMTPsubmission. Select the SMTP authentication type (when submitting locally,plainshould be fine). Decide if you want to verify the identity of the server and, if so, what type of verification you want to do. Choose what sender address the emails will have (I usemastodon@*my.domain*).
DNS Configuration¶
Add A Record to instance IP with your DNS manager.
Reverse Proxy¶
You need a Reverse Proxy in front of your Mastodon instance. The preferred software for this Caddy 2 which is already a part of docker-compose.yml file that provided.
Caddy 2 is still the only web server to use TLS automatically and by default. Caddy 2 enables to deploy and scale HTTPS effortlessly.
Caddyfile Configuration¶
You need to configure Caddy to serve your Mastodon instance.
Reminder: Replace all occurrences of example.com with your own instance’s domain or sub-domain.
Create a Caddyfile containing only these lines and replace your.domain.tech with your own domain:
your.domain.tech {
reverse_proxy web:3000
}
Launch Mastodon¶
After it’s done, you can launch Mastodon with:
docker-compose up -d
Within ~30 seconds all docker containers should be up and running.
You can check it with docker ps command.
Creating accounts¶
Main admin account is created when building the app. Any other accounts must be created and confirmed on the terminal with admin CLI that Mastodon provides. Since an e-mail delivery service or other SMTP server is not dedicated for the server, its only possible to confirm users with the admin CLI tool.
Execute the following command on the terminal where the docker-compose.yml file exists to create an account
docker-compose run --rm web bin/tootctl accounts create USERNAME --email EMAIL --confirmed
Replace USERNAME and EMAIL with relevant information.
If an account is created at sign-up page and waiting for confirmation, user can be modified with the following command:
docker-compose run --rm web bin/tootctl accounts modify USERNAME --confirm
For further information about admin CLI, you can check out Using the admin CLI
Dashboard and API¶
Clone this project’s repository.
git clone https://github.com/dogukanburda/Developing-interfaces-for-social-network-experiments.git
# Change directory to ./Developing-interfaces-for-social-network-experiments
cd Developing-interfaces-for-social-network-experiments
Project Components¶
Steamlit¶
Streamlit is an open-source app framework used to create a Dashboard with Python.
main.py is the main frontend source file to run the project.
Pure.py¶
serves as a
server communicatorbetween Mastadon and Dashboard
Built on top of the Mastodon.py python wrapper. Further information:
Creating an actual Mastodon API instance:¶
By creating a Pure object you create an actual Mastodon API instance. It has an API that allows you to interact with any given Mastodon server’s every aspect.
Generate your own secret files with provided methods and substitute your secret files to the Pure object.
Register¶
Register your app! This only needs to be done once. Uncomment the code and substitute in your information:
from pure import Pure
'''
Pure.create_app(
'PURE',
api_base_url = 'https://mastodon.social',
to_file = 'app_cred.secret'
)
'''
Login¶
Then login. This can be done every time, or you can use the persisted information:
from pure import Pure
pure = Pure(
client_id = 'app_cred.secret',
api_base_url = 'https://mastodon.social'
)
pure.log_in(
'my_login_email@example.com',
'incrediblygoodpassword',
to_file = 'user_cred.secret'
)
To post, create an actual API instance:
from pure import Pure
pure = Pure(
access_token = 'user_cred.secret',
api_base_url = 'https://mastodon.social'
)
pure.toot('Tooting from python !')
Pure object’s functionality :¶
Get a title and a description from the server - when conducting research on other mastodon servers your dashboard will automatically update its title
Get a timeline of specific logged in user
Create a dataframe which includes
{user_ids, usernames, toot_ids, toot_time, favourites_count, toot_content}Get user_id from a provided username
Provide user_toots from a given user_id
Create a Followings Network of 400 users starting from given a user_id
Create detailed .html file that includes account_information from a provided username
Get clean content of last 400 toots from a provided user_id which is implemented to create a Word Cloud
Main.py¶
Main.py functionality:
Implement a Pure object
Display Options Menu : User Toots, User Profile, Diffusion Netwok, Word Cloud
If user selects User Toots:
Takes username as input and calls
get_user_idto communicate with Mastodon API and callsget_user_tootsmethod of Pure objectPlots a graph of toots’ favourites_count versus toot_time
Graph can also display details about each toot when hover on it
If user selects User Profile:
Program displays .html file that includes information about user
Display Account Statistics: - Toots by day of the week - Toots timeseries
If user selects Diffusion Netwok:
Takes username as an input and displays a Diffusion Network graph.
If user selects Word Cloud:
Program gets toots’ content from Pure object, and creates a word cloud in shape of a mastodon
Bot Template¶
Listener:
Listener class inherits mastodon.py’s StreamListener class.
Listener class listens the server with its every method and executes the followings according to corresponding server response.
By default follows back when a user follows the logged in account.
Replies mentions with
Dont disturb me 😡.sentence.Replies the toots on the current stream with sentiment analysis scores.
# Main object listens to the stream
class Listener(StreamListener):
def __init__(self,bot,pure):
# self.bot for handling content
self.bot = bot
#self.pure for taking actions on the server with given credentials
self.pure = pure
def on_notification(self, ntf):
# Follows back whoever follows the logged in user
if ntf['type'] == 'follow':
print("You have a new follower !",ntf['account']['username'])
self.pure.account_follow(ntf['account']['id'])
print("You followed back {}.".format(ntf['account']['username']))
#print(ntf)
# Replies mentions with 'Dont disturb'
elif ntf['type'] == 'mention':
print('Someone mentioned you !',ntf['account']['username'])
self.pure.status_post('Dont disturb me 😡.',in_reply_to_id=ntf['status']['id'])
def on_update(self, status):
"""A new status has appeared! 'status' is the parsed JSON dictionary
describing the status."""
# Detects language of the status and does a sentiment analysis with transformers pipeline
toot_id = status['id']
lang = self.bot.toot_language(status)
label_score = self.bot.toot_sentiment(status)
string = "This status' sentiment analysis is classified as {}, with confidence of {}. Model provided from transformers 🤗 (https://github.com/huggingface/transformers)".format(label_score[0]['label'],label_score[0]['score'])
pure.status_post(string,in_reply_to_id=toot_id)
context = self.bot.context(status)
print('Context: ', context)
print('Detected language: ', lang)
def handle_heartbeat(self):
"""The server has sent us a keep-alive message. This callback may be
useful to carry out periodic housekeeping tasks, or just to confirm
that the connection is still open."""
print('Bot script is working...')
MastodonBot:
Bot class for handling content.
Cleans the returned values’ content for analysis.
Detects the language and sentiment of shared content.
This can be customized for the user’s needs.
# bot features
class MastodonBot():
def __init__(self):
pass
def cleanhtml(self,raw_html):
import re
cleanr = re.compile('<.*?>')
cleantext = re.sub(cleanr, '', raw_html)
return cleantext
def toot_language(self,status):
from textblob.sentiments import NaiveBayesAnalyzer
from textblob import TextBlob
global lang_dict
toot_context = self.cleanhtml(status['content'])
blob = TextBlob(toot_context,analyzer=NaiveBayesAnalyzer())
detected_language = blob.detect_language()
detected_language_name = lang_dict[detected_language]
return detected_language_name
def toot_sentiment(self,status):
from transformers import pipeline
classifier = pipeline('sentiment-analysis')
label_score = classifier(self.cleanhtml(status['content']))
return label_score
def context(self,status):
toot_context = self.cleanhtml(status['content'])
return toot_context
Running the bot¶
First create a MastodonBot object for handling content
Create a Pure object with your credentials to communicate with server.
Initialize the Listener object with the MastodonBot and Pure objects.
Connect the Listener object to desired stream.
For streaming options, check Streaming link.
Example bot initalization.
bot = MastodonBot()
pure = Pure(access_token='./secrets/pure_user.secret',api_base_url='https://mastodon.social')
listener = Listener(bot,pure)
pure.stream_local(listener)