def notify_tx(cursor: sqlite3.Cursor, txid: str) -> None: cursor.execute('select * from notified_tx where txid == ?', (txid, )) if cursor.fetchone() is not None: return try: result = coinrpc.call('decoderawtransaction', coinrpc.call('getrawtransaction', txid)) except: result = None if result is None: cursor.execute( 'insert into notified_tx(txid, time, confirmed) values(?, ?, ?)', (txid, int(time.time()), -1)) return account_found = False for vout in result.get('vout'): for address in vout.get('scriptPubKey').get('addresses'): cursor.execute( 'select account from account_address where address == ?', (address, )) account = cursor.fetchone() if account is None: continue account_found = True account = account[0] value = vout.get('value') cursor.execute( 'insert into notified_tx(txid, time, account, value) values(?, ?, ?, ?)', (txid, int(time.time()), account, value)) continue if not account_found: cursor.execute( 'insert into notified_tx(txid, time, confirmed) values(?, ?, ?)', (txid, int(time.time()), 2))
def exec_withdrawal(cursor: sqlite3.Cursor) -> None: cursor.execute( 'select address from withdrawal_req where completed == 0') addresses = {r[0] for r in cursor.fetchall()} if len(addresses) == 0: return params = {} for address in addresses: cursor.execute( 'select amount from withdrawal_req where address == ? and completed == 0', (address, )) req_amounts = [Decimal(str(r[0])) for r in cursor.fetchall()] amount = Decimal('0.0') for req_amount in req_amounts: amount = amount + req_amount params[address] = float(amount) try: txid = coinrpc.call('sendmany', '', params, MINCONF, '', list(addresses)) except: txid = None if txid is None: cursor.execute( 'select account, value from withdrawal_req where completed == 0' ) for req in cursor.fetchall(): account = req[0] value = Decimal(str(req[1])) cursor.execute( 'select balance from account_wallet where account == ?', (account, )) balance = Decimal(str(cursor.fetchone()[0])) balance = balance + value cursor.execute( 'update account_wallet set balance = ? where account == ?', (float(balance), account)) cursor.execute( 'update withdrawal_req set completed = -1 where completed == 0' ) return cursor.execute( 'update withdrawal_req set completed = 1 where completed == 0')
def check_tx(cursor: sqlite3.Cursor) -> None: cursor.execute( 'select txid from notified_tx where confirmed == 0 and time > ?', (int(time.time()) - 60 * MINCONF * 10, )) txids = {r[0] for r in cursor.fetchall()} if len(txids) == 0: return for txid in txids: result = coinrpc.call('gettransaction', txid) if result is None: cursor.execute( 'update notified_tx set confirmed = -1 where txid == ?', (txid, )) continue confirmations = result.get('confirmations') if confirmations < MINCONF: continue cursor.execute( 'select account, value from notified_tx where txid == ?', (txid, )) for account, value in cursor.fetchall(): cursor.execute( 'select balance from account_wallet where account == ?', (account, )) balance = Decimal(str(cursor.fetchone()[0])) value = Decimal(str(value)) balance = balance + value cursor.execute( 'update account_wallet set balance = ? where account == ?', (float(balance), account)) cursor.execute( 'update notified_tx set confirmed = 1 where txid == ?', (txid, ))
def get_account_address(cursor: sqlite3.Cursor, account: str) -> str: cursor.execute('insert or ignore into account_wallet(account) values(?)', (account, )) cursor.execute( 'select address from account_address where account == ? and time > ?', (account, int(time.time()) - 60 * 60 * 24 * 7)) address = cursor.fetchone() if address is None: address = coinrpc.call('getnewaddress') cursor.execute( 'insert into account_address(account, address, time) values(?, ?, ?)', (account, address, int(time.time()))) else: address = address[0] return address
def execute(text: str, user_id: str, screen_name: str, name: str, from_tweet: bool) -> Optional[str]: account = 'twitter-' + user_id name = name.split('@')[0] if not name.startswith('@') else name command = get_command(text) if command['method'] == None: return None if command['method'] == 'tip': if len(command['params']) < 2: text = 'tipkotoneの使い方をご確認ください! https://github.com/akarinS/tipkotone/blob/master/README.md' return get_message(text, screen_name) if from_tweet else get_message(text) if re.match('^@', command['params'][0]): to_screen_name = command['params'][0][1:] str_amount = command['params'][1] elif re.match('^@', command['params'][1]): to_screen_name = command['params'][1][1:] str_amount = command['params'][0] else: text = '宛先が間違っています・・・' return get_message(text, screen_name) if from_tweet else get_message(text) if to_screen_name == screen_name: text = '自身には投げ銭できません・・・' return get_message(text, screen_name) if from_tweet else get_message(text) if to_screen_name == BOTSCREENNAME: to_account = 'FREE' else: to_user = twitter.user(to_screen_name) if to_user.get('error') is not None: text = '宛先が見つかりませんでした・・・' return get_message(text, screen_name) if from_tweet else get_message(text) to_user_id = to_user['id_str'] to_account = 'twitter-' + to_user_id to_name = to_user['name'] to_name = to_name.split('@')[0] if not to_name.startswith('@') else name result = aw.move(account, to_account, str_amount) if result == 'wrong': text = '不正な金額です・・・' return get_message(text, screen_name) if from_tweet else get_message(text) elif result == 'few': text = '金額が小さすぎです・・・' return get_message(text, screen_name) if from_tweet else get_message(text) elif result == 'insufficient': text = '残高が足りません・・・' return get_message(text, screen_name) if from_tweet else get_message(text) amount = Decimal_to_str(result) if to_screen_name == BOTSCREENNAME: text = f'{amount}KOTO 寄付していただきありがとうございます!' return get_message(text, screen_name) if from_tweet else get_message(text) text = f'{name}さんから {to_name}さんへ お心付けです! {amount}KOTO' return get_message(text, screen_name, to_screen_name) if from_tweet else get_message(text) if command['method'] == 'balance': balance, confirming_balance = aw.get_account_balance(account) balance = Decimal_to_str(balance) if confirming_balance == 0: text = f'{name}さんの残高は {balance}KOTO です!' else: confirming_balance = Decimal_to_str(confirming_balance) text = f'{name}さんの残高は {balance}KOTO (+{confirming_balance}KOTO 承認中) です!' return get_message(text, screen_name) if from_tweet else get_message(text) if command['method'] == 'deposit': address = aw.get_account_address(account) text = f'{address} に送金してください!' return get_message(text, screen_name) if from_tweet else get_message(text) if command['method'] == 'withdraw': if len(command['params']) < 2: text = 'tipkotoneの使い方をご確認ください! https://github.com/akarinS/tipkotone/blob/master/README.md' return get_message(text, screen_name) if from_tweet else get_message(text) if re.match('^(k|jz)', command['params'][0]): address = command['params'][0] str_amount = command['params'][1] elif re.match('^(k|jz)', command['params'][1]): address = command['params'][1] str_amount = command['params'][0] else: text = 'アドレスが間違っています・・・' return get_message(text, screen_name) if from_tweet else get_message(text) if not coinrpc.call('validateaddress', address)['isvalid']: text = 'アドレスが間違っています・・・' return get_message(text, screen_name) if from_tweet else get_message(text) result = aw.add_withdrawal_request(account, address, str_amount) if result == 'wrong': text = '不正な金額です・・・' return get_message(text, screen_name) if from_tweet else get_message(text) elif result == 'few': text = '金額が小さすぎです・・・' return get_message(text, screen_name) if from_tweet else get_message(text) elif result == 'insufficient': text = '残高が足りません・・・' return get_message(text, screen_name) if from_tweet else get_message(text) amount = Decimal_to_str(result) text = f'{amount}KOTO の出金リクエストを受け付けました!' return get_message(text, screen_name) if from_tweet else get_message(text) if command['method'] == 'help': text = 'tipkotoneの使い方はこちらです! https://github.com/akarinS/tipkotone/blob/master/README.md' return get_message(text, screen_name) if from_tweet else get_message(text) if command['method'] == 'follow': twitter.follow(user_id) text = 'フォローしました!' return get_message(text, screen_name) if from_tweet else get_message(text) return None