/guinea-bot

A guinea pig Twitter bot using a Markov chain.

Primary LanguagePythonMIT LicenseMIT

guinea-bot

Test & QA GitHub release (latest by date)

A guinea pig Twitter bot currently tweeting under the handle @guinea_bot

It uses a very simple state machine implementation of a Markov chain (some state changes are probabilistic rather than deterministic). The state machine runs with a change of state triggered being every 15 minutes (by default). The guinea pig has a lifespan, and some simple internal attributes which affect its behaviour to emulate its exciting and busy life. It will randomly tweet what it is doing on each state change and may also post a random picture from its archive if configured to do so.

Tweets are selected from a JSON file, loaded on start-up, that contains a variety of amusing messages for each state. For photos to be tweeted the path to a folder of .jpg files must be set using the command line option, a list of the photos available to use is then loaded on start-up. Tweeting is limited to a 1 in 8 chance as it's very easy to generate hundreds of tweets a day. If the state doesn't change then there is a 1 in 80 chance of tweeting a photo (if there are any).

The bot has the functionality to automatically search for other guinea pigs related accounts and randomly follow them. This was triggered with a 1 in 80 chance if nothing was tweeted, but having bots automatically follow other accounts on Twitter is a bit of minefield as you never know what you are going to get so this functionality has been disabled. When enabled the bot searched for accounts using the "#guineapig" tag and then decide if it wanted to follow them after checking for a number of "red flag" keywords in the accounts' bio/description. It would also attempt to curate the existing friends list by removing friends who no longer pass the "friendship test", unless they were following back, in which case they were muted.

The state changes that drive the guinea pig prioritise sleeping and eating, but the internal attributes can drive it from any state to any other state (see the code for the exact rules). Sleeping is used as the start state, and the end state can be reached at any time as the guinea pig ages, reaches its lifespan duration and dies 😢. But, the internal state, age/attributes, are persisted to a file on each state change so that the same guinea pig can continue to run after a crash or power outage. The file is stored in the current users home directory by default but that can be changed using the -h/--house option.

Image of Guinea Pig States

Installing

Checkout the source code from here:

$ git clone https://github.com/RatJuggler/guinea-bot.git
$ cd guinea-bot

Then install/update the bot as a Python package:

$ pip3 install -U .

Running

$ guineabot --help

Usage: guineabot [OPTIONS]

  Guinea Pig Twitter bot.

Options:
  --version                       Show the version and exit.
  -n, --name TEXT                 The name of the guinea pig.  [default:
                                  Holly]
  -h, --house DIRECTORY           Piggy house where the guinea pig is kept.
                                  [default: (user home directory)]
  -p, --photos DIRECTORY          Optional path to photos which can be
                                  Tweeted.
  -d, --duration INTEGER RANGE    How many days the bot should run for (guinea
                                  pig lifespan), random if not set.
  -i, --interval INTEGER RANGE    The interval between changes in state
                                  (guinea pig activity), in minutes.
                                  [default: 15]
  -a, --accelerated               Ignore the pauses between state changes,
                                  forces quiet mode to prevent Twitter API
                                  rate limit triggering.  [default: False]
  -l, --log-level [DEBUG|INFO|WARNING]
                                  Show additional logging information.
                                  [default: INFO]
  -m, --metrics                   Publish metrics on the internal state of the
                                  bot.  [default: False]
  -o, --port INTEGER RANGE        Port that any published metrics will be
                                  available on.  [default: 8000]
  -q, --quiet                     Run without invoking the Twitter API.
                                  [default: False]
  -t, --test                      Test the Twitter access tokens and exit.
                                  [default: False]
  --help                          Show this message and exit.

You can use the -q option to run without using the Twitter API but to make it fully functional you will need to set up a Twitter account and apply for access here. The access tokens created for the account then need to be made available as environment variables for the bot to find. The easiest way to do this is to create a guinea-bot.env file from the supplied template, then edit the file and copy in your tokens.

$ cp guinea-bot.env.template guinea-bot.env

The edited file should then look something like this (not real tokens):

TWITTER_CONSUMER_KEY=123abc456cde789fgh012ijkl
TWITTER_CONSUMER_SECRET=456cde789fgh012ijkl123abc456cde789fgh012ijkl123abc
TWITTER_ACCESS_TOKEN=789fgh012ijkl123abc456cde789fgh012ijkl123abc456cde
TWITTER_ACCESS_TOKEN_SECRET=c456cde789fgh012ijkl123abc456cde789fgh012ijkl

The bot will always start by looking for the file in the current directory and then searching upwards. Test that your tokens are working by using:

$ guineabot --test

Metrics are available in the standard Prometheus format.

Installing as a service under systemd

First edit the guinea-bot.service file and add any command line options you need, making sure to keep them within the command quotes. It's also a good idea to set a user as the service does not need to run as root, but make sure the home directory is accessible if you are using the default state save location. Once complete copy the file to /etc/systemd/system and enter the following to create a systemd unit service.

$ sudo cp guinea-bot.service /etc/systemd/system

A configuration file also needs to be created to hold the Twitter access tokens.

$ sudo systemctl edit guinea-bot

Add an entry to the generated file like so:

[Service]
Environment="TWITTER_CONSUMER_KEY=<your consumer key here>"
Environment="TWITTER_CONSUMER_SECRET=<your consumer secret here>"
Environment="TWITTER_ACCESS_TOKEN=<your access token here>"
Environment="TWITTER_ACCESS_TOKEN_SECRET=<your access token secret here>"

Reload the service files after changes or when new:

$ sudo systemctl daemon-reload

Enable the service:

$ sudo systemctl enable guinea-bot.service

Start the service:

$ sudo systemctl start guinea-bot.service

Check the status of the service:

$ sudo systemctl status guinea-bot.service

Stop the service:

$ sudo systemctl stop guinea-bot.service

Tail service's log:

$ sudo journalctl -f -u guinea-bot.service

Docker

Docker build and compose files are available which create a standalone image for the guinea bot to live in.

Create the image with: docker build -f docker/Dockerfile -t guinea-bot:local .

We need to be careful that any Twitter access tokens aren't included in the image in case it is pushed to a public repository (and it's also just best practice). There are a number of ways to inject the tokens into the image but probably the easiest is to create a guinea-bot.env file as described above and then run the image with the --env-file option.

docker run guinea-bot:local -d --env-file guinea-bot.env -p 8000:8000

The image is configured with a simple health check, using the --test option, for running under orchestration and metrics enabled on the default port. You can check they are running by pointing your browser to http://localhost:8000.

Or just use the compose file to do everything:

docker-compose up -d

Environment variables can be used to configure image tagging (see the file),