├── .gitignore ├── LICENSE ├── README.md └── daemon.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2013 Bitmessage 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PyBitmessage-Daemon 2 | =================== 3 | 4 | PyBitmessage Daemon Client allows users to interact with Bitmessage through the command line. Bitmessage can be found here: https://github.com/Bitmessage/PyBitmessage 5 | 6 | It allows you to interact with Bitmessage via the api which unforatunately (as of Bitmessage version 0.3.5) is limited. 7 | 8 | Bitmessage API Reference: https://bitmessage.org/wiki/API_Reference 9 | 10 | Please make sure that you are using Python 2.7.5+ or if you would like to download Daemon.exe you can do that on my site here: http://addictronics.com/bitmessage.php Just scroll down to the Daemon section. 11 | 12 | Setup 13 | ===== 14 | 1. Install PyBitmessage and run it once. Close Bitmessage. 15 | 2. Download daemon.py into the same directory as Bitmessage. 16 | 3. Run Bitmessage. 17 | 4. Run daemon.py and it will either: automatically pull your API information from the keys.dat file or ask if you want to automatically add the API information to the keys.dat file. 18 | 5. Test the API connection with the command "apiTest". 19 | 20 | 21 | Features 22 | ===== 23 | 1. Change bitmessage settings (keys.dat) including setting api information, connection information, and daemon mode 24 | 2. Automatically load api information from bitmessage keys.dat file or create settings for remote connection 25 | 2. Send and receive messages or broadcasts with or without attachments 26 | 3. Reply to or forward messages 27 | 2. View the inbox/outbox 28 | 3. Save inbox/outbox messages to txt file 29 | 4. Create new identites 30 | 5. Subscribe to/unsubscribe from broadcast addresses 31 | 6. Get addresss from a passphrase without adding to identities 32 | 7. Delete individual or all messages from the inbox or outbox 33 | -------------------------------------------------------------------------------- /daemon.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.7.x 2 | # Created by Adam Melton (.dok) referenceing https://bitmessage.org/wiki/API_Reference for API documentation 3 | # Distributed under the MIT/X11 software license. See http://www.opensource.org/licenses/mit-license.php. 4 | 5 | # This is an example of a daemon client for PyBitmessage 0.4.2, by .dok (Version 0.3.0) 6 | 7 | 8 | import ConfigParser 9 | import xmlrpclib 10 | import datetime 11 | import hashlib 12 | import getopt 13 | import imghdr 14 | import ntpath 15 | import json 16 | import time 17 | import sys 18 | import os 19 | 20 | api = '' 21 | keysName = 'keys.dat' 22 | keysPath = 'keys.dat' 23 | usrPrompt = 0 #0 = First Start, 1 = prompt, 2 = no prompt if the program is starting up 24 | knownAddresses = dict() 25 | 26 | def userInput(message): #Checks input for exit or quit. Also formats for input, etc 27 | global usrPrompt 28 | print '\n' + message 29 | uInput = raw_input('> ') 30 | 31 | if (uInput.lower() == 'exit'): #Returns the user to the main menu 32 | usrPrompt = 1 33 | main() 34 | 35 | elif (uInput.lower() == 'quit'): #Quits the program 36 | print '\n Bye\n' 37 | sys.exit() 38 | os.exit() 39 | else: 40 | return uInput 41 | 42 | def restartBmNotify(): #Prompts the user to restart Bitmessage. 43 | print '\n *******************************************************************' 44 | print ' WARNING: If Bitmessage is running locally, you must restart it now.' 45 | print ' *******************************************************************\n' 46 | 47 | def safeConfigGetBoolean(section,field): 48 | global keysPath 49 | config = ConfigParser.SafeConfigParser() 50 | config.read(keysPath) 51 | 52 | try: 53 | return config.getboolean(section,field) 54 | except: 55 | return False 56 | 57 | #Begin keys.dat interactions 58 | def lookupAppdataFolder(): #gets the appropriate folders for the .dat files depending on the OS. Taken from bitmessagemain.py 59 | APPNAME = "PyBitmessage" 60 | from os import path, environ 61 | if sys.platform == 'darwin': 62 | if "HOME" in environ: 63 | dataFolder = path.join(os.environ["HOME"], "Library/Application support/", APPNAME) + '/' 64 | else: 65 | print ' Could not find home folder, please report this message and your OS X version to the Daemon Github.' 66 | os.exit() 67 | 68 | elif 'win32' in sys.platform or 'win64' in sys.platform: 69 | dataFolder = path.join(environ['APPDATA'], APPNAME) + '\\' 70 | else: 71 | dataFolder = path.expanduser(path.join("~", ".config/" + APPNAME + "/")) 72 | return dataFolder 73 | 74 | def configInit(): 75 | global keysName 76 | config = ConfigParser.SafeConfigParser() 77 | 78 | config.add_section('bitmessagesettings') 79 | config.set('bitmessagesettings', 'port', '8444') #Sets the bitmessage port to stop the warning about the api not properly being setup. This is in the event that the keys.dat is in a different directory or is created locally to connect to a machine remotely. 80 | config.set('bitmessagesettings','apienabled','true') #Sets apienabled to true in keys.dat 81 | 82 | with open(keysName, 'wb') as configfile: 83 | config.write(configfile) 84 | 85 | print '\n ' + str(keysName) + ' Initalized in the same directory as daemon.py' 86 | print ' You will now need to configure the ' + str(keysName) + ' file.\n' 87 | 88 | def apiInit(apiEnabled): 89 | global keysPath 90 | global usrPrompt 91 | config = ConfigParser.SafeConfigParser() 92 | config.read(keysPath) 93 | 94 | 95 | 96 | if (apiEnabled == False): #API information there but the api is disabled. 97 | uInput = userInput("The API is not enabled. Would you like to do that now, (Y)es or (N)o?").lower() 98 | 99 | if uInput == "y": # 100 | config.set('bitmessagesettings','apienabled','true') #Sets apienabled to true in keys.dat 101 | with open(keysPath, 'wb') as configfile: 102 | config.write(configfile) 103 | 104 | print 'Done' 105 | restartBmNotify() 106 | return True 107 | 108 | elif uInput == "n": 109 | print ' \n************************************************************' 110 | print ' Daemon will not work when the API is disabled. ' 111 | print ' Please refer to the Bitmessage Wiki on how to setup the API.' 112 | print ' ************************************************************\n' 113 | usrPrompt = 1 114 | main() 115 | 116 | else: 117 | print '\n Invalid Entry\n' 118 | usrPrompt = 1 119 | main() 120 | elif (apiEnabled == True): #API correctly setup 121 | #Everything is as it should be 122 | return True 123 | 124 | else: #API information was not present. 125 | print '\n ' + str(keysPath) + ' not properly configured!\n' 126 | uInput = userInput("Would you like to do this now, (Y)es or (N)o?").lower() 127 | 128 | if uInput == "y": #User said yes, initalize the api by writing these values to the keys.dat file 129 | print ' ' 130 | 131 | apiUsr = userInput("API Username") 132 | apiPwd = userInput("API Password") 133 | apiInterface = userInput("API Interface. (127.0.0.1)") 134 | apiPort = userInput("API Port") 135 | apiEnabled = userInput("API Enabled? (True) or (False)").lower() 136 | daemon = userInput("Daemon mode Enabled? (True) or (False)").lower() 137 | 138 | if (daemon != 'true' and daemon != 'false'): 139 | print '\n Invalid Entry for Daemon.\n' 140 | uInput = 1 141 | main() 142 | 143 | print ' -----------------------------------\n' 144 | 145 | config.set('bitmessagesettings', 'port', '8444') #sets the bitmessage port to stop the warning about the api not properly being setup. This is in the event that the keys.dat is in a different directory or is created locally to connect to a machine remotely. 146 | config.set('bitmessagesettings','apienabled','true') 147 | config.set('bitmessagesettings', 'apiport', apiPort) 148 | config.set('bitmessagesettings', 'apiinterface', '127.0.0.1') 149 | config.set('bitmessagesettings', 'apiusername', apiUsr) 150 | config.set('bitmessagesettings', 'apipassword', apiPwd) 151 | config.set('bitmessagesettings', 'daemon', daemon) 152 | with open(keysPath, 'wb') as configfile: 153 | config.write(configfile) 154 | 155 | print '\n Finished configuring the keys.dat file with API information.\n' 156 | restartBmNotify() 157 | return True 158 | 159 | elif uInput == "n": 160 | print '\n ***********************************************************' 161 | print ' Please refer to the Bitmessage Wiki on how to setup the API.' 162 | print ' ***********************************************************\n' 163 | usrPrompt = 1 164 | main() 165 | else: 166 | print ' \nInvalid entry\n' 167 | usrPrompt = 1 168 | main() 169 | 170 | 171 | def apiData(): 172 | global keysName 173 | global keysPath 174 | global usrPrompt 175 | 176 | config = ConfigParser.SafeConfigParser() 177 | config.read(keysPath) #First try to load the config file (the keys.dat file) from the program directory 178 | 179 | try: 180 | config.get('bitmessagesettings','port') 181 | appDataFolder = '' 182 | except: 183 | #Could not load the keys.dat file in the program directory. Perhaps it is in the appdata directory. 184 | appDataFolder = lookupAppdataFolder() 185 | keysPath = appDataFolder + keysPath 186 | config = ConfigParser.SafeConfigParser() 187 | config.read(keysPath) 188 | 189 | try: 190 | config.get('bitmessagesettings','port') 191 | except: 192 | #keys.dat was not there either, something is wrong. 193 | print '\n ******************************************************************' 194 | print ' There was a problem trying to access the Bitmessage keys.dat file' 195 | print ' or keys.dat is not set up correctly' 196 | print ' Make sure that daemon is in the same directory as Bitmessage. ' 197 | print ' ******************************************************************\n' 198 | 199 | uInput = userInput("Would you like to create a keys.dat in the local directory, (Y)es or (N)o?").lower() 200 | 201 | if (uInput == "y" or uInput == "yes"): 202 | configInit() 203 | keysPath = keysName 204 | usrPrompt = 0 205 | main() 206 | elif (uInput == "n" or uInput == "no"): 207 | print '\n Trying Again.\n' 208 | usrPrompt = 0 209 | main() 210 | else: 211 | print '\n Invalid Input.\n' 212 | 213 | usrPrompt = 1 214 | main() 215 | 216 | try: #checks to make sure that everyting is configured correctly. Excluding apiEnabled, it is checked after 217 | config.get('bitmessagesettings', 'apiport') 218 | config.get('bitmessagesettings', 'apiinterface') 219 | config.get('bitmessagesettings', 'apiusername') 220 | config.get('bitmessagesettings', 'apipassword') 221 | except: 222 | apiInit("") #Initalize the keys.dat file with API information 223 | 224 | #keys.dat file was found or appropriately configured, allow information retrieval 225 | apiEnabled = apiInit(safeConfigGetBoolean('bitmessagesettings','apienabled')) #if false it will prompt the user, if true it will return true 226 | 227 | config.read(keysPath)#read again since changes have been made 228 | apiPort = int(config.get('bitmessagesettings', 'apiport')) 229 | apiInterface = config.get('bitmessagesettings', 'apiinterface') 230 | apiUsername = config.get('bitmessagesettings', 'apiusername') 231 | apiPassword = config.get('bitmessagesettings', 'apipassword') 232 | 233 | print '\n API data successfully imported.\n' 234 | 235 | return "http://" + apiUsername + ":" + apiPassword + "@" + apiInterface+ ":" + str(apiPort) + "/" #Build the api credentials 236 | 237 | #End keys.dat interactions 238 | 239 | 240 | def apiTest(): #Tests the API connection to bitmessage. Returns true if it is connected. 241 | 242 | try: 243 | result = api.add(2,3) 244 | except: 245 | return False 246 | 247 | if (result == 5): 248 | return True 249 | else: 250 | return False 251 | 252 | def bmSettings(): #Allows the viewing and modification of keys.dat settings. 253 | global keysPath 254 | global usrPrompt 255 | config = ConfigParser.SafeConfigParser() 256 | keysPath = 'keys.dat' 257 | 258 | config.read(keysPath)#Read the keys.dat 259 | try: 260 | port = config.get('bitmessagesettings', 'port') 261 | except: 262 | print '\n File not found.\n' 263 | usrPrompt = 0 264 | main() 265 | 266 | startonlogon = safeConfigGetBoolean('bitmessagesettings', 'startonlogon') 267 | minimizetotray = safeConfigGetBoolean('bitmessagesettings', 'minimizetotray') 268 | showtraynotifications = safeConfigGetBoolean('bitmessagesettings', 'showtraynotifications') 269 | startintray = safeConfigGetBoolean('bitmessagesettings', 'startintray') 270 | defaultnoncetrialsperbyte = config.get('bitmessagesettings', 'defaultnoncetrialsperbyte') 271 | defaultpayloadlengthextrabytes = config.get('bitmessagesettings', 'defaultpayloadlengthextrabytes') 272 | daemon = safeConfigGetBoolean('bitmessagesettings', 'daemon') 273 | 274 | socksproxytype = config.get('bitmessagesettings', 'socksproxytype') 275 | sockshostname = config.get('bitmessagesettings', 'sockshostname') 276 | socksport = config.get('bitmessagesettings', 'socksport') 277 | socksauthentication = safeConfigGetBoolean('bitmessagesettings', 'socksauthentication') 278 | socksusername = config.get('bitmessagesettings', 'socksusername') 279 | sockspassword = config.get('bitmessagesettings', 'sockspassword') 280 | 281 | 282 | print '\n -----------------------------------' 283 | print ' | Current Bitmessage Settings |' 284 | print ' -----------------------------------' 285 | print ' port = ' + port 286 | print ' startonlogon = ' + str(startonlogon) 287 | print ' minimizetotray = ' + str(minimizetotray) 288 | print ' showtraynotifications = ' + str(showtraynotifications) 289 | print ' startintray = ' + str(startintray) 290 | print ' defaultnoncetrialsperbyte = ' + defaultnoncetrialsperbyte 291 | print ' defaultpayloadlengthextrabytes = ' + defaultpayloadlengthextrabytes 292 | print ' daemon = ' + str(daemon) 293 | print '\n ------------------------------------' 294 | print ' | Current Connection Settings |' 295 | print ' -----------------------------------' 296 | print ' socksproxytype = ' + socksproxytype 297 | print ' sockshostname = ' + sockshostname 298 | print ' socksport = ' + socksport 299 | print ' socksauthentication = ' + str(socksauthentication) 300 | print ' socksusername = ' + socksusername 301 | print ' sockspassword = ' + sockspassword 302 | print ' ' 303 | 304 | uInput = userInput("Would you like to modify any of these settings, (Y)es or (N)o?").lower() 305 | 306 | if uInput == "y": 307 | while True: #loops if they mistype the setting name, they can exit the loop with 'exit' 308 | invalidInput = False 309 | uInput = userInput("What setting would you like to modify?").lower() 310 | print ' ' 311 | 312 | if uInput == "port": 313 | print ' Current port number: ' + port 314 | uInput = userInput("Enter the new port number.") 315 | config.set('bitmessagesettings', 'port', str(uInput)) 316 | elif uInput == "startonlogon": 317 | print ' Current status: ' + str(startonlogon) 318 | uInput = userInput("Enter the new status.") 319 | config.set('bitmessagesettings', 'startonlogon', str(uInput)) 320 | elif uInput == "minimizetotray": 321 | print ' Current status: ' + str(minimizetotray) 322 | uInput = userInput("Enter the new status.") 323 | config.set('bitmessagesettings', 'minimizetotray', str(uInput)) 324 | elif uInput == "showtraynotifications": 325 | print ' Current status: ' + str(showtraynotifications) 326 | uInput = userInput("Enter the new status.") 327 | config.set('bitmessagesettings', 'showtraynotifications', str(uInput)) 328 | elif uInput == "startintray": 329 | print ' Current status: ' + str(startintray) 330 | uInput = userInput("Enter the new status.") 331 | config.set('bitmessagesettings', 'startintray', str(uInput)) 332 | elif uInput == "defaultnoncetrialsperbyte": 333 | print ' Current default nonce trials per byte: ' + defaultnoncetrialsperbyte 334 | uInput = userInput("Enter the new defaultnoncetrialsperbyte.") 335 | config.set('bitmessagesettings', 'defaultnoncetrialsperbyte', str(uInput)) 336 | elif uInput == "defaultpayloadlengthextrabytes": 337 | print ' Current default payload length extra bytes: ' + defaultpayloadlengthextrabytes 338 | uInput = userInput("Enter the new defaultpayloadlengthextrabytes.") 339 | config.set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(uInput)) 340 | elif uInput == "daemon": 341 | print ' Current status: ' + str(daemon) 342 | uInput = userInput("Enter the new status.").lower() 343 | config.set('bitmessagesettings', 'daemon', str(uInput)) 344 | elif uInput == "socksproxytype": 345 | print ' Current socks proxy type: ' + socksproxytype 346 | print "Possibilities: 'none', 'SOCKS4a', 'SOCKS5'." 347 | uInput = userInput("Enter the new socksproxytype.") 348 | config.set('bitmessagesettings', 'socksproxytype', str(uInput)) 349 | elif uInput == "sockshostname": 350 | print ' Current socks host name: ' + sockshostname 351 | uInput = userInput("Enter the new sockshostname.") 352 | config.set('bitmessagesettings', 'sockshostname', str(uInput)) 353 | elif uInput == "socksport": 354 | print ' Current socks port number: ' + socksport 355 | uInput = userInput("Enter the new socksport.") 356 | config.set('bitmessagesettings', 'socksport', str(uInput)) 357 | elif uInput == "socksauthentication": 358 | print ' Current status: ' + str(socksauthentication) 359 | uInput = userInput("Enter the new status.") 360 | config.set('bitmessagesettings', 'socksauthentication', str(uInput)) 361 | elif uInput == "socksusername": 362 | print ' Current socks username: ' + socksusername 363 | uInput = userInput("Enter the new socksusername.") 364 | config.set('bitmessagesettings', 'socksusername', str(uInput)) 365 | elif uInput == "sockspassword": 366 | print ' Current socks password: ' + sockspassword 367 | uInput = userInput("Enter the new password.") 368 | config.set('bitmessagesettings', 'sockspassword', str(uInput)) 369 | else: 370 | print "\n Invalid input. Please try again.\n" 371 | invalidInput = True 372 | 373 | if invalidInput != True: #don't prompt if they made a mistake. 374 | uInput = userInput("Would you like to change another setting, (Y)es or (N)o?").lower() 375 | 376 | if uInput != "y": 377 | print '\n Changes Made.\n' 378 | with open(keysPath, 'wb') as configfile: 379 | config.write(configfile) 380 | restartBmNotify() 381 | break 382 | 383 | 384 | elif uInput == "n": 385 | usrPrompt = 1 386 | main() 387 | else: 388 | print "Invalid input." 389 | usrPrompt = 1 390 | main() 391 | 392 | def validAddress(address): 393 | address_information = api.decodeAddress(address) 394 | address_information = eval(address_information) 395 | 396 | if 'success' in str(address_information.get('status')).lower(): 397 | return True 398 | else: 399 | return False 400 | 401 | def getAddress(passphrase,vNumber,sNumber): 402 | passphrase = passphrase.encode('base64')#passphrase must be encoded 403 | 404 | return api.getDeterministicAddress(passphrase,vNumber,sNumber) 405 | 406 | def subscribe(): 407 | global usrPrompt 408 | 409 | while True: 410 | address = userInput("What address would you like to subscribe to?") 411 | 412 | if (address == "c"): 413 | usrPrompt = 1 414 | print ' ' 415 | main() 416 | elif (validAddress(address)== False): 417 | print '\n Invalid. "c" to cancel. Please try again.\n' 418 | else: 419 | break 420 | 421 | label = userInput("Enter a label for this address.") 422 | label = label.encode('base64') 423 | 424 | api.addSubscription(address,label) 425 | print ('\n You are now subscribed to: ' + address + '\n') 426 | 427 | def unsubscribe(): 428 | global usrPrompt 429 | 430 | while True: 431 | address = userInput("What address would you like to unsubscribe from?") 432 | 433 | if (address == "c"): 434 | usrPrompt = 1 435 | print ' ' 436 | main() 437 | elif (validAddress(address)== False): 438 | print '\n Invalid. "c" to cancel. Please try again.\n' 439 | else: 440 | break 441 | 442 | 443 | uInput = userInput("Are you sure, (Y)es or (N)o?").lower() 444 | 445 | api.deleteSubscription(address) 446 | print ('\n You are now unsubscribed from: ' + address + '\n') 447 | 448 | def listSubscriptions(): 449 | global usrPrompt 450 | #jsonAddresses = json.loads(api.listSubscriptions()) 451 | #numAddresses = len(jsonAddresses['addresses']) #Number of addresses 452 | print '\nLabel, Address, Enabled\n' 453 | try: 454 | print api.listSubscriptions() 455 | except: 456 | print '\n Connection Error\n' 457 | usrPrompt = 0 458 | main() 459 | 460 | '''for addNum in range (0, numAddresses): #processes all of the addresses and lists them out 461 | label = jsonAddresses['addresses'][addNum]['label'] 462 | address = jsonAddresses['addresses'][addNum]['address'] 463 | enabled = jsonAddresses['addresses'][addNum]['enabled'] 464 | 465 | print label, address, enabled 466 | ''' 467 | print ' ' 468 | 469 | def createChan(): 470 | global usrPrompt 471 | password = userInput("Enter channel name") 472 | password = password.encode('base64') 473 | try: 474 | print api.createChan(password) 475 | except: 476 | print '\n Connection Error\n' 477 | usrPrompt = 0 478 | main() 479 | 480 | 481 | def joinChan(): 482 | global usrPrompt 483 | while True: 484 | address = userInput("Enter channel address") 485 | 486 | if (address == "c"): 487 | usrPrompt = 1 488 | print ' ' 489 | main() 490 | elif (validAddress(address)== False): 491 | print '\n Invalid. "c" to cancel. Please try again.\n' 492 | else: 493 | break 494 | 495 | password = userInput("Enter channel name") 496 | password = password.encode('base64') 497 | try: 498 | print api.joinChan(password,address) 499 | except: 500 | print '\n Connection Error\n' 501 | usrPrompt = 0 502 | main() 503 | 504 | def leaveChan(): 505 | global usrPrompt 506 | while True: 507 | address = userInput("Enter channel address") 508 | 509 | if (address == "c"): 510 | usrPrompt = 1 511 | print ' ' 512 | main() 513 | elif (validAddress(address)== False): 514 | print '\n Invalid. "c" to cancel. Please try again.\n' 515 | else: 516 | break 517 | 518 | try: 519 | print api.leaveChan(address) 520 | except: 521 | print '\n Connection Error\n' 522 | usrPrompt = 0 523 | main() 524 | 525 | 526 | def listAdd(): #Lists all of the addresses and their info 527 | global usrPrompt 528 | try: 529 | jsonAddresses = json.loads(api.listAddresses()) 530 | numAddresses = len(jsonAddresses['addresses']) #Number of addresses 531 | except: 532 | print '\n Connection Error\n' 533 | usrPrompt = 0 534 | main() 535 | 536 | #print '\nAddress Number,Label,Address,Stream,Enabled\n' 537 | print '\n --------------------------------------------------------------------------' 538 | print ' | # | Label | Address |S#|Enabled|' 539 | print ' |---|-------------------|-------------------------------------|--|-------|' 540 | for addNum in range (0, numAddresses): #processes all of the addresses and lists them out 541 | label = str(jsonAddresses['addresses'][addNum]['label']) 542 | address = str(jsonAddresses['addresses'][addNum]['address']) 543 | stream = str(jsonAddresses['addresses'][addNum]['stream']) 544 | enabled = str(jsonAddresses['addresses'][addNum]['enabled']) 545 | 546 | if (len(label) > 19): 547 | label = label[:16] + '...' 548 | 549 | print ' |' + str(addNum).ljust(3) + '|' + label.ljust(19) + '|' + address.ljust(37) + '|' + stream.ljust(1), '|' + enabled.ljust(7) + '|' 550 | 551 | print ' --------------------------------------------------------------------------\n' 552 | 553 | def genAdd(lbl,deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe): #Generate address 554 | global usrPrompt 555 | if deterministic == False: #Generates a new address with the user defined label. non-deterministic 556 | addressLabel = lbl.encode('base64') 557 | try: 558 | generatedAddress = api.createRandomAddress(addressLabel) 559 | except: 560 | print '\n Connection Error\n' 561 | usrPrompt = 0 562 | main() 563 | 564 | return generatedAddress 565 | 566 | elif deterministic == True: #Generates a new deterministic address with the user inputs. 567 | passphrase = passphrase.encode('base64') 568 | try: 569 | generatedAddress = api.createDeterministicAddresses(passphrase, numOfAdd, addVNum, streamNum, ripe) 570 | except: 571 | print '\n Connection Error\n' 572 | usrPrompt = 0 573 | main() 574 | return generatedAddress 575 | else: 576 | return 'Entry Error' 577 | 578 | def saveFile(fileName, fileData): #Allows attachments and messages/broadcats to be saved 579 | 580 | #This section finds all invalid characters and replaces them with ~ 581 | fileName = fileName.replace(" ", "") 582 | fileName = fileName.replace("/", "~") 583 | #fileName = fileName.replace("\\", "~") How do I get this to work...? 584 | fileName = fileName.replace(":", "~") 585 | fileName = fileName.replace("*", "~") 586 | fileName = fileName.replace("?", "~") 587 | fileName = fileName.replace('"', "~") 588 | fileName = fileName.replace("<", "~") 589 | fileName = fileName.replace(">", "~") 590 | fileName = fileName.replace("|", "~") 591 | 592 | directory = 'attachments' 593 | 594 | if not os.path.exists(directory): 595 | os.makedirs(directory) 596 | 597 | filePath = directory +'/'+ fileName 598 | 599 | '''try: #Checks if file already exists 600 | with open(filePath): 601 | print 'File Already Exists' 602 | return 603 | except IOError: pass''' 604 | 605 | 606 | f = open(filePath, 'wb+') #Begin saving to file 607 | f.write(fileData.decode("base64")) 608 | f.close 609 | 610 | print '\n Successfully saved '+ filePath + '\n' 611 | 612 | def attachment(): #Allows users to attach a file to their message or broadcast 613 | theAttachmentS = '' 614 | 615 | while True: 616 | 617 | isImage = False 618 | theAttachment = '' 619 | 620 | while True:#loops until valid path is entered 621 | filePath = userInput('\nPlease enter the path to the attachment or just the attachment name if in this folder.') 622 | 623 | try: 624 | with open(filePath): break 625 | except IOError: 626 | print '\n %s was not found on your filesystem or can not be opened.\n' % filePath 627 | pass 628 | 629 | #print filesize, and encoding estimate with confirmation if file is over X size (1mb?) 630 | invSize = os.path.getsize(filePath) 631 | invSize = (invSize / 1024) #Converts to kilobytes 632 | round(invSize,2) #Rounds to two decimal places 633 | 634 | if (invSize > 500.0):#If over 500KB 635 | print '\n WARNING:The file that you are trying to attach is ', invSize, 'KB and will take considerable time to send.\n' 636 | uInput = userInput('Are you sure you still want to attach it, (Y)es or (N)o?').lower() 637 | 638 | if uInput != "y": 639 | print '\n Attachment discarded.\n' 640 | return '' 641 | elif (invSize > 184320.0): #If larger than 180MB, discard. 642 | print '\n Attachment too big, maximum allowed size:180MB\n' 643 | main() 644 | 645 | pathLen = len(str(ntpath.basename(filePath))) #Gets the length of the filepath excluding the filename 646 | fileName = filePath[(len(str(filePath)) - pathLen):] #reads the filename 647 | 648 | filetype = imghdr.what(filePath) #Tests if it is an image file 649 | if filetype is not None: 650 | print '\n ---------------------------------------------------' 651 | print ' Attachment detected as an Image.' 652 | print ' tags will automatically be included,' 653 | print ' allowing the recipient to view the image' 654 | print ' using the "View HTML code..." option in Bitmessage.' 655 | print ' ---------------------------------------------------\n' 656 | isImage = True 657 | time.sleep(2) 658 | 659 | print '\n Encoding Attachment, Please Wait ...\n' #Alert the user that the encoding process may take some time. 660 | 661 | with open(filePath, 'rb') as f: #Begin the actual encoding 662 | data = f.read(188743680) #Reads files up to 180MB, the maximum size for Bitmessage. 663 | data = data.encode("base64") 664 | 665 | if (isImage == True): #If it is an image, include image tags in the message 666 | theAttachment = """ 667 | 668 | 669 | 670 | Filename:%s 671 | Filesize:%sKB 672 | Encoding:base64 673 | 674 |
675 |
676 | %s 677 |
678 |
""" % (fileName,invSize,fileName,filetype,data) 679 | else: #Else it is not an image so do not include the embedded image code. 680 | theAttachment = """ 681 | 682 | 683 | 684 | Filename:%s 685 | Filesize:%sKB 686 | Encoding:base64 687 | 688 | """ % (fileName,invSize,fileName,fileName,data) 689 | 690 | uInput = userInput('Would you like to add another attachment, (Y)es or (N)o?').lower() 691 | 692 | if (uInput == 'y' or uInput == 'yes'):#Allows multiple attachments to be added to one message 693 | theAttachmentS = str(theAttachmentS) + str(theAttachment)+ '\n\n' 694 | elif (uInput == 'n' or uInput == 'no'): 695 | break 696 | 697 | theAttachmentS = theAttachmentS + theAttachment 698 | return theAttachmentS 699 | 700 | def sendMsg(toAddress, fromAddress, subject, message): #With no arguments sent, sendMsg fills in the blanks. subject and message must be encoded before they are passed. 701 | global usrPrompt 702 | if (validAddress(toAddress)== False): 703 | while True: 704 | toAddress = userInput("What is the To Address?") 705 | 706 | if (toAddress == "c"): 707 | usrPrompt = 1 708 | print ' ' 709 | main() 710 | elif (validAddress(toAddress)== False): 711 | print '\n Invalid Address. "c" to cancel. Please try again.\n' 712 | else: 713 | break 714 | 715 | 716 | if (validAddress(fromAddress)== False): 717 | try: 718 | jsonAddresses = json.loads(api.listAddresses()) 719 | numAddresses = len(jsonAddresses['addresses']) #Number of addresses 720 | except: 721 | print '\n Connection Error\n' 722 | usrPrompt = 0 723 | main() 724 | 725 | if (numAddresses > 1): #Ask what address to send from if multiple addresses 726 | found = False 727 | while True: 728 | print ' ' 729 | fromAddress = userInput("Enter an Address or Address Label to send from.") 730 | 731 | if fromAddress == "exit": 732 | usrPrompt = 1 733 | main() 734 | 735 | for addNum in range (0, numAddresses): #processes all of the addresses 736 | label = jsonAddresses['addresses'][addNum]['label'] 737 | address = jsonAddresses['addresses'][addNum]['address'] 738 | #stream = jsonAddresses['addresses'][addNum]['stream'] 739 | #enabled = jsonAddresses['addresses'][addNum]['enabled'] 740 | if (fromAddress == label): #address entered was a label and is found 741 | fromAddress = address 742 | found = True 743 | break 744 | 745 | if (found == False): 746 | if(validAddress(fromAddress)== False): 747 | print '\n Invalid Address. Please try again.\n' 748 | 749 | else: 750 | for addNum in range (0, numAddresses): #processes all of the addresses 751 | #label = jsonAddresses['addresses'][addNum]['label'] 752 | address = jsonAddresses['addresses'][addNum]['address'] 753 | #stream = jsonAddresses['addresses'][addNum]['stream'] 754 | #enabled = jsonAddresses['addresses'][addNum]['enabled'] 755 | if (fromAddress == address): #address entered was a found in our addressbook. 756 | found = True 757 | break 758 | 759 | if (found == False): 760 | print '\n The address entered is not one of yours. Please try again.\n' 761 | 762 | if (found == True): 763 | break #Address was found 764 | 765 | else: #Only one address in address book 766 | print '\n Using the only address in the addressbook to send from.\n' 767 | fromAddress = jsonAddresses['addresses'][0]['address'] 768 | 769 | if (subject == ''): 770 | subject = userInput("Enter your Subject.") 771 | subject = subject.encode('base64') 772 | if (message == ''): 773 | message = userInput("Enter your Message.") 774 | 775 | uInput = userInput('Would you like to add an attachment, (Y)es or (N)o?').lower() 776 | if uInput == "y": 777 | message = message + '\n\n' + attachment() 778 | 779 | message = message.encode('base64') 780 | 781 | try: 782 | ackData = api.sendMessage(toAddress, fromAddress, subject, message) 783 | print '\n Message Status:', api.getStatus(ackData), '\n' 784 | except: 785 | print '\n Connection Error\n' 786 | usrPrompt = 0 787 | main() 788 | 789 | 790 | def sendBrd(fromAddress, subject, message): #sends a broadcast 791 | global usrPrompt 792 | if (fromAddress == ''): 793 | 794 | try: 795 | jsonAddresses = json.loads(api.listAddresses()) 796 | numAddresses = len(jsonAddresses['addresses']) #Number of addresses 797 | except: 798 | print '\n Connection Error\n' 799 | usrPrompt = 0 800 | main() 801 | 802 | if (numAddresses > 1): #Ask what address to send from if multiple addresses 803 | found = False 804 | while True: 805 | fromAddress = userInput("\nEnter an Address or Address Label to send from.") 806 | 807 | if fromAddress == "exit": 808 | usrPrompt = 1 809 | main() 810 | 811 | for addNum in range (0, numAddresses): #processes all of the addresses 812 | label = jsonAddresses['addresses'][addNum]['label'] 813 | address = jsonAddresses['addresses'][addNum]['address'] 814 | #stream = jsonAddresses['addresses'][addNum]['stream'] 815 | #enabled = jsonAddresses['addresses'][addNum]['enabled'] 816 | if (fromAddress == label): #address entered was a label and is found 817 | fromAddress = address 818 | found = True 819 | break 820 | 821 | if (found == False): 822 | if(validAddress(fromAddress)== False): 823 | print '\n Invalid Address. Please try again.\n' 824 | 825 | else: 826 | for addNum in range (0, numAddresses): #processes all of the addresses 827 | #label = jsonAddresses['addresses'][addNum]['label'] 828 | address = jsonAddresses['addresses'][addNum]['address'] 829 | #stream = jsonAddresses['addresses'][addNum]['stream'] 830 | #enabled = jsonAddresses['addresses'][addNum]['enabled'] 831 | if (fromAddress == address): #address entered was a found in our addressbook. 832 | found = True 833 | break 834 | 835 | if (found == False): 836 | print '\n The address entered is not one of yours. Please try again.\n' 837 | 838 | if (found == True): 839 | break #Address was found 840 | 841 | else: #Only one address in address book 842 | print '\n Using the only address in the addressbook to send from.\n' 843 | fromAddress = jsonAddresses['addresses'][0]['address'] 844 | 845 | if (subject == ''): 846 | subject = userInput("Enter your Subject.") 847 | subject = subject.encode('base64') 848 | if (message == ''): 849 | message = userInput("Enter your Message.") 850 | 851 | uInput = userInput('Would you like to add an attachment, (Y)es or (N)o?').lower() 852 | if uInput == "y": 853 | message = message + '\n\n' + attachment() 854 | 855 | message = message.encode('base64') 856 | 857 | try: 858 | ackData = api.sendBroadcast(fromAddress, subject, message) 859 | print '\n Message Status:', api.getStatus(ackData), '\n' 860 | except: 861 | print '\n Connection Error\n' 862 | usrPrompt = 0 863 | main() 864 | 865 | def inbox(unreadOnly = False): #Lists the messages by: Message Number, To Address Label, From Address Label, Subject, Received Time) 866 | global usrPrompt 867 | try: 868 | inboxMessages = json.loads(api.getAllInboxMessages()) 869 | numMessages = len(inboxMessages['inboxMessages']) 870 | except: 871 | print '\n Connection Error\n' 872 | usrPrompt = 0 873 | main() 874 | 875 | messagesPrinted = 0 876 | messagesUnread = 0 877 | for msgNum in range (0, numMessages): #processes all of the messages in the inbox 878 | message = inboxMessages['inboxMessages'][msgNum] 879 | # if we are displaying all messages or if this message is unread then display it 880 | if not unreadOnly or not message['read']: 881 | print ' -----------------------------------\n' 882 | print ' Message Number:',msgNum #Message Number 883 | print ' To:', getLabelForAddress(message['toAddress']) #Get the to address 884 | print ' From:', getLabelForAddress(message['fromAddress']) #Get the from address 885 | print ' Subject:', message['subject'].decode('base64') #Get the subject 886 | print ' Received:', datetime.datetime.fromtimestamp(float(message['receivedTime'])).strftime('%Y-%m-%d %H:%M:%S') 887 | messagesPrinted += 1 888 | if not message['read']: messagesUnread += 1 889 | 890 | if (messagesPrinted%20 == 0 and messagesPrinted != 0): 891 | uInput = userInput('(Press Enter to continue or type (Exit) to return to the main menu.)').lower() 892 | 893 | print '\n -----------------------------------' 894 | print ' There are %d unread messages of %d messages in the inbox.' % (messagesUnread, numMessages) 895 | print ' -----------------------------------\n' 896 | 897 | def outbox(): 898 | global usrPrompt 899 | try: 900 | outboxMessages = json.loads(api.getAllSentMessages()) 901 | numMessages = len(outboxMessages['sentMessages']) 902 | except: 903 | print '\n Connection Error\n' 904 | usrPrompt = 0 905 | main() 906 | 907 | for msgNum in range (0, numMessages): #processes all of the messages in the outbox 908 | print '\n -----------------------------------\n' 909 | print ' Message Number:',msgNum #Message Number 910 | #print ' Message ID:', outboxMessages['sentMessages'][msgNum]['msgid'] 911 | print ' To:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['toAddress']) #Get the to address 912 | print ' From:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['fromAddress']) #Get the from address 913 | print ' Subject:', outboxMessages['sentMessages'][msgNum]['subject'].decode('base64') #Get the subject 914 | print ' Status:', outboxMessages['sentMessages'][msgNum]['status'] #Get the subject 915 | 916 | print ' Last Action Time:', datetime.datetime.fromtimestamp(float(outboxMessages['sentMessages'][msgNum]['lastActionTime'])).strftime('%Y-%m-%d %H:%M:%S') 917 | 918 | if (msgNum%20 == 0 and msgNum != 0): 919 | uInput = userInput('(Press Enter to continue or type (Exit) to return to the main menu.)').lower() 920 | 921 | print '\n -----------------------------------' 922 | print ' There are ',numMessages,' messages in the outbox.' 923 | print ' -----------------------------------\n' 924 | 925 | def readSentMsg(msgNum): #Opens a sent message for reading 926 | global usrPrompt 927 | try: 928 | outboxMessages = json.loads(api.getAllSentMessages()) 929 | numMessages = len(outboxMessages['sentMessages']) 930 | except: 931 | print '\n Connection Error\n' 932 | usrPrompt = 0 933 | main() 934 | 935 | print ' ' 936 | 937 | if (msgNum >= numMessages): 938 | print '\n Invalid Message Number.\n' 939 | main() 940 | 941 | #Begin attachment detection 942 | message = outboxMessages['sentMessages'][msgNum]['message'].decode('base64') 943 | 944 | while True: #Allows multiple messages to be downloaded/saved 945 | if (';base64,' in message): #Found this text in the message, there is probably an attachment. 946 | attPos= message.index(";base64,") #Finds the attachment position 947 | attEndPos = message.index("' />") #Finds the end of the attachment 948 | #attLen = attEndPos - attPos #Finds the length of the message 949 | 950 | 951 | if ('alt = "' in message): #We can get the filename too 952 | fnPos = message.index('alt = "') #Finds position of the filename 953 | fnEndPos = message.index('" src=') #Finds the end position 954 | #fnLen = fnEndPos - fnPos #Finds the length of the filename 955 | 956 | fileName = message[fnPos+7:fnEndPos] 957 | else: 958 | fnPos = attPos 959 | fileName = 'Attachment' 960 | 961 | uInput = userInput('\n Attachment Detected. Would you like to save the attachment, (Y)es or (N)o?').lower() 962 | if (uInput == "y" or uInput == 'yes'): 963 | 964 | attachment = message[attPos+9:attEndPos] 965 | saveFile(fileName,attachment) 966 | 967 | message = message[:fnPos] + '~~' + message[(attEndPos+4):] 968 | 969 | else: 970 | break 971 | 972 | #End attachment Detection 973 | 974 | print '\n To:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['toAddress']) #Get the to address 975 | print ' From:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['fromAddress']) #Get the from address 976 | print ' Subject:', outboxMessages['sentMessages'][msgNum]['subject'].decode('base64') #Get the subject 977 | print ' Status:', outboxMessages['sentMessages'][msgNum]['status'] #Get the subject 978 | print ' Last Action Time:', datetime.datetime.fromtimestamp(float(outboxMessages['sentMessages'][msgNum]['lastActionTime'])).strftime('%Y-%m-%d %H:%M:%S') 979 | print ' Message:\n' 980 | print message #inboxMessages['inboxMessages'][msgNum]['message'].decode('base64') 981 | print ' ' 982 | 983 | def readMsg(msgNum): #Opens a message for reading 984 | global usrPrompt 985 | try: 986 | inboxMessages = json.loads(api.getAllInboxMessages()) 987 | numMessages = len(inboxMessages['inboxMessages']) 988 | except: 989 | print '\n Connection Error\n' 990 | usrPrompt = 0 991 | main() 992 | 993 | if (msgNum >= numMessages): 994 | print '\n Invalid Message Number.\n' 995 | main() 996 | 997 | #Begin attachment detection 998 | message = inboxMessages['inboxMessages'][msgNum]['message'].decode('base64') 999 | 1000 | while True: #Allows multiple messages to be downloaded/saved 1001 | if (';base64,' in message): #Found this text in the message, there is probably an attachment. 1002 | attPos= message.index(";base64,") #Finds the attachment position 1003 | attEndPos = message.index("' />") #Finds the end of the attachment 1004 | #attLen = attEndPos - attPos #Finds the length of the message 1005 | 1006 | 1007 | if ('alt = "' in message): #We can get the filename too 1008 | fnPos = message.index('alt = "') #Finds position of the filename 1009 | fnEndPos = message.index('" src=') #Finds the end position 1010 | #fnLen = fnEndPos - fnPos #Finds the length of the filename 1011 | 1012 | fileName = message[fnPos+7:fnEndPos] 1013 | else: 1014 | fnPos = attPos 1015 | fileName = 'Attachment' 1016 | 1017 | uInput = userInput('\n Attachment Detected. Would you like to save the attachment, (Y)es or (N)o?').lower() 1018 | if (uInput == "y" or uInput == 'yes'): 1019 | 1020 | attachment = message[attPos+9:attEndPos] 1021 | saveFile(fileName,attachment) 1022 | 1023 | message = message[:fnPos] + '~~' + message[(attEndPos+4):] 1024 | 1025 | else: 1026 | break 1027 | 1028 | #End attachment Detection 1029 | print '\n To:', getLabelForAddress(inboxMessages['inboxMessages'][msgNum]['toAddress']) #Get the to address 1030 | print ' From:', getLabelForAddress(inboxMessages['inboxMessages'][msgNum]['fromAddress']) #Get the from address 1031 | print ' Subject:', inboxMessages['inboxMessages'][msgNum]['subject'].decode('base64') #Get the subject 1032 | print ' Received:',datetime.datetime.fromtimestamp(float(inboxMessages['inboxMessages'][msgNum]['receivedTime'])).strftime('%Y-%m-%d %H:%M:%S') 1033 | print ' Message:\n' 1034 | print message #inboxMessages['inboxMessages'][msgNum]['message'].decode('base64') 1035 | print ' ' 1036 | return inboxMessages['inboxMessages'][msgNum]['msgid'] 1037 | 1038 | def replyMsg(msgNum,forwardORreply): #Allows you to reply to the message you are currently on. Saves typing in the addresses and subject. 1039 | global usrPrompt 1040 | forwardORreply = forwardORreply.lower() #makes it lowercase 1041 | try: 1042 | inboxMessages = json.loads(api.getAllInboxMessages()) 1043 | except: 1044 | print '\n Connection Error\n' 1045 | usrPrompt = 0 1046 | main() 1047 | 1048 | fromAdd = inboxMessages['inboxMessages'][msgNum]['toAddress']#Address it was sent To, now the From address 1049 | message = inboxMessages['inboxMessages'][msgNum]['message'].decode('base64') #Message that you are replying too. 1050 | 1051 | subject = inboxMessages['inboxMessages'][msgNum]['subject'] 1052 | subject = subject.decode('base64') 1053 | 1054 | if (forwardORreply == 'reply'): 1055 | toAdd = inboxMessages['inboxMessages'][msgNum]['fromAddress'] #Address it was From, now the To address 1056 | subject = "Re: " + subject 1057 | 1058 | elif (forwardORreply == 'forward'): 1059 | subject = "Fwd: " + subject 1060 | 1061 | while True: 1062 | toAdd = userInput("What is the To Address?") 1063 | 1064 | if (toAdd == "c"): 1065 | usrPrompt = 1 1066 | print ' ' 1067 | main() 1068 | elif (validAddress(toAdd)== False): 1069 | print '\n Invalid Address. "c" to cancel. Please try again.\n' 1070 | else: 1071 | break 1072 | else: 1073 | print '\n Invalid Selection. Reply or Forward only' 1074 | usrPrompt = 0 1075 | main() 1076 | 1077 | subject = subject.encode('base64') 1078 | 1079 | newMessage = userInput("Enter your Message.") 1080 | 1081 | uInput = userInput('Would you like to add an attachment, (Y)es or (N)o?').lower() 1082 | if uInput == "y": 1083 | newMessage = newMessage + '\n\n' + attachment() 1084 | 1085 | newMessage = newMessage + '\n\n------------------------------------------------------\n' 1086 | newMessage = newMessage + message 1087 | newMessage = newMessage.encode('base64') 1088 | 1089 | sendMsg(toAdd, fromAdd, subject, newMessage) 1090 | 1091 | main() 1092 | 1093 | def delMsg(msgNum): #Deletes a specified message from the inbox 1094 | global usrPrompt 1095 | try: 1096 | inboxMessages = json.loads(api.getAllInboxMessages()) 1097 | msgId = inboxMessages['inboxMessages'][int(msgNum)]['msgid'] #gets the message ID via the message index number 1098 | 1099 | msgAck = api.trashMessage(msgId) 1100 | except: 1101 | print '\n Connection Error\n' 1102 | usrPrompt = 0 1103 | main() 1104 | 1105 | return msgAck 1106 | 1107 | def delSentMsg(msgNum): #Deletes a specified message from the outbox 1108 | global usrPrompt 1109 | try: 1110 | outboxMessages = json.loads(api.getAllSentMessages()) 1111 | msgId = outboxMessages['sentMessages'][int(msgNum)]['msgid'] #gets the message ID via the message index number 1112 | msgAck = api.trashSentMessage(msgId) 1113 | except: 1114 | print '\n Connection Error\n' 1115 | usrPrompt = 0 1116 | main() 1117 | 1118 | return msgAck 1119 | 1120 | def getLabelForAddress(address): 1121 | global usrPrompt 1122 | 1123 | if address in knownAddresses: 1124 | return knownAddresses[address] 1125 | else: 1126 | buildKnownAddresses() 1127 | if address in knownAddresses: 1128 | return knownAddresses[address] 1129 | 1130 | return address 1131 | 1132 | def buildKnownAddresses(): 1133 | # add from address book 1134 | try: 1135 | response = api.listAddressBookEntries() 1136 | # if api is too old then fail 1137 | if "API Error 0020" in response: return 1138 | addressBook = json.loads(response) 1139 | for entry in addressBook['addresses']: 1140 | if entry['address'] not in knownAddresses: 1141 | knownAddresses[entry['address']] = "%s (%s)" % (entry['label'].decode('base64'), entry['address']) 1142 | except: 1143 | print '\n Connection Error\n' 1144 | usrPrompt = 0 1145 | main() 1146 | 1147 | # add from my addresses 1148 | try: 1149 | response = api.listAddresses2() 1150 | # if api is too old just return then fail 1151 | if "API Error 0020" in response: return 1152 | addresses = json.loads(response) 1153 | for entry in addresses['addresses']: 1154 | if entry['address'] not in knownAddresses: 1155 | knownAddresses[entry['address']] = "%s (%s)" % (entry['label'].decode('base64'), entry['address']) 1156 | except: 1157 | print '\n Connection Error\n' 1158 | usrPrompt = 0 1159 | main() 1160 | 1161 | def listAddressBookEntries(): 1162 | try: 1163 | response = api.listAddressBookEntries() 1164 | if "API Error" in response: 1165 | return getAPIErrorCode(response) 1166 | addressBook = json.loads(response) 1167 | print 1168 | print ' --------------------------------------------------------------' 1169 | print ' | Label | Address |' 1170 | print ' |--------------------|---------------------------------------|' 1171 | for entry in addressBook['addresses']: 1172 | label = entry['label'].decode('base64') 1173 | address = entry['address'] 1174 | if (len(label) > 19): label = label[:16] + '...' 1175 | print ' | ' + label.ljust(19) + '| ' + address.ljust(37) + ' |' 1176 | print ' --------------------------------------------------------------' 1177 | print 1178 | 1179 | except: 1180 | print '\n Connection Error\n' 1181 | usrPrompt = 0 1182 | main() 1183 | 1184 | def addAddressToAddressBook(address, label): 1185 | try: 1186 | response = api.addAddressBookEntry(address, label.encode('base64')) 1187 | if "API Error" in response: 1188 | return getAPIErrorCode(response) 1189 | except: 1190 | print '\n Connection Error\n' 1191 | usrPrompt = 0 1192 | main() 1193 | 1194 | def deleteAddressFromAddressBook(address): 1195 | try: 1196 | response = api.deleteAddressBookEntry(address) 1197 | if "API Error" in response: 1198 | return getAPIErrorCode(response) 1199 | except: 1200 | print '\n Connection Error\n' 1201 | usrPrompt = 0 1202 | main() 1203 | 1204 | def getAPIErrorCode(response): 1205 | if "API Error" in response: 1206 | # if we got an API error return the number by getting the number 1207 | # after the second space and removing the trailing colon 1208 | return int(response.split()[2][:-1]) 1209 | 1210 | def markMessageRead(messageID): 1211 | try: 1212 | response = api.getInboxMessageByID(messageID, True) 1213 | if "API Error" in response: 1214 | return getAPIErrorCode(response) 1215 | except: 1216 | print '\n Connection Error\n' 1217 | usrPrompt = 0 1218 | main() 1219 | 1220 | def markMessageUnread(messageID): 1221 | try: 1222 | response = api.getInboxMessageByID(messageID, False) 1223 | if "API Error" in response: 1224 | return getAPIErrorCode(response) 1225 | except: 1226 | print '\n Connection Error\n' 1227 | usrPrompt = 0 1228 | main() 1229 | 1230 | def markAllMessagesRead(): 1231 | try: 1232 | inboxMessages = json.loads(api.getAllInboxMessages())['inboxMessages'] 1233 | except: 1234 | print '\n Connection Error\n' 1235 | usrPrompt = 0 1236 | main() 1237 | for message in inboxMessages: 1238 | if not message['read']: 1239 | markMessageRead(message['msgid']) 1240 | 1241 | def markAllMessagesUnread(): 1242 | try: 1243 | inboxMessages = json.loads(api.getAllInboxMessages())['inboxMessages'] 1244 | except: 1245 | print '\n Connection Error\n' 1246 | usrPrompt = 0 1247 | main() 1248 | for message in inboxMessages: 1249 | if message['read']: 1250 | markMessageUnread(message['msgid']) 1251 | 1252 | 1253 | def UI(usrInput): #Main user menu 1254 | global usrPrompt 1255 | 1256 | if usrInput == "help" or usrInput == "h" or usrInput == "?": 1257 | print ' ' 1258 | print ' -------------------------------------------------------------------------' 1259 | print ' | https://github.com/Dokument/PyBitmessage-Daemon |' 1260 | print ' |-----------------------------------------------------------------------|' 1261 | print ' | Command | Description |' 1262 | print ' |------------------------|----------------------------------------------|' 1263 | print ' | help | This help file. |' 1264 | print ' | apiTest | Tests the API |' 1265 | print ' | addInfo | Returns address information (If valid) |' 1266 | print ' | bmSettings | BitMessage settings |' 1267 | print ' | exit | Use anytime to return to main menu |' 1268 | print ' | quit | Quits the program |' 1269 | print ' |------------------------|----------------------------------------------|' 1270 | print ' | listAddresses | Lists all of the users addresses |' 1271 | print ' | generateAddress | Generates a new address |' 1272 | print ' | getAddress | Get determinist address from passphrase |' 1273 | print ' |------------------------|----------------------------------------------|' 1274 | print ' | listAddressBookEntries | Lists entries from the Address Book |' 1275 | print ' | addAddressBookEntry | Add address to the Address Book |' 1276 | print ' | deleteAddressBookEntry | Deletes address from the Address Book |' 1277 | print ' |------------------------|----------------------------------------------|' 1278 | print ' | subscribe | Subscribes to an address |' 1279 | print ' | unsubscribe | Unsubscribes from an address |' 1280 | #print ' | listSubscriptions | Lists all of the subscriptions. |' 1281 | print ' |------------------------|----------------------------------------------|' 1282 | print ' | create | Creates a channel |' 1283 | print ' | join | Joins a channel |' 1284 | print ' | leave | Leaves a channel |' 1285 | print ' |------------------------|----------------------------------------------|' 1286 | print ' | inbox | Lists the message information for the inbox |' 1287 | print ' | outbox | Lists the message information for the outbox |' 1288 | print ' | send | Send a new message or broadcast |' 1289 | print ' | unread | Lists all unread inbox messages |' 1290 | print ' | read | Reads a message from the inbox or outbox |' 1291 | print ' | save | Saves message to text file |' 1292 | print ' | delete | Deletes a message or all messages |' 1293 | print ' -------------------------------------------------------------------------' 1294 | print ' ' 1295 | main() 1296 | 1297 | elif usrInput == "apitest": #tests the API Connection. 1298 | if (apiTest() == True): 1299 | print '\n API connection test has: PASSED\n' 1300 | else: 1301 | print '\n API connection test has: FAILED\n' 1302 | main() 1303 | 1304 | elif usrInput == "addinfo": 1305 | tmp_address = userInput('\nEnter the Bitmessage Address.') 1306 | address_information = api.decodeAddress(tmp_address) 1307 | address_information = eval(address_information) 1308 | 1309 | print '\n------------------------------' 1310 | 1311 | if 'success' in str(address_information.get('status')).lower(): 1312 | print ' Valid Address' 1313 | print ' Address Version: %s' % str(address_information.get('addressVersion')) 1314 | print ' Stream Number: %s' % str(address_information.get('streamNumber')) 1315 | else: 1316 | print ' Invalid Address !' 1317 | 1318 | print '------------------------------\n' 1319 | main() 1320 | 1321 | elif usrInput == "bmsettings": #tests the API Connection. 1322 | bmSettings() 1323 | print ' ' 1324 | main() 1325 | 1326 | elif usrInput == "quit": #Quits the application 1327 | print '\n Bye\n' 1328 | sys.exit() 1329 | os.exit() 1330 | 1331 | elif usrInput == "listaddresses": #Lists all of the identities in the addressbook 1332 | listAdd() 1333 | main() 1334 | 1335 | elif usrInput == "generateaddress": #Generates a new address 1336 | uInput = userInput('\nWould you like to create a (D)eterministic or (R)andom address?').lower() 1337 | 1338 | if uInput == "d" or uInput == "determinstic": #Creates a deterministic address 1339 | deterministic = True 1340 | 1341 | #lbl = raw_input('Label the new address:') #currently not possible via the api 1342 | lbl = '' 1343 | passphrase = userInput('Enter the Passphrase.')#.encode('base64') 1344 | numOfAdd = int(userInput('How many addresses would you like to generate?')) 1345 | #addVNum = int(raw_input('Address version number (default "0"):')) 1346 | #streamNum = int(raw_input('Stream number (default "0"):')) 1347 | addVNum = 3 1348 | streamNum = 1 1349 | isRipe = userInput('Shorten the address, (Y)es or (N)o?').lower() 1350 | 1351 | if isRipe == "y": 1352 | ripe = True 1353 | print genAdd(lbl,deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe) 1354 | main() 1355 | elif isRipe == "n": 1356 | ripe = False 1357 | print genAdd(lbl, deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe) 1358 | main() 1359 | elif isRipe == "exit": 1360 | usrPrompt = 1 1361 | main() 1362 | else: 1363 | print '\n Invalid input\n' 1364 | main() 1365 | 1366 | 1367 | elif uInput == "r" or uInput == "random": #Creates a random address with user-defined label 1368 | deterministic = False 1369 | null = '' 1370 | lbl = userInput('Enter the label for the new address.') 1371 | 1372 | print genAdd(lbl,deterministic, null,null, null, null, null) 1373 | main() 1374 | 1375 | else: 1376 | print '\n Invalid input\n' 1377 | main() 1378 | 1379 | elif usrInput == "getaddress": #Gets the address for/from a passphrase 1380 | phrase = userInput("Enter the address passphrase.") 1381 | print '\n Working...\n' 1382 | #vNumber = int(raw_input("Enter the address version number:")) 1383 | #sNumber = int(raw_input("Enter the address stream number:")) 1384 | 1385 | address = getAddress(phrase,4,1)#,vNumber,sNumber) 1386 | print ('\n Address: ' + address + '\n') 1387 | 1388 | usrPrompt = 1 1389 | main() 1390 | 1391 | elif usrInput == "subscribe": #Subsribe to an address 1392 | subscribe() 1393 | usrPrompt = 1 1394 | main() 1395 | elif usrInput == "unsubscribe": #Unsubscribe from an address 1396 | unsubscribe() 1397 | usrPrompt = 1 1398 | main() 1399 | elif usrInput == "listsubscriptions": #Unsubscribe from an address 1400 | listSubscriptions() 1401 | usrPrompt = 1 1402 | main() 1403 | 1404 | elif usrInput == "create": 1405 | createChan() 1406 | userPrompt = 1 1407 | main() 1408 | 1409 | elif usrInput == "join": 1410 | joinChan() 1411 | userPrompt = 1 1412 | main() 1413 | 1414 | elif usrInput == "leave": 1415 | leaveChan() 1416 | userPrompt = 1 1417 | main() 1418 | 1419 | elif usrInput == "inbox": 1420 | print '\n Loading...\n' 1421 | inbox() 1422 | main() 1423 | 1424 | elif usrInput == "unread": 1425 | print '\n Loading...\n' 1426 | inbox(True) 1427 | main() 1428 | 1429 | elif usrInput == "outbox": 1430 | print '\n Loading...\n' 1431 | outbox() 1432 | main() 1433 | 1434 | elif usrInput == 'send': #Sends a message or broadcast 1435 | uInput = userInput('Would you like to send a (M)essage or (B)roadcast?').lower() 1436 | 1437 | if (uInput == 'm' or uInput == 'message'): 1438 | null = '' 1439 | sendMsg(null,null,null,null) 1440 | main() 1441 | elif (uInput =='b' or uInput == 'broadcast'): 1442 | null = '' 1443 | sendBrd(null,null,null) 1444 | main() 1445 | 1446 | 1447 | elif usrInput == "read": #Opens a message from the inbox for viewing. 1448 | 1449 | uInput = userInput("Would you like to read a message from the (I)nbox or (O)utbox?").lower() 1450 | 1451 | if (uInput != 'i' and uInput != 'inbox' and uInput != 'o' and uInput != 'outbox'): 1452 | print '\n Invalid Input.\n' 1453 | usrPrompt = 1 1454 | main() 1455 | 1456 | msgNum = int(userInput("What is the number of the message you wish to open?")) 1457 | 1458 | if (uInput == 'i' or uInput == 'inbox'): 1459 | print '\n Loading...\n' 1460 | messageID = readMsg(msgNum) 1461 | 1462 | uInput = userInput("\nWould you like to keep this message unread, (Y)es or (N)o?").lower() 1463 | 1464 | if not (uInput == 'y' or uInput == 'yes'): 1465 | markMessageRead(messageID) 1466 | usrPrompt = 1 1467 | 1468 | uInput = userInput("\nWould you like to (D)elete, (F)orward, (R)eply to, or (Exit) this message?").lower() 1469 | 1470 | if (uInput == 'r' or uInput == 'reply'): 1471 | print '\n Loading...\n' 1472 | print ' ' 1473 | replyMsg(msgNum,'reply') 1474 | usrPrompt = 1 1475 | 1476 | elif (uInput == 'f' or uInput == 'forward'): 1477 | print '\n Loading...\n' 1478 | print ' ' 1479 | replyMsg(msgNum,'forward') 1480 | usrPrompt = 1 1481 | 1482 | elif (uInput == "d" or uInput == 'delete'): 1483 | uInput = userInput("Are you sure, (Y)es or (N)o?").lower()#Prevent accidental deletion 1484 | 1485 | if uInput == "y": 1486 | delMsg(msgNum) 1487 | print '\n Message Deleted.\n' 1488 | usrPrompt = 1 1489 | else: 1490 | usrPrompt = 1 1491 | else: 1492 | print '\n Invalid entry\n' 1493 | usrPrompt = 1 1494 | 1495 | elif (uInput == 'o' or uInput == 'outbox'): 1496 | readSentMsg(msgNum) 1497 | 1498 | uInput = userInput("Would you like to (D)elete, or (Exit) this message?").lower() #Gives the user the option to delete the message 1499 | 1500 | if (uInput == "d" or uInput == 'delete'): 1501 | uInput = userInput('Are you sure, (Y)es or (N)o?').lower() #Prevent accidental deletion 1502 | 1503 | if uInput == "y": 1504 | delSentMsg(msgNum) 1505 | print '\n Message Deleted.\n' 1506 | usrPrompt = 1 1507 | else: 1508 | usrPrompt = 1 1509 | else: 1510 | print '\n Invalid Entry\n' 1511 | usrPrompt = 1 1512 | 1513 | main() 1514 | 1515 | elif usrInput == "save": 1516 | 1517 | uInput = userInput("Would you like to save a message from the (I)nbox or (O)utbox?").lower() 1518 | 1519 | if (uInput != 'i' and uInput == 'inbox' and uInput != 'o' and uInput == 'outbox'): 1520 | print '\n Invalid Input.\n' 1521 | usrPrompt = 1 1522 | main() 1523 | 1524 | if (uInput == 'i' or uInput == 'inbox'): 1525 | inboxMessages = json.loads(api.getAllInboxMessages()) 1526 | numMessages = len(inboxMessages['inboxMessages']) 1527 | 1528 | while True: 1529 | msgNum = int(userInput("What is the number of the message you wish to save?")) 1530 | 1531 | if (msgNum >= numMessages): 1532 | print '\n Invalid Message Number.\n' 1533 | else: 1534 | break 1535 | 1536 | subject = inboxMessages['inboxMessages'][msgNum]['subject'].decode('base64') 1537 | message = inboxMessages['inboxMessages'][msgNum]['message']#Don't decode since it is done in the saveFile function 1538 | 1539 | elif (uInput == 'o' or uInput == 'outbox'): 1540 | outboxMessages = json.loads(api.getAllSentMessages()) 1541 | numMessages = len(outboxMessages['sentMessages']) 1542 | 1543 | while True: 1544 | msgNum = int(userInput("What is the number of the message you wish to save?")) 1545 | 1546 | if (msgNum >= numMessages): 1547 | print '\n Invalid Message Number.\n' 1548 | else: 1549 | break 1550 | 1551 | subject = outboxMessages['sentMessages'][msgNum]['subject'].decode('base64') 1552 | message = outboxMessages['sentMessages'][msgNum]['message']#Don't decode since it is done in the saveFile function 1553 | 1554 | subject = subject +'.txt' 1555 | saveFile(subject,message) 1556 | 1557 | usrPrompt = 1 1558 | main() 1559 | 1560 | elif usrInput == "delete": #will delete a message from the system, not reflected on the UI. 1561 | 1562 | uInput = userInput("Would you like to delete a message from the (I)nbox or (O)utbox?").lower() 1563 | 1564 | if (uInput == 'i' or uInput == 'inbox'): 1565 | inboxMessages = json.loads(api.getAllInboxMessages()) 1566 | numMessages = len(inboxMessages['inboxMessages']) 1567 | 1568 | while True: 1569 | msgNum = userInput('Enter the number of the message you wish to delete or (A)ll to empty the inbox.').lower() 1570 | 1571 | if (msgNum == 'a' or msgNum == 'all'): 1572 | break 1573 | elif (int(msgNum) >= numMessages): 1574 | print '\n Invalid Message Number.\n' 1575 | else: 1576 | break 1577 | 1578 | uInput = userInput("Are you sure, (Y)es or (N)o?").lower()#Prevent accidental deletion 1579 | 1580 | if uInput == "y": 1581 | if (msgNum == 'a' or msgNum == 'all'): 1582 | print ' ' 1583 | for msgNum in range (0, numMessages): #processes all of the messages in the inbox 1584 | print ' Deleting message ', msgNum+1, ' of ', numMessages 1585 | delMsg(0) 1586 | 1587 | print '\n Inbox is empty.' 1588 | usrPrompt = 1 1589 | else: 1590 | delMsg(int(msgNum)) 1591 | 1592 | print '\n Notice: Message numbers may have changed.\n' 1593 | main() 1594 | else: 1595 | usrPrompt = 1 1596 | elif (uInput == 'o' or uInput == 'outbox'): 1597 | outboxMessages = json.loads(api.getAllSentMessages()) 1598 | numMessages = len(outboxMessages['sentMessages']) 1599 | 1600 | while True: 1601 | msgNum = userInput('Enter the number of the message you wish to delete or (A)ll to empty the inbox.').lower() 1602 | 1603 | if (msgNum == 'a' or msgNum == 'all'): 1604 | break 1605 | elif (int(msgNum) >= numMessages): 1606 | print '\n Invalid Message Number.\n' 1607 | else: 1608 | break 1609 | 1610 | uInput = userInput("Are you sure, (Y)es or (N)o?").lower()#Prevent accidental deletion 1611 | 1612 | if uInput == "y": 1613 | if (msgNum == 'a' or msgNum == 'all'): 1614 | print ' ' 1615 | for msgNum in range (0, numMessages): #processes all of the messages in the outbox 1616 | print ' Deleting message ', msgNum+1, ' of ', numMessages 1617 | delSentMsg(0) 1618 | 1619 | print '\n Outbox is empty.' 1620 | usrPrompt = 1 1621 | else: 1622 | delSentMsg(int(msgNum)) 1623 | print '\n Notice: Message numbers may have changed.\n' 1624 | main() 1625 | else: 1626 | usrPrompt = 1 1627 | else: 1628 | print '\n Invalid Entry.\n' 1629 | userPrompt = 1 1630 | main() 1631 | 1632 | elif usrInput == "exit": 1633 | print '\n You are already at the main menu. Use "quit" to quit.\n' 1634 | usrPrompt = 1 1635 | main() 1636 | 1637 | elif usrInput == "listaddressbookentries": 1638 | res = listAddressBookEntries() 1639 | if res == 20: print '\n Error: API function not supported.\n' 1640 | usrPrompt = 1 1641 | main() 1642 | 1643 | elif usrInput == "addaddressbookentry": 1644 | address = userInput('Enter address') 1645 | label = userInput('Enter label') 1646 | res = addAddressToAddressBook(address, label) 1647 | if res == 16: print '\n Error: Address already exists in Address Book.\n' 1648 | if res == 20: print '\n Error: API function not supported.\n' 1649 | usrPrompt = 1 1650 | main() 1651 | 1652 | elif usrInput == "deleteaddressbookentry": 1653 | address = userInput('Enter address') 1654 | res = deleteAddressFromAddressBook(address) 1655 | if res == 20: print '\n Error: API function not supported.\n' 1656 | usrPrompt = 1 1657 | main() 1658 | 1659 | elif usrInput == "markallmessagesread": 1660 | markAllMessagesRead() 1661 | usrPrompt = 1 1662 | main() 1663 | 1664 | elif usrInput == "markallmessagesunread": 1665 | markAllMessagesUnread() 1666 | usrPrompt = 1 1667 | main() 1668 | 1669 | else: 1670 | print '\n "',usrInput,'" is not a command.\n' 1671 | usrPrompt = 1 1672 | main() 1673 | 1674 | def main(): 1675 | global api 1676 | global usrPrompt 1677 | 1678 | if (usrPrompt == 0): 1679 | print '\n ------------------------------' 1680 | print ' | Bitmessage Daemon by .dok |' 1681 | print ' | Version 0.2.6 for BM 0.3.5 |' 1682 | print ' ------------------------------' 1683 | api = xmlrpclib.ServerProxy(apiData()) #Connect to BitMessage using these api credentials 1684 | 1685 | if (apiTest() == False): 1686 | print '\n ****************************************************************' 1687 | print ' WARNING: You are not connected to the Bitmessage client.' 1688 | print ' Either Bitmessage is not running or your settings are incorrect.' 1689 | print ' Use the command "apiTest" or "bmSettings" to resolve this issue.' 1690 | print ' ****************************************************************\n' 1691 | 1692 | print 'Type (H)elp for a list of commands.' #Startup message 1693 | usrPrompt = 2 1694 | 1695 | #if (apiTest() == False):#Preform a connection test #taken out until I get the error handler working 1696 | # print '*************************************' 1697 | # print 'WARNING: No connection to Bitmessage.' 1698 | # print '*************************************' 1699 | # print ' ' 1700 | elif (usrPrompt == 1): 1701 | print '\nType (H)elp for a list of commands.' #Startup message 1702 | usrPrompt = 2 1703 | 1704 | try: 1705 | UI((raw_input('>').lower()).replace(" ", "")) 1706 | except EOFError: 1707 | UI("quit") 1708 | 1709 | if __name__ == "__main__": 1710 | main() 1711 | --------------------------------------------------------------------------------