├── .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 |