Last active
December 21, 2015 04:09
-
-
Save dreness/6247320 to your computer and use it in GitHub Desktop.
Send email notifications of shares and blocks.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
import time, os | |
import re | |
from email.mime.text import MIMEText | |
from subprocess import Popen, PIPE | |
from datetime import datetime, timedelta | |
# Watch the bitcoin log, send notifications of shares and blocks, with | |
# current payout | |
### SETTINGS | |
# The bitcoin log to watch for interesting events | |
BTLOG = "/home/towelie/.bitcoin/p2pool/data/bitcoin/log" | |
# Log file to update with status and sent message notifications | |
ACTIVITY_LOG = "/home/towelie/watcher.log" | |
# The address to notify. If SMS, consider setting TERSE_OUTPUT to True below. | |
EMADDRESS = "[email protected]" | |
# Set a custom 'full name' email field. Doesn't seem to work with SMS. | |
# This is used as the argument for sendmail's -F option | |
EMAIL_FROM = "P2Pool" | |
# In terse mode, we use no message subject and a shorter body | |
TERSE_OUTPUT = True | |
#TERSE_OUTPUT = False | |
# Max number of messages we'll send within 1 minute. If we go over, we assume | |
# something went crazy and we exit. | |
MAX_PER_MINUTE = 10 | |
# Enable or disable test mode. In test mode, we'll process the log | |
# starting from the beginning, and send notifications until we hit the | |
# sanity check threshold, or until we reach the end of the file. This tests | |
# both delivery and possibly the sanity check. | |
TEST_MODE = False | |
#TEST_MODE = True | |
# Customize the message prefix. Emojo might not work with SMS. | |
#MESSAGE_PREFIX = "💰 " | |
#MESSAGE_PREFIX = "btc-watcher: GOT" | |
MESSAGE_PREFIX = "" | |
### | |
# The regular expression pattern for parsing the current payout | |
PAYOUT_RE = '.* Current payout: (\S+) BTC' | |
# Store the current payout rate here (it's a float) | |
PAYOUT = 0.0 | |
# Full status here | |
STATUS = '' | |
# Keep track of times we've sent email | |
SENT = [] | |
# open the log file, get its size | |
file = open(BTLOG, 'r') | |
st_results = os.stat(BTLOG) | |
st_size = st_results[6] | |
# If we're not in testmode, seek to the end of the file | |
if not TEST_MODE: | |
file.seek(st_size) | |
# Also open our activity log for writing | |
activity = open(ACTIVITY_LOG,'a') | |
# The adjusted payout doesn't arrive in the log until after SHARE / BLOCKs | |
# are logged. Every time we get a share / block, we'll wait until the next | |
# payout rate arrives before sending the message. | |
pendingMessage = '' | |
### Functions | |
# Sanity check; have we sent more than MAX_PER_MINUTE in the last minute? | |
def overloaded(): | |
global SENT | |
global MAX_PER_MINUTE | |
inRange = 0 | |
now = datetime.now() | |
d = timedelta(minutes=1) | |
for x in SENT: | |
if x > now - d: | |
inRange += 1 | |
if inRange > MAX_PER_MINUTE: | |
return True | |
else: | |
return False | |
# Compose and send a message | |
def sendMessage(line, PAYOUT, STATUS): | |
global SENT, pendingMessage, MESSAGE_PREFIX | |
global TERSE_OUTPUT, EMADDRESS, activity | |
# Compose the message. Emoji doesn't seem to work via (some?) SMS | |
subject = MESSAGE_PREFIX | |
# Add the type of event and maybe some details | |
if ('SHARE' in line): | |
subject += "SHARE! [%.4f BTC]" % (PAYOUT,) | |
if ('BLOCK' in line): | |
subject += "BLOCK! [%.4f BTC]" % (PAYOUT,) | |
if not TERSE_OUTPUT: | |
msg = MIMEText("%s\n%s" % (line,STATUS)) | |
msg["Subject"] = subject | |
else: | |
msg = MIMEText("%s\n" % (subject,)) | |
msg["To"] = EMADDRESS | |
aLog("%s sending: %s to address %s\n" % | |
(stamp,subject,EMADDRESS)) | |
if overloaded(): # sanity check | |
print "exiting due to insane message rate!" | |
aLog("%s bailed due to insane message rate!\n" % (stamp,)) | |
exit(1) | |
# shell out to sendmail, passing the message on stdin | |
cmd = ['/usr/sbin/sendmail', '-F', EMAIL_FROM, '-t'] | |
aLog("%s\n" % (' '.join(cmd))) | |
p = Popen(cmd, stdin=PIPE) | |
p.communicate(msg.as_string()) | |
if p.returncode: # non-zero return code indicates error | |
aLog("sendmail exited abnormally, bailing! code %r" % (p.returncode)) | |
# Turn off the pendingMessage indicator | |
pendingMessage = '' | |
# Store a current timestamp, for rate-limiting sanity checks | |
SENT.append(datetime.now()) | |
# Truncate the list of timestamps so it doens't grow without bound | |
# Remove from the front of the list, since new ones appear at the end | |
if len(SENT) > 50: | |
SENT = SENT[20:] | |
def aLog(s): | |
global activity | |
activity.write(s) | |
activity.flush() | |
### Main | |
# Loop forever | |
while 1: | |
# get the current offset in the file (i.e. current location) | |
where = file.tell() | |
# get a line | |
line = file.readline() | |
# Make a timestamp for our activity log | |
stamp = time.strftime("%m-%d-%Y %H:%M:%S", time.localtime()) | |
# if we got nothing, sleep for a bit, reset position, exit the loop early | |
if (not line): | |
aLog("%s Current payout: %f\n" % (stamp,PAYOUT,)) | |
time.sleep(10) | |
file.seek(where) | |
continue | |
# We got a line. Process it. | |
# Get rid of the time / date stamp (first two fields) | |
line = ' '.join(line.split()[2:]) | |
# Keep track of current status and payout info | |
if ('Current' in line): | |
m = re.search(PAYOUT_RE, line) | |
if (m): | |
PAYOUT = float(m.group(1)) | |
STATUS = line | |
if (pendingMessage != ''): | |
sendMessage(pendingMessage, PAYOUT, STATUS) | |
# Something cool happened! | |
if ('GOT' in line): | |
pendingMessage = line | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment