We're putting the finishing touches on Ethboard and thought it might be a fun idea to brush up on our python and whip together a bot to tweet whenever anyone updates the board. Turns out you can do that in <100 lines of code in Python, so I thought our first post should be a dissection of the bot I put together, since it might be useful to other dApp developers.
# Imports
import requests
import json
import tweepy
import logging
import time
# Logging object for docker logs
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger();
# Etherscan API URL
api_url = "https://api.etherscan.io/api";
# Parameters to fetch the normal transactions from an account from the
# etherscan API
api_params = {
'module': 'account',
'action': 'txlist',
'address': '<YOUR ADDY HERE>',
'startblock': 0,
'endblock': 99999999,
'sort': 'desc',
'apikey': '<YOUR API KEY HERE>'
};
# Etherscan API will reject requests without a User-Agent declared
api_headers = {'User-Agent': 'Python'};
# Twitter API keys go here
auth = tweepy.OAuthHandler("<TWITTER API KEY>", "<TWITTER API SECRETE>");
auth.set_access_token("<TWITTER ACCESS TOKEN>", "<TWITTER ACCESS SECRET>");
# Tweepy API object
api = tweepy.API(auth);
# Verify credentials
try:
api.verify_credentials()
logger.info("Bot is authorized to send tweets!");
except:
logger.info("Twitter authorization failed!");
exit();
# Loads the last block number from block_data
def loadLastBlock():
f = open("block_data", "r+");
lbn = f.read();
f.close();
return lbn;
# Saves the last block number to block_data
def saveLastBlock(blockNumber):
f = open("block_data", "w");
f.write(blockNumber);
f.close();
# Decodes a blocks input data into a message our URL
def decodeMessage(blockData):
data = bytearray.fromhex(blockData[2:]);
len = data[67]+68;
return data[68:len];
def main():
# Try to load the last block number
try:
lastBlockNumber = loadLastBlock();
except IOError:
logger.info("block_data file not found, that's OK we're just going to start from 0 then...");
lastBlockNumber = 0;
while True:
# Load the normal transactions from etherscan starting from the last block we checked
api_params['startblock'] = lastBlockNumber;
txJson = requests.get(api_url, params=api_params, headers=api_headers);
# Parse the JSON data to get a list of transactions
txs = json.loads(txJson.text)['result'];
# We're going to assume that the first block found is the newest and we haven't missed any
# This may not always be true...
tx = txs[0]
# Check to make sure we actually have new data
if(tx['blockNumber'] == lastBlockNumber):
logger.info("No new transactions");
else:
# Generate the tweet text
tweet = u"\U0001F38A " + tx['from'] + \
" spent " + str(long(tx['value'])/10e+17) + " #Ethereum at " + \
"<URL TO YOUR DAPP>, check it out!" + u"\U0001F38A"
# Log some information for us to keep track of what the bot is doing
logger.info("New transaction found:")
logger.info("From: " + tx['from']);
logger.info("Tx Hash: " + tx['hash']);
logger.info("Trying to send tweet:\r\n" + tweet);
# Try to send the tweet, if it doe
try:
api.update_status(tweet);
logger.info("Tweet sent!");
except:
logger.info("Failed to send tweet...")
exit();
# We had new data so save the last block number
lastBlockNumber = txs[0]['blockNumber'];
saveLastBlock(txs[0]['blockNumber']);
# Sleep for 30 seconds
time.sleep(30);
# Only execute if invoked as a main module
if __name__ == "__main__":
main()
Anyone with a rudimentary understanding of Python should be able to suss out what is going on here, but I'll run you through the code
- At the beginning we have our standard includes which includes requests, and Tweepy which are used to interact with Etherscan and Twitter respectively
- Declare a logging object to log things to the console, this is useful if you plan to deploy the bot to a docker instance since you'll get feedback within the container logs
- Define the variables to hold the Etherscan API url, and the parameters and headers for the request. You should note that you would need to specify your own address and API key within the parameters or else the bot won't work.
- Define the variables to create the Tweepy OAuth handler, as well as the Tweepy API object
- Next we try to authenticate to Twitter using the keys supplied to the OAuth handler, if all goes well we are authenticated otherwise there's been a problem and we exit
- 3 Functions are defined, loadLastBlock and saveLastBlock which save the block number of the last read block to a text file called block_data, and decodeMessage which decodes the first group of text in the Input Data section of the transaction. You will want to replace this function with one of your own instead.
- Now we begin the main function
- First try to load the data from block_data, it's not a problem if there's an error here we just need to either load the blocknumber and set lastBlockNumber to it or set it to 0
- Go into a infinite While loop
- set the startblock request parameters to lastBlockNumber
- perform the Etherscan API request and save the JSON data
- decode the JSON data and store the results JSON data, these are the transactions
- Store the first result, in this bot we assume this is the most recent data and don't bother processing anything older. This is so our tweets should occur only when new data is receive and we shouldn't be tweeting any old transactions
- Check the block number, if it's > than lastBlockNumber this is a new block we've not seen before
- Put together a string that we'll be tweeting out with some arbitrary values from the transaction, in this example it's the from address as well as the transaction value
- Log that same information
- Try to call update_status which will send our tweet, providing we don't get an error our tweet should get sent out
- Sleep 30 seconds which should be the time between Ethereum blocks on average
Whew! What a run down, luckily this is all really simple and that should make it even easier to understand. Hopefully this helps you set up a twitter bot for your own uses and apps. Thanks for reading, have a great day!