├── README.md ├── notify_email.py ├── notify_telegram.py ├── config.py └── notify.py /README.md: -------------------------------------------------------------------------------- 1 | CoreWatchPush 2 | ===================================== 3 | 4 | What is CoreWatchPush? 5 | ---------------- 6 | 7 | Watch a bunch of xpubs via Bitcoin Core and get notifications on new transactions. 8 | 9 | Currently supported is Telegram as push notification channel. 10 | 11 | Setup 12 | ------------------- 13 | 14 | * Make sure you have the right xpub(s) in your `config.py` 15 | * **Make sure you use an empty wallet** (Multiwallet is currently not well suppored) 16 | * Set the path to your Bitcoin-CLI as well as the optional datadir in `config.py` 17 | 18 | * **Make sure your `bitcoin.conf` has set `-walletnotify=/path/to/notify.py` 19 | 20 | Test your import 21 | ``` 22 | ./notify.py importtest 23 | ``` 24 | 25 | Import your xpubs 26 | ``` 27 | ./notify.py import 28 | ``` 29 | 30 | Rescan old blocks 31 | ``` 32 | ./notify.py rescan 33 | #OR# 34 | ./notify.py rescan 35 | ``` 36 | -------------------------------------------------------------------------------- /notify_email.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from config import * 4 | 5 | from email.utils import make_msgid 6 | 7 | import sys 8 | import smtplib 9 | import datetime 10 | 11 | class Notify_SMTP: 12 | def push(text="test"): 13 | smtpObj = smtplib.SMTP(Config.notify_email_smtphost) 14 | smtpObj.login(Config.notify_email_smtpuser, Config.notify_email_smtppass) 15 | 16 | message = "Content-Type: text/plain; charset=utf-8\nMessage-ID: "+make_msgid(None, "corewatchpush.local")+"\nDate: "+datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S %Z')+"\n"+"Content-Disposition: inline\nContent-Transfer-Encoding: 8bit\nFrom: "+Config.notify_email_sender+"\nTo: "+Config.notify_email_receiver+"\n"+"Subject: CoreWatchPush"+"\n\r\n\r" 17 | message += text 18 | smtpObj.sendmail(Config.notify_email_sender, [Config.notify_email_receiver], message.encode("utf8")) 19 | 20 | if __name__ == '__main__': 21 | if len(sys.argv) == 2: 22 | Notify_SMTP.push(sys.argv[1]) -------------------------------------------------------------------------------- /notify_telegram.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2018 The CoreWatchPush developers 3 | # Distributed under the MIT software license, see the accompanying 4 | # file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | from config import * 7 | 8 | import sys 9 | import http.client 10 | import urllib.parse 11 | 12 | # telegram send message helper 13 | class Notify_Telegram: 14 | def push(text="test"): 15 | conn = http.client.HTTPSConnection('api.telegram.org') 16 | params = urllib.parse.urlencode({'chat_id': Config.notify_telegram_chat_id, 'parse_mode': 'markdown', 'disable_web_page_preview': '1', 'text': text}) 17 | conn.request('POST', '/bot'+Config.notify_telegram_key+'/sendMessage', params, {"Content-type": "application/x-www-form-urlencoded"}) 18 | response = conn.getresponse() 19 | #print(response.read().decode()) 20 | 21 | # can be run directly with ./notify_telegram.py 22 | if __name__ == '__main__': 23 | if len(sys.argv) == 2: 24 | Notify_Telegram.push(sys.argv[1]) -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | #always use a BIP44 account key 3 | #keys will be derived at /0/* and /1/* (internal) 4 | #if you have an ypub or zpub, use Electrums CLI to conert it to xpub ("convert_xkey(, "standard")") 5 | bip44_account_xpubs=[["xpub6BYyMLt46C5p4tQ28bFykNX9HN849dbCnhKQTdCz7dBPBTvDANv66rExpGNZdYXShSemd38NDGo4iEigRjy23ycftPv4vchMKdfTG54LcoX", "My Old Wallet", ["pkh()"]], 6 | ["xpub6AqZNvH9ujEmBj9QoKSw7U7jyqy3uPukpTcKDPhhUDE1v2tbHRgXfcKb5MKv2rGYh1Nv2cYyaHEY4zVZ7ACK2DYinTA7G61m2UeR69zdguv", "My New Wallet", ["wpkh()"]], 7 | ["xpub6A9rFqRrf5tpnKRLCGdEtUnPJPnjZ4XG48ugxbBtK3mYCdNuNUK8vX4NjGDnfctGkcrJCd2EUDi3chJrGuzKiNT8FWywWUmfijymrgKutSV", "My Other Wallet"] 8 | ] 9 | 10 | # the range to derive addresses up to (for each script_type) 11 | xpub_range=1000 12 | 13 | # default script types to derive if no script type is given in bip44_account_xpubs 14 | script_types = ["pkh()", "wpkh()", "sh(wpkh())"] 15 | 16 | # bitcoin cli location 17 | # requires Bitcoin Core 0.18 (or newer) 18 | bitcoin_cli="/btc/apps/bitcoin-0.18.0/bin/bitcoin-cli" 19 | 20 | # bitcoin 21 | bitcoin_cli_args="--datadir=/somewhere/bitcoin" 22 | 23 | # name of the wallet 24 | # WARNING: CoreWatchPush is not really multiwallet capable 25 | bitcoin_wallet_name="" #use empty string for default wallet 26 | 27 | # optional blockexplorer link 28 | blockexplorer = "https://bitcointools.jonasschnelli.ch/explorer/index.php?search=" 29 | 30 | # push channels (only telegram for now) 31 | notify_channels=["telegram"] 32 | #notify_channels=["telegram", "email"] #enable in case you want email and smtp 33 | 34 | # telegram KEY and chat-ID 35 | # google the internet how to create a bot, get the key as well as a chat-id 36 | notify_telegram_key="###" 37 | notify_telegram_chat_id="###" 38 | 39 | # email notification SMTP 40 | notify_email_receiver = "You " 41 | notify_email_sender = "autosend " 42 | notify_email_smtphost = "your.smtp.host" 43 | notify_email_smtpuser = "your_smtp_username" 44 | notify_email_smtppass = "your_smtp_password" 45 | -------------------------------------------------------------------------------- /notify.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2018 The CoreWatchPush developers 3 | # Distributed under the MIT software license, see the accompanying 4 | # file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | from config import * 7 | 8 | from notify_telegram import * 9 | from notify_email import * 10 | 11 | import json 12 | import subprocess 13 | import sys 14 | from datetime import datetime 15 | 16 | class Bitcoin_CLI: 17 | def call(method, args=[]): 18 | cmdbase = [Config.bitcoin_cli] 19 | if len(Config.bitcoin_cli_args) > 0: 20 | cmdbase.append(Config.bitcoin_cli_args) 21 | if len(Config.bitcoin_wallet_name) > 0: 22 | cmdbase.append("-rpcwallet="+Config.bitcoin_wallet_name) 23 | cmd = cmdbase 24 | cmd.append(method) 25 | if len(args) > 0: 26 | cmd = cmd + args 27 | result = subprocess.run(cmd, stdout=subprocess.PIPE) 28 | return result.stdout.decode() 29 | 30 | if __name__ == '__main__': 31 | if len(sys.argv) >= 2 and (sys.argv[1] == "rescan"): 32 | args = [] 33 | if len(sys.argv) == 4: 34 | args.append(sys.argv[2]) 35 | args.append(sys.argv[3]) 36 | Bitcoin_CLI.call("rescanblockchain", args) 37 | 38 | if len(sys.argv) == 2 and (sys.argv[1] == "import" or sys.argv[1] == "importtest"): 39 | for xpub_array in Config.bip44_account_xpubs: 40 | xpub = xpub_array[0] 41 | script_array = Config.script_types 42 | label = "undefined" 43 | if len(xpub_array) >= 2: 44 | label = xpub_array[1] 45 | if len(xpub_array) == 3: 46 | script_array = xpub_array[2] 47 | 48 | for script_type in script_array: 49 | parsed_ext = json.loads(Bitcoin_CLI.call("getdescriptorinfo", [script_type.replace("()", "("+xpub+"/0/*)")])) 50 | parsed_int = json.loads(Bitcoin_CLI.call("getdescriptorinfo", [script_type.replace("()", "("+xpub+"/1/*)")])) 51 | if sys.argv[1] == "importtest": 52 | print("XPUB TEXT, label: "+label+"\n") 53 | print(script_type) 54 | print(Bitcoin_CLI.call("deriveaddresses", [parsed_ext['descriptor'], "10"])) 55 | else: 56 | parsed = json.loads(Bitcoin_CLI.call("importmulti", ['[{"desc": "'+parsed_ext['descriptor']+'", "range": '+str(Config.xpub_range)+', "watchonly": true, "internal": false, "timestamp": 0, "label": "'+label+'"}]', '{"rescan": false}'])) 57 | parsed = json.loads(Bitcoin_CLI.call("importmulti", ['[{"desc": "'+parsed_int['descriptor']+'", "range": '+str(Config.xpub_range)+', "watchonly": true, "internal": true, "timestamp": 0}]', '{"rescan": false}'])) 58 | 59 | 60 | if len(sys.argv) == 2 and len(sys.argv[1]) == 64: 61 | txid = sys.argv[1] 62 | parsed = json.loads(Bitcoin_CLI.call("gettransaction", [txid, "true"])) 63 | time = str(datetime.fromtimestamp(parsed['time'])) 64 | text = "*new transaction*\n" 65 | text += "*time:* "+time+"\n"+"*confirmations:* "+str(parsed['confirmations'])+"\n"+"*txid:* "+parsed['txid']+"\n"; 66 | 67 | if len(parsed['details']) > 0: 68 | for item in parsed['details']: 69 | label="" 70 | if item.get('label'): 71 | label = item['label']+" / " 72 | if item['category'] == "send": 73 | text += "\U0001F534" 74 | else: 75 | text += "\U00002705" 76 | text += "*"+item['category']+"* amount: "+str(item['amount'])+" via: "+label+item['address']+"\n" 77 | 78 | parsed = json.loads(Bitcoin_CLI.call("getbalances")) 79 | text += "*New trusted balance:* "+str(parsed['watchonly']['trusted'])+"\n" 80 | text += "*New untrusted balance:* "+str(parsed['watchonly']['untrusted_pending'])+"\n" 81 | if (len(Config.blockexplorer) > 0): 82 | text += "[Blockexplorer]("+Config.blockexplorer+txid+")\n" 83 | Notify_Telegram.push(text) if "telegram" in Config.notify_channels: 84 | Notify_Telegram.push(text) 85 | if "email" in Config.notify_channels: 86 | Notify_SMTP.push(text) --------------------------------------------------------------------------------