forked from mitche50/NanoTipBot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtipcheck.py
executable file
·356 lines (301 loc) · 16.8 KB
/
tipcheck.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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
#!/usr/bin/env python3
# DEPENDENCIES =========================================
from datetime import datetime
from decimal import Decimal
import configparser
import json
import logging
import nano
import os
import requests
import telegram
import tweepy
from TwitterAPI import TwitterAPI
from modules.currency import get_pow, receive_pending
from modules.db import get_db_data, set_db_data
from modules.social import send_dm
# CONFIG CONSTANTS =====================================
# Read config and parse constants
config = configparser.ConfigParser()
config.read('{}/webhookconfig.ini'.format(os.getcwd()))
CURRENCY = config.get('main', 'currency')
CONVERT_MULTIPLIER = {
'nano': 1000000000000000000000000000000,
'banano': 100000000000000000000000000000
}
CONSUMER_KEY = config.get(CURRENCY, 'consumer_key')
CONSUMER_SECRET = config.get(CURRENCY, 'consumer_secret')
ACCESS_TOKEN = config.get(CURRENCY, 'access_token')
ACCESS_TOKEN_SECRET = config.get(CURRENCY, 'access_token_secret')
DB_HOST = config.get('main', 'host')
DB_USER = config.get('main', 'user')
DB_PW = config.get('main', 'password')
DB_SCHEMA = config.get(CURRENCY, 'schema')
WALLET = config.get(CURRENCY, 'wallet')
NODE_IP = config.get(CURRENCY, 'node_ip')
BOT_ACCOUNT = config.get(CURRENCY, 'bot_account')
WORK_SERVER = config.get(CURRENCY, 'work_server')
WORK_KEY = config.get(CURRENCY, 'work_key')
TELEGRAM_KEY = config.get(CURRENCY, 'telegram_key')
MIN_TIP = config.get(CURRENCY, 'min_tip')
# Connect to Twitter
auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
api = tweepy.API(auth)
# Secondary API for non-tweepy supported requests
twitterAPI = TwitterAPI(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
# Connect to Nano Node
rpc = nano.rpc.Client(NODE_IP)
# Connect to Telegram
telegram_bot = telegram.Bot(token=TELEGRAM_KEY)
# Set Log File
logging.basicConfig(handlers=[logging.FileHandler('{}/tipreturn.log'.format(os.getcwd()), 'a', 'utf-8')],
level=logging.INFO)
def unregistered_user_reminder(day_difference, dm_text):
"""
Check for unregistered users day_difference away from the current day and send a DM with the provided dm_text
"""
db_call = ("SELECT user_id, system "
"FROM users "
"WHERE register = 0 "
"AND DATE(created_ts) BETWEEN DATE_SUB(NOW(), INTERVAL {} DAY) "
"AND DATE_SUB(NOW(), INTERVAL {} DAY)").format(day_difference, (day_difference - 1))
try:
unregistered_users = get_db_data(db_call)
except Exception as e:
logging.info(e)
raise e
logging.info("unregistered_users: {}".format(unregistered_users))
for user in unregistered_users:
try:
send_dm(user[0], dm_text, user[1])
logging.info("{}: User {} reminded after {} days.".format(str(datetime.now()), user[0], day_difference))
except tweepy.TweepError as e:
logging.info("{}: Tweepy Error: {}".format(datetime.now(), e))
except Exception as e:
logging.info("{}: Exception: {}".format(datetime.now(), e))
raise e
def send_returned_notice_to_receivers():
"""
Notify all unregistered users that their tips are being returned.
"""
unregistered_users_call = ("SELECT DISTINCT tip_list.receiver_id, tip_list.system FROM tip_list "
"INNER JOIN users "
"ON tip_list.receiver_id = users.user_id AND tip_list.system = users.system "
"WHERE DATE(tip_list.timestamp) < DATE_SUB(now(), interval 30 day) "
"AND users.register = 0 "
"AND tip_list.processed = 9;")
unregistered_users_data = get_db_data(unregistered_users_call)
# for user in unregistered_users_data:
# send_dm(user[0], "You had tips that were sent 30 or more days ago, and you haven't registered your account. "
# "These tips were returned to the sender so they can continue to spread {} to others. "
# "If you would like to keep any future tips, please register to prevent any more returns!"
# .format(CURRENCY.title()),
# user[1])
mark_notified("receivers")
def mark_notified(user_type):
"""
Set the DB to show that the receivers were identified.
"""
if user_type == 'receivers':
processed_num = 9
else:
processed_num = 8
notified_users_call = ("SELECT dm_id "
"FROM tip_list "
"WHERE processed = {}".format(processed_num))
notified_users = get_db_data(notified_users_call)
for id in notified_users:
notified_send_call = ("UPDATE tip_list "
"SET processed = %s "
"WHERE dm_id = %s")
notified_send_values = [(processed_num - 1), id[0]]
set_db_data(notified_send_call, notified_send_values)
def send_returned_notice_to_senders():
"""
Notify all users who sent tips which were returned that their balance has been updated.
"""
sender_return_notice_call = ("SELECT tip_list.sender_id, tip_list.system, sum(tip_list.amount) "
"FROM tip_list "
"INNER JOIN users "
"ON tip_list.receiver_id = users.user_id AND tip_list.system = users.system "
"WHERE DATE(tip_list.timestamp) < DATE_SUB(now(), interval 30 day) "
"AND users.register = 0 "
"AND tip_list.processed = 8 "
"GROUP BY tip_list.sender_id, tip_list.system;")
sender_return_list = get_db_data(sender_return_notice_call)
sender_return_names = ("SELECT tip_list.sender_id, tip_list.system, users.user_name "
"FROM tip_list "
"INNER JOIN users "
"ON tip_list.receiver_id = users.user_id AND tip_list.system = users.system "
"WHERE DATE(tip_list.timestamp) < DATE_SUB(now(), interval 30 day) "
"AND users.register = 0 "
"AND tip_list.processed = 8;")
sender_return_name_list = get_db_data(sender_return_names)
return_dict = {}
for sender in sender_return_name_list:
sender_comp = str(sender[0]) + "-" + str(sender[1])
if sender_comp not in return_dict.keys():
return_dict[sender_comp] = [sender[2]]
else:
return_dict[sender_comp].append(sender[2])
# for sender in sender_return_list:
# donation_amount, send_amount = calculate_donation_amount(sender[2], sender[0], sender[1])
# sender_comp = str(sender[0]) + "-" + str(sender[1])
# logging.info("send amount in Nano = {}".format(Decimal(str(send_amount / CONVERT_MULTIPLIER[CURRENCY]))))
# send_dm(sender[0], "You've had tips returned to your account due to the following list of users "
# "not registering: {}. Your account has been credited {} {}. Continue spreading the "
# "love or withdraw to your wallet!".format(return_dict[sender_comp],
# Decimal(str(send_amount / CONVERT_MULTIPLIER[CURRENCY])),
# CURRENCY.upper()),
# sender[1])
mark_notified("senders")
def calculate_donation_amount(amount, sender_id, system):
donation_call = ("SELECT donation_percent FROM donation_info "
"WHERE user_id = {} AND system = '{}'").format(sender_id, system)
logging.info("{}: donation amount check call: {}".format(datetime.now(), donation_call))
donation_raw = get_db_data(donation_call)
donation_percent = Decimal(str(donation_raw[0][0] * .01))
if amount * donation_percent >= float(MIN_TIP):
donation = amount * donation_percent
if CURRENCY == 'banano':
donation = round(donation)
else:
donation = round(donation, 5)
amount -= donation
donation_amount = int(donation * CONVERT_MULTIPLIER[CURRENCY])
send_amount = int(amount * CONVERT_MULTIPLIER[CURRENCY])
else:
donation = 0
donation_amount = 0
send_amount = int(amount * CONVERT_MULTIPLIER[CURRENCY])
return donation_amount, send_amount
def return_tips():
tips_to_return_call = ("SELECT tip_list.dm_id, tip_list.sender_id, "
"users.account, tip_list.amount, tip_list.system "
"FROM tip_list "
"INNER JOIN users "
"ON tip_list.receiver_id = users.user_id AND tip_list.system = users.system "
"WHERE DATE(tip_list.timestamp) < DATE_SUB(now(), interval 7 day) "
"AND users.register = 0 "
"AND tip_list.processed = 2;")
tip_list = get_db_data(tips_to_return_call)
for tip in tip_list:
transaction_id = tip[0]
sender_id = tip[1]
receiver_account = tip[2]
amount = Decimal(str(tip[3]))
system = tip[4]
logging.info("{}: Returning tip {}".format(datetime.now(), transaction_id))
sender_account_call = "SELECT account FROM users WHERE user_id = {} AND system = '{}'".format(sender_id, system)
sender_account_info = get_db_data(sender_account_call)
sender_account = sender_account_info[0][0]
donation_amount, send_amount = calculate_donation_amount(amount, sender_id, system)
logging.info("donation amount: {}".format(donation_amount))
logging.info("send_amount: {} - {}".format(amount, send_amount))
receive_pending(receiver_account)
work = get_pow(receiver_account)
try:
if work == '':
send_hash = rpc.send(wallet="{}".format(WALLET), source="{}".format(receiver_account),
destination="{}".format(sender_account), amount=send_amount)
if donation_amount > 0:
donation_hash = rpc.send(wallet="{}".format(WALLET), source="{}".format(receiver_account),
destination="{}".format(BOT_ACCOUNT), amount=donation_amount)
logging.info("{}: Donation sent from account {} under hash: {}".format(datetime.now(), receiver_account,
donation_hash))
else:
send_hash = rpc.send(wallet="{}".format(WALLET), source="{}".format(receiver_account),
destination="{}".format(sender_account), amount=send_amount, work=work)
if donation_amount > 0:
donation_work = get_pow(receiver_account)
donation_hash = rpc.send(wallet="{}".format(WALLET), source="{}".format(receiver_account),
destination="{}".format(BOT_ACCOUNT), amount=donation_amount, work=donation_work)
logging.info("{}: Donation sent from account {} under hash: {}".format(datetime.now(), receiver_account,
donation_hash))
logging.info("{}: Tip returned under hash: {}".format(str(datetime.now()), send_hash))
except nano.rpc.RPCException as e:
logging.info("{}: Insufficient balance to return {} raw and {} donation from account {}. Descriptive error: {}".format(datetime.now(),
send_amount,
donation_amount,
receiver_account,
e))
insufficient_balance_check = rpc.account_balance(receiver_account)
logging.info("Current balance: {}".format(insufficient_balance_check))
insufficient_balance_call = ("UPDATE tip_list "
"SET processed = 6 "
"WHERE dm_id = %s;")
insufficient_balance_values = [transaction_id,]
set_db_data(insufficient_balance_call, insufficient_balance_values)
continue
except Exception as f:
logging.info("{}: Unexpected error: {}".format(datetime.now(), f))
continue
update_tip_call = ("UPDATE tip_list "
"SET processed = 9 "
"WHERE dm_id = %s;")
update_tip_values = [transaction_id,]
try:
set_db_data(update_tip_call, update_tip_values)
except Exception as e:
logging.info("{}: Error updating tip to returned: {}".format(datetime.now(), e))
send_returned_notice_to_receivers()
send_returned_notice_to_senders()
def return_unused_balance():
get_inactive_users = ("SELECT user_id, system, account "
" FROM tip_bot.return_address "
" WHERE last_action < DATE_SUB(now(), interval 60 day) "
" AND account IS NOT NULL;")
inactive_users = get_db_data(get_inactive_users)
logging.info("{}: Returning inactive balances for user list: {}".format(datetime.now(), inactive_users))
for user in inactive_users:
get_tip_account = ("SELECT account FROM users "
"WHERE user_id = {} AND system = '{}'".format(user[0], user[1]))
tip_account_data = get_db_data(get_tip_account)
tip_account = tip_account_data[0][0]
print("Returning unused balance for user {} system {} account {} to ")
# check for any unreceived tips
receive_pending(tip_account)
# get balance of tip account
balance_data = {'action': 'account_balance', 'account': tip_account}
json_request = json.dumps(balance_data)
r = requests.post('{}'.format(NODE_IP), data=json_request)
rx = r.json()
balance_raw = rx['balance']
balance = Decimal(balance_raw) / CONVERT_MULTIPLIER[CURRENCY]
# send from tip account to return account
if Decimal(balance) > 0:
try:
donation_amount, send_amount = calculate_donation_amount(Decimal(balance), user[0], user[1])
work = get_pow(tip_account)
donation_hash = rpc.send(wallet=WALLET, source=tip_account, destination=BOT_ACCOUNT, work=work, amount=donation_amount)
work = get_pow(tip_account)
inactive_hash = rpc.send(wallet=WALLET, source=tip_account, destination=user[2], work=work, amount=send_amount)
except Exception as e:
logging.info("{}: ERROR: {}".format(datetime.now, e))
logging.info(
"{}: Inactive user {} on {} had their funds returned to their recovery address {} under hash {}".format(
datetime.now(),
user[0],
user[1],
user[2],
inactive_hash))
logging.info(
"{}: Inactive user {} on {} donated under hash {}".format(
datetime.now(),
user[0],
user[1],
user[2],
donation_hash))
else:
logging.info("{}: Balance for user {} on {} was 0".format(datetime.now(), user[0], user[1]))
def main():
# Check for users who need reminders - removed to prevent suspension in the future.
# unregistered_user_reminder(int(10), "Just a reminder that someone sent you a tip and you haven't registered your account yet! Reply to this message with !register to do so, then !help to see all my commands!")
# unregistered_user_reminder(int(20), "You still have not registered your account. If you do not register within 30 days, your tip will be returned. Please respond with !register to complete your registration, or !help to see my commands!")
# unregistered_user_reminder(int(29), "This is your final notice! If you do not register your account before tomorrow, your tips will be sent back to the users that tipped you!")
# Send back the tip to users not registered in 30 days
return_tips()
logging.info("{}: completed check for unregistered users.".format(datetime.now()))
return_unused_balance()
main()