Skip to content

Commit

Permalink
adding a twitter module
Browse files Browse the repository at this point in the history
  • Loading branch information
iannesbitt committed Nov 29, 2019
1 parent c8518f5 commit 7ec102a
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 9 deletions.
30 changes: 26 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
[![PyPI - Downloads](https://img.shields.io/pypi/dm/rsudp)](https://pypi.org/project/rsudp/)
[![GitHub](https://img.shields.io/github/license/raspishake/rsudp)](https://github.com/raspishake/rsudp/blob/master/LICENSE)

`rsudp` is a tool for receiving and interacting with UDP data sent from a Raspberry Shake seismograph. It contains six main features:
`rsudp` is a tool for receiving and interacting with UDP data sent from a Raspberry Shake seismograph. It contains seven main features:
1. Print - a debugging tool to output raw UDP output to the command line
2. Writer - a miniSEED writer
3. Plot - a live-plotting routine to display data as it arrives on the port, with an option to save plots some time after an `ALARM` message is read from the queue
4. Forward - forward a data cast to another destination
5. Alarm - an earthquake/sudden motion alert---complete with bandpass filter capability---configured to send an `ALARM` message to the queue in the event of a recursive STA/LTA alarm trigger, and optionally run some code
6. AlertSound - a thread that plays a MP3 audio file when an `ALARM` message is read from the queue
7. Tweeter - a thread that tweets when an `ALARM` message is read from the queue, and optionally can tweet saved plots from the plot module

`rsudp` is written in Python but requires no coding knowledge to run. Simply follow the instructions to install the software, go to your Shake's web front end, configure a UDP datacast to your computer's local IP address, start rsudp from the command line, and watch the data roll in.

Expand Down Expand Up @@ -62,6 +63,12 @@ git pull
bash unix-install-rsudp.sh
```

The update script will replace the previous default settings file (`~/.config/rsudp/rsudp_settings.json`) with a new settings file. If you use the default settings file, you will need to copy some old values over to the new file. The reason for this is that the default settings file may change (i.e. add or modify sections of values) and thus must be rewritten when updating. On Linux, backed up settings files will be named `~/.config/rsudp/rsudp_settings.json.~x~`, where `x` is an integer. On Mac, the backed up file will simply be named `~/.config/rsudp/rsudp_settings.json~`. To back up the settings file yourself to a location that will not be overwritten, you can do a command similar to the following:

```bash
cp ~/.config/rsudp/rsudp_settings.json ~/.config/rsudp/rsudp_settings.json.bak
```

### On Windows

1. Download and install [Anaconda](https://www.anaconda.com/distribution/#windows) or [Miniconda](https://docs.conda.io/en/latest/miniconda.html).
Expand Down Expand Up @@ -145,7 +152,14 @@ By default, the settings are as follows:
"win_override": false},
"alertsound": {
"enabled": false,
"mp3file": "doorbell"}
"mp3file": "doorbell"},
"tweets": {
"enabled": false,
"tweet_images": true,
"api_key": "n/a",
"api_secret": "n/a",
"access_token": "n/a",
"access_secret": "n/a"}
}
```

Expand Down Expand Up @@ -181,6 +195,8 @@ By default, the settings are as follows:

The software will install several small MP3 files. The `"mp3file"` is `"doorbell"` (two doorbell chimes) by default, but there are a few more aggressive alert sounds, including: a three-beep sound `"beeps"`, a sequence of sonar pings `"sonar"`, and a continuous alarm beeping for 5 seconds, `"alarm"`. You can also point the `"mp3file"` field to an MP3 file somewhere in your filesystem. For example, if your username was pi and you had a file called earthquake.mp3 in your Downloads folder, you would specify `"mp3file": "/home/pi/Downloads/earthquake.mp3"`. The program will throw an error if it can't find (or load) the specified MP3 file. It will also alert you if the software dependencies for playback are not installed.

- **`tweets`** if `"enabled"` is `true`, and all API keys have been generated and are correctly entered, then this module will use the Twitter API to create tweets when an `ALARM` message arrives on the queue. If `"tweet_images"` is `true`, then the module will also tweet a saved image of the event, if `"eq_screenshots"` is set to `true` in the `"plot"` module. Note that in order for this to work, the user has to create 1) a [twitter profile for automatically tweeting alerts](https://twitter.com/signup) (or use an existing account), 2) a [Twitter developer account](https://developer.twitter.com/en.html), 3) a [Twitter API app](https://opensource.com/article/17/8/raspberry-pi-twitter-bot) inside said developer account, and 4) consumer keys and API keys for that app. Once you have generated the four API keys required for authentication (consumer API key, consumer API secret, access token, and access token secret), you may enter them into your settings file in the appropriate fields: `"api_key"`, `"api_secret"`, `"access_token"`, and `"access_secret"`.

## Disclaimer

**NOTE: It is extremely important that you do not rely on this code to save life or property.** Raspberry Shake is not liable for earthquake detection of false positives, false negatives, errors running the Alert module, or any other part of this library; it is meant for hobby and non-professional notification use only. If you need professional software meant to provide a warning intended to save life or property please contact Raspberry Shake directly or look elsewhere. See sections 16 and 16b of the [License](LICENSE) for further details.
Expand Down Expand Up @@ -242,7 +258,14 @@ This plot of a M 3.0 earthquake 50 km away was saved automatically without user
"win_override": false},
"alertsound": {
"enabled": true,
"mp3file": "doorbell"}
"mp3file": "doorbell"},
"tweets": {
"enabled": false,
"tweet_images": true,
"api_key": "n/a",
"api_secret": "n/a",
"access_token": "n/a",
"access_secret": "n/a"}
}
```

Expand All @@ -255,7 +278,6 @@ Contributions to this project are more than welcome. If you find ways to improve
Since the Producer function passes an `ALARM` queue message when it sees `Alert.alarm=True`, other modules can be easily added and programmed to do something when they see this message. This is to help make the addition of other action-based modules straightforward.

Some ideas for improvements are:
- a module that creates a twitter post when it reads the "ALARM" queue message
- a way to plot trigger-on and trigger-off events using osbpy's [trigger_onset](https://docs.obspy.org/packages/autogen/obspy.signal.trigger.trigger_onset.html) ([example here](https://docs.obspy.org/tutorial/code_snippets/trigger_tutorial.html#advanced-example))
- GPIO pin interactions (lights, motor control, buzzers, etc.)
- a more efficient plotting routine
Expand Down
9 changes: 6 additions & 3 deletions rsudp/c_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def __init__(self, cha='all', q=False,
sys.stdout.flush()
sys.exit()

self.master_queue = None # careful with this, this goes directly to the master consumer. gets set by main thread.
printM('Starting.', self.sender)

self.stream = RS.Stream()
Expand Down Expand Up @@ -139,7 +140,6 @@ def __init__(self, cha='all', q=False,
self.bgcolor = '#202530' # background
self.fgcolor = '0.8' # axis and label color
self.linecolor = '#c28285' # seismogram color
self.figimage = False

def deconvolve(self):
RS.deconvolve(self)
Expand Down Expand Up @@ -223,10 +223,11 @@ def _eventsave(self):
self.save.reverse()

event_time_str = event[1].strftime('%Y-%m-%d-%H%M%S') # event time
title_time_str = event[1].strftime('%Y-%m-%d %H:%M:%S')

# change title (just for a moment)
self.fig.suptitle('%s.%s detected event - %s' # title
% (self.net, self.stn, event_time_str),
self.fig.suptitle('%s.%s detected event - %s UTC' # title
% (self.net, self.stn, title_time_str),
fontsize=14, color=self.fgcolor, x=0.52)

# save figure
Expand All @@ -246,6 +247,8 @@ def savefig(self, event_time=RS.UTCDateTime.now(),
plt.savefig(figname, facecolor=self.fig.get_facecolor(), edgecolor='none')
print() # distancing from \r line
printM('Saved %s' % (figname), sender=self.sender)
printM('%s thread has saved an image, sending IMGPATH message to queues' % self.sender, sender=self.sender)
self.master_queue.put(b'IMGPATH %s %s' % (bytes(str(event_time), 'utf-8'), bytes(str(figname), 'utf-8')))

def _set_fig_title(self):
self.fig.suptitle('%s.%s live output - detected events: %s' # title
Expand Down
88 changes: 88 additions & 0 deletions rsudp/c_tweet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import os, sys, platform
import pkg_resources as pr
import time
import math
from threading import Thread
import numpy as np
from datetime import datetime, timedelta
import rsudp.raspberryshake as RS
from rsudp import printM
import rsudp
from twython import Twython


class Tweeter(Thread):
def __init__(self, consumer_key, consumer_secret, access_token, access_token_secret,
q=False, tweet_images=False,
):
"""
Initialize the process
"""
super().__init__()
self.sender = 'Tweeter'
self.alarm = False
self.alive = True
self.tweet_images = tweet_images
self.fmt = '%Y-%m-%d %H:%M:%S UTC'

if q:
self.queue = q
else:
printM('ERROR: no queue passed to consumer! Thread will exit now!', self.sender)
sys.stdout.flush()
self.alive = False
sys.exit()

self.twitter = Twython(
consumer_key,
consumer_secret,
access_token,
access_token_secret
)
self.message0 = '(Raspberry Shake station %s.%s) Event detected at' % (RS.net, RS.stn)
self.message1 = '(Raspberry Shake station %s.%s) Image of event detected at' % (RS.net, RS.stn)

printM('Starting.', self.sender)

def getq(self):
d = self.queue.get()
self.queue.task_done()

if 'TERM' in str(d):
self.alive = False
printM('Exiting.', self.sender)
sys.exit()

elif 'ALARM' in str(d):
event_time = RS.UTCDateTime.strptime(d.decode('utf-8'), 'ALARM %Y-%m-%dT%H:%M:%S.%fZ')
self.last_event_str = event_time.strftime(self.fmt)
message = '%s %s' % (self.message0, self.last_event_str)
printM('Sending tweet...', sender=self.sender)
self.twitter.update_status(status=message)
print()
printM('Tweeted: %s' % (message), sender=self.sender)

elif 'IMGPATH' in str(d):
if self.tweet_images:
imgdetails = d.decode('utf-8').split(' ')
imgtime = RS.UTCDateTime.strptime(imgdetails[1], '%Y-%m-%dT%H:%M:%S.%fZ')
message = '%s %s' % (self.message1, imgtime.strftime(self.fmt))
print()
if os.path.exists(imgdetails[2]):
with open(imgdetails[2], 'rb') as image:
printM('Uploading image to Twitter %s' % (imgdetails[2]), self.sender)
response = self.twitter.upload_media(media=image)
print()
printM('Sending tweet...', sender=self.sender)
self.twitter.update_status(status=message, media_ids=response['media_id'])
print()
printM('Tweeted with image: %s' % (message), sender=self.sender)
else:
printM('Could not find image: %s' % (imgdetails[2]), sender=self.sender)

def run(self):
"""
Reads data from the queue and plays self.sound if it sees an ALARM message
"""
while True:
self.getq()
25 changes: 24 additions & 1 deletion rsudp/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from rsudp.c_forward import Forward
from rsudp.c_alert import Alert
from rsudp.c_alertsound import AlertSound
from rsudp.c_tweet import Tweeter
import pkg_resources as pr
import fnmatch
try:
Expand Down Expand Up @@ -149,6 +150,19 @@ def mk_p(proc):
alsnd = AlertSound(q=q, sound=sound)
mk_p(alsnd)

if settings['tweets']['enabled']:
consumer_key = settings['tweets']['api_key']
consumer_secret = settings['tweets']['api_secret']
access_token = settings['tweets']['access_token']
access_token_secret = settings['tweets']['access_secret']
tweet_images = settings['tweets']['tweet_images']

q = mk_q()
tweet = Tweeter(q=q, consumer_key=consumer_key, consumer_secret=consumer_secret,
access_token=access_token, access_token_secret=access_token_secret,
tweet_images=tweet_images)
mk_p(tweet)


# master queue and consumer
queue = Queue(RS.qsize)
Expand All @@ -162,11 +176,13 @@ def mk_p(proc):
prod.start()

if settings['plot']['enabled'] and mpl:
Plotter.master_queue = queue
Plotter.run()
else:
while not prod.stop:
time.sleep(0.1)


time.sleep(0.5)

print()
Expand Down Expand Up @@ -259,7 +275,14 @@ def default_settings(output_dir='%s/rsudp' % os.path.expanduser('~').replace('\\
"win_override": false},
"alertsound": {
"enabled": false,
"mp3file": "doorbell"}
"mp3file": "doorbell"},
"tweets": {
"enabled": false,
"tweet_images": true,
"api_key": "n/a",
"api_secret": "n/a",
"access_token": "n/a",
"access_secret": "n/a"}
}
""" % (output_dir)
if verbose:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
url="https://github.com/raspishake/rsudp",
packages=setuptools.find_packages(),
include_package_data=True,
install_requires=['obspy', 'numpy', 'matplotlib==3.1.1', 'pydub'],
install_requires=['obspy', 'numpy', 'matplotlib==3.1.1', 'pydub', 'twython'],
entry_points = {
'console_scripts': [
'rs-local=rsudp.shake_udp_local:main',
Expand Down

0 comments on commit 7ec102a

Please sign in to comment.