-
Notifications
You must be signed in to change notification settings - Fork 0
/
twitter.py
267 lines (224 loc) Β· 10.2 KB
/
twitter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
import os
import time
import requests
from web3 import Web3
import tweepy
from dotenv import load_dotenv
import json
import logging
import random
LAST_BLOCKS_FILE = 'data/last_blocks.json'
DONATION_BLOCKS_FILE = 'data/last_donation_block.json'
load_dotenv()
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Twitter API credentials
consumer_key = os.getenv("CONSUMER_KEY")
consumer_secret = os.getenv("CONSUMER_SECRET")
access_token = os.getenv("ACCESS_TOKEN")
access_token_secret = os.getenv("ACCESS_TOKEN_SECRET")
client = tweepy.Client(
consumer_key=consumer_key, consumer_secret=consumer_secret,
access_token=access_token, access_token_secret=access_token_secret
)
logging.info("Twitter client initialized.")
# Web3 for converting Wei to Ether
w3 = Web3()
# Endpoints
proposed_blocks_url = "https://sp-api.dappnode.io/memory/proposedblocks"
wrong_fee_blocks_url = "https://sp-api.dappnode.io/memory/wrongfeeblocks"
donations_blocks_url = "https://sp-api.dappnode.io/memory/donations"
# By default, latest block twitted is 0
last_proposed_block = 0
last_wrong_fee_block = 0
# This should be saved to data too
last_twit_index = 0
happy_emojis = ["π₯³", "π", "π"]
sad_emojis = ["π", "π", "π’"]
happy_phrases = [
"From a βοΈ Smooth Operator π",
"Courtesy of a π€οΈ Smooth Operator π€",
"Brought to you by a π¬οΈ Smooth Operator π",
]
sad_phrases = [
"Unfortunately, ",
"Regrettably, ",
"Sadly, ",
]
check_block_phrases = [
"More info at:",
"Link to the slot:",
"Check the slot here:",
]
def save_last_block(endpoint, block_number):
data = {}
try:
with open(LAST_BLOCKS_FILE, 'r') as file:
data = json.load(file)
except (FileNotFoundError, json.JSONDecodeError):
logging.warning("Could not load the last block data file; starting fresh.")
data[endpoint] = block_number
with open(LAST_BLOCKS_FILE, 'w') as file:
json.dump(data, file)
logging.info(f"Saved last block for {endpoint}: {block_number}")
def load_last_block(endpoint):
try:
with open(LAST_BLOCKS_FILE, 'r') as file:
data = json.load(file)
logging.info(f"Successfully Loaded last block for {endpoint}: {data.get(endpoint, 0)}")
return data.get(endpoint, 0)
except (FileNotFoundError, json.JSONDecodeError):
logging.warning("Failed to load last block data, defaulting to 0.")
return 0
def save_last_donation_block(block_number):
"""Save the last donation block number to the donation blocks file."""
try:
with open(DONATION_BLOCKS_FILE, 'w') as file:
json.dump({'last_donation_block': block_number}, file)
logging.info(f"Saved last donation block: {block_number}")
except Exception as e:
logging.error(f"Error saving last donation block: {e}")
def load_last_donation_block():
"""Load the last donation block number from the donation blocks file."""
try:
with open(DONATION_BLOCKS_FILE, 'r') as file:
data = json.load(file)
last_donation_block = data.get('last_donation_block', 0)
logging.info(f"Successfully Loaded last donation block: {last_donation_block}")
return last_donation_block
except (FileNotFoundError, json.JSONDecodeError):
logging.warning("Failed to load last donation block data, defaulting to 0.")
return 0
# Load last blocks
last_proposed_block = load_last_block('proposed_blocks')
last_wrong_fee_block = load_last_block('wrong_fee_blocks')
# Load last donation block
last_donation_block = load_last_donation_block()
def fetch_data(url):
try:
response = requests.get(url, timeout=10) # 10 seconds timeout
response.raise_for_status() # This will raise an HTTPError if the HTTP request returned an unsuccessful status code
return response.json()
except requests.RequestException as e:
logging.error(f"Network error occurred when calling the Oracle: {e}")
return None
def shorten_address(address):
# Shorten the address to the first 6 and last 4 characters
return address[:6] + '...' + address[-4:]
def tweet_new_block(block_data):
global last_twit_index
reward_eth = w3.from_wei(int(block_data['reward_wei']), 'ether')
# Local index variables for this function call
local_emoji_index = last_twit_index
local_phrase_index = last_twit_index
local_ending_index = last_twit_index
party_emoji = happy_emojis[local_emoji_index] if reward_eth > 0.1 else ""
phrase = happy_phrases[local_phrase_index]
ending = check_block_phrases[local_ending_index]
slot_url = f"https://beaconcha.in/slot/{block_data['slot']}"
tweet = (
f"π° NEW BLOCK IN SMOOTH π°\n\n"
f"Reward: {reward_eth:.4f} ETH {party_emoji}\n\n"
f"{phrase}\n"
f"Proposer Validator Index: {block_data['validator_index']}\n\n"
f"{ending} {slot_url}"
)
try:
client.create_tweet(text=tweet)
logging.info(f"Successfully tweeted new proposed block: {tweet}")
except tweepy.TweepyException as e:
logging.error(f"Error while tweeting new wrong fee block: {e}")
# Increment global index at the end of the function
last_twit_index = (last_twit_index + 1) % len(happy_emojis) # Assuming all lists are the same length
def tweet_wrong_fee_block(block_data):
global last_twit_index
amount_eth = w3.from_wei(int(block_data['reward_wei']), 'ether')
shortened_address = shorten_address(block_data['withdrawal_address'])
# Local index variables for this function call
local_emoji_index = last_twit_index
local_phrase_index = last_twit_index
local_ending_index = last_twit_index
sad_emoji = sad_emojis[local_emoji_index]
sad_phrase = sad_phrases[local_phrase_index]
ending = check_block_phrases[local_ending_index]
slot_url = f"https://beaconcha.in/slot/{block_data['slot']}"
tweet = (
f"β BANNED FROM SMOOTH β\n\n"
f"{sad_emoji} {sad_phrase}validator {shortened_address} has been banned for sending {amount_eth:.4f} ETH out of the pool {sad_emoji} \n\n"
f"{ending} {slot_url}"
)
try:
client.create_tweet(text=tweet)
logging.info(f"Successfully tweeted new block: {tweet}")
except tweepy.TweepyException as e:
logging.error(f"Error while tweeting new block: {e}")
# Increment global index at the end of the function
last_twit_index = (last_twit_index + 1) % len(sad_emojis) # Assuming all lists are the same length
def fetch_donations_data(url):
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
logging.error(f"Network error occurred when calling the Donations API: {e}")
return None
def tweet_new_donation(donation_block):
amount_wei = int(donation_block['amount_wei'])
if amount_wei < 100000000000000000: # Check if donation is less than 0.1 ETH
logging.info("Donation amount is less than 0.1 ETH. Skipping tweet.")
return # Exit the function and do not tweet
amount_eth = w3.from_wei(amount_wei, 'ether')
donor_address = donation_block['sender']
transaction_url = f"https://beaconcha.in/tx/{donation_block['tx_hash']}"
# Determine the number of happy emojis based on the donation amount
extra_emojis = ""
if amount_wei >= 1000000000000000000: # 1 ETH in Wei
extra_emojis = " π₯³πππ"
# Different tweet variations
tweet_variations = [
f"π New Donation! π\n\nAmount: {amount_eth:.4f} ETH\nDonor: {donor_address}\n\nThank you for your support! π{extra_emojis}\n\nTransaction URL: {transaction_url}",
f"π We received a new donation! π\n\nAmount: {amount_eth:.4f} ETH\nDonor: {donor_address}\n\nYour generosity is greatly appreciated! π{extra_emojis}\n\nTransaction URL: {transaction_url}",
f"π Thank you for your contribution! π\n\nAmount: {amount_eth:.4f} ETH\nDonor: {donor_address}\n\nWe're grateful for your support! π{extra_emojis}\n\nTransaction URL: {transaction_url}"
]
# Choose a random tweet variation
tweet = random.choice(tweet_variations)
try:
client.create_tweet(text=tweet)
logging.info(f"Successfully tweeted new donation: {tweet}")
except tweepy.TweepyException as e:
logging.error(f"Error while tweeting new donation: {e}")
while True:
# Fetching data from proposed blocks
try:
logging.info("Fetching proposed blocks data from Oracle.")
proposed_blocks = fetch_data(proposed_blocks_url)
if proposed_blocks and proposed_blocks[-1]['block'] != last_proposed_block:
last_proposed_block = proposed_blocks[-1]['block']
tweet_new_block(proposed_blocks[-1])
save_last_block('proposed_blocks', last_proposed_block)
except Exception as e:
logging.error(f"Error while processing proposed blocks: {e}")
# Fetching data from wrong fee blocks
try:
logging.info("Fetching wrong fee blocks data from Oracle.")
wrong_fee_blocks = fetch_data(wrong_fee_blocks_url)
if wrong_fee_blocks and wrong_fee_blocks[-1]['block'] != last_wrong_fee_block:
last_wrong_fee_block = wrong_fee_blocks[-1]['block']
tweet_wrong_fee_block(wrong_fee_blocks[-1])
save_last_block('wrong_fee_blocks', last_wrong_fee_block)
except Exception as e:
logging.error(f"Error while processing wrong fee blocks: {e}")
# Fetching data from donation blocks
try:
logging.info("Fetching donation data from API.")
donations_block = fetch_donations_data(donations_blocks_url)
if donations_block:
latest_donation = donations_block[-1]
last_donation_block = latest_donation['block_number']
if last_donation_block != load_last_donation_block():
tweet_new_donation(latest_donation)
save_last_donation_block(last_donation_block)
except Exception as e:
logging.error(f"Error while processing donations data: {e}")
# Wait before next iteration
logging.info("Waiting for next update cycle.")
time.sleep(300) # wait for 5 minutes