We are pleased to tell you that the virtual server you ordered has now been set up and is operational.
6 |
VPS Details ============================= Main IP: 000.000.000.000nbsp; Root Password: xxxx
You can access your server using a free simple SSH client program called Putty located at: http://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html
7 |
To reinstall an operating system on your VPS and for various other functions like enabling TUN/TAP, PPP etc you can access the following control panel : ============================= URL: https://crownpanel.com or http://crownpanel.com Username: paneluserxxxx Password: xxxx (If you already have access to the control panel please use your existing password)
8 |
Thanks & have a nice day! CrownCloud - Staff.
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/cloudomate/test/resources/test_settings.cfg:
--------------------------------------------------------------------------------
1 | [user]
2 | email = bot@pleb.net
3 | firstname = Pleb
4 | lastname = Net
5 | companyname = PlebNet
6 | phonenumber = 1234567890
7 | password = hunter2
8 | username = Pleb
9 |
10 | [address]
11 | address = Plebweg 4
12 | city = Plebst
13 | state = PlebState
14 | countrycode = PB
15 | zipcode = 123456
16 |
17 | [payment]
18 | walletpath = /path/to/electrum/wallet
19 |
20 | [server]
21 | ns1 = ns1
22 | ns2 = ns2
23 | hostname = hostname
24 | root_password = hunter2
25 |
26 | [testhoster]
27 | email = test@test.net
28 |
--------------------------------------------------------------------------------
/cloudomate/test/test_captchasolver.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | from __future__ import division
3 | from __future__ import print_function
4 | from __future__ import unicode_literals
5 |
6 | import os
7 | import unittest
8 |
9 | from future import standard_library
10 | from mock.mock import patch, MagicMock
11 |
12 | standard_library.install_aliases()
13 |
14 | from cloudomate.util.captchasolver import CaptchaSolver, ReCaptchaSolver
15 |
16 |
17 | class TestCaptchaSolver(unittest.TestCase):
18 |
19 | def setUp(self):
20 | self.captcha_solver = CaptchaSolver("213389asd8u912823")
21 | self.recaptcha_solver = ReCaptchaSolver("213389asd8u912824")
22 |
23 | def test_get_current_key(self):
24 | self.assertEqual(self.captcha_solver.get_current_key(), "213389asd8u912823")
25 | self.assertEqual(self.recaptcha_solver.get_current_key(), "213389asd8u912824")
26 |
27 | @patch("time.sleep")
28 | def test_solve_captcha_text_case_sensitive(self, mock_time):
29 | self.captcha_solver._create_task_captcha_text_case_sensitive = MagicMock()
30 | self.captcha_solver._get_task_status = MagicMock()
31 | self.captcha_solver._get_task_result = MagicMock()
32 |
33 | self.captcha_solver.solve_captcha_text_case_sensitive(
34 | os.path.join(os.path.dirname(__file__), "resources/captcha.png"))
35 |
36 | self.assertTrue(self.captcha_solver._create_task_captcha_text_case_sensitive.called)
37 | self.assertTrue(self.captcha_solver._get_task_status.called)
38 | self.assertTrue(self.captcha_solver._get_task_result.called)
39 |
40 | @patch("time.sleep")
41 | def test_solve_google_recaptcha(self, mock_time):
42 | self.recaptcha_solver._create_task_google_recaptcha = MagicMock()
43 | self.recaptcha_solver._get_task_status = MagicMock()
44 | self.recaptcha_solver._get_task_result = MagicMock()
45 |
46 | self.recaptcha_solver.solve_google_recaptcha("test1", "test2")
47 |
48 | self.assertTrue(self.recaptcha_solver._create_task_google_recaptcha.called)
49 | self.assertTrue(self.recaptcha_solver._get_task_status.called)
50 | self.assertTrue(self.recaptcha_solver._get_task_result.called)
51 |
52 |
53 | if __name__ == "__main__":
54 | unittest.main()
55 |
--------------------------------------------------------------------------------
/cloudomate/test/test_clientarea.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | from __future__ import division
3 | from __future__ import print_function
4 | from __future__ import unicode_literals
5 |
6 | import os
7 | import unittest
8 | from builtins import open
9 | from unittest import skip
10 |
11 | from future import standard_library
12 | from mock import MagicMock
13 |
14 | from cloudomate.hoster.vps.clientarea import ClientArea
15 |
16 | standard_library.install_aliases()
17 |
18 |
19 | class TestClientArea(unittest.TestCase):
20 | @skip("Update needed for new clientarea")
21 | def test_extract_services(self):
22 | html_file = open(os.path.join(os.path.dirname(__file__), 'resources/clientarea_services.html'), 'r')
23 | data = html_file.read()
24 | html_file.close()
25 | mock = MagicMock(ClientArea)
26 | mock.clientarea_url = ''
27 | services = ClientArea._extract_services(mock, data)
28 | self.assertEqual(services, [
29 | {'status': 'active', 'product': 'Basic', 'url': '?action=productdetails&id=8961', 'price': '$4.99 USD',
30 | 'term': 'Monthly', 'next_due_date': '2017-06-19', 'id': '8961'},
31 | {'status': 'cancelled', 'product': 'Basic', 'url': '?action=productdetails&id=9019',
32 | 'price': '$4.99 USD', 'term': 'Monthly', 'next_due_date': '2017-05-24', 'id': '9019'}
33 | ])
34 |
35 | @skip("Update needed for new clientarea")
36 | def test_extract_service(self):
37 | html_file = open(os.path.join(os.path.dirname(__file__), 'resources/clientarea_service.html'), 'r')
38 | data = html_file.read()
39 | html_file.close()
40 | info = ClientArea._extract_service_info(data)
41 | self.assertEqual(info, ['hostname', '178.32.53.129', 'ns1.pulseservers.comns2.pulseservers.com'])
42 |
43 |
44 | if __name__ == '__main__':
45 | unittest.main(exit=False)
46 |
--------------------------------------------------------------------------------
/cloudomate/test/test_cmdline.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | from __future__ import division
3 | from __future__ import print_function
4 | from __future__ import unicode_literals
5 |
6 | import os
7 | import unittest
8 | from argparse import Namespace
9 |
10 | from future import standard_library
11 | from mock.mock import MagicMock
12 |
13 | import cloudomate.cmdline as cmdline
14 | from cloudomate.hoster.vpn.azirevpn import AzireVpn
15 | from cloudomate.hoster.vps.linevast import LineVast
16 | from cloudomate.hoster.vps.vps_hoster import VpsOption
17 |
18 | standard_library.install_aliases()
19 |
20 |
21 | class TestCmdLine(unittest.TestCase):
22 | def setUp(self):
23 | self.settings_file = os.path.join(os.path.dirname(__file__), 'resources/test_settings.cfg')
24 | self.vps_options_real = LineVast.get_options
25 | self.vps_purchase_real = LineVast.purchase
26 |
27 | def tearDown(self):
28 | LineVast.get_options = self.vps_options_real
29 | LineVast.purchase = self.vps_purchase_real
30 |
31 | def test_execute_vps_list(self):
32 | command = ["vps", "list"]
33 | cmdline.execute(command)
34 |
35 | def test_execute_vpn_list(self):
36 | command = ["vpn", "list"]
37 | cmdline.execute(command)
38 |
39 | def test_execute_vps_options(self):
40 | mock_method = self._mock_vps_options()
41 | command = ["vps", "options", "linevast"]
42 | cmdline.providers["vps"]["linevast"].configurations = []
43 | cmdline.execute(command)
44 | mock_method.assert_called_once()
45 | self._restore_vps_options()
46 |
47 | def test_execute_vpn_options(self):
48 | mock_method = self._mock_vpn_options()
49 | command = ["vpn", "options", "azirevpn"]
50 | cmdline.providers["vpn"]["azirevpn"].configurations = []
51 | cmdline.execute(command)
52 | mock_method.assert_called_once()
53 | self._restore_vpn_options()
54 |
55 | def test_execute_vps_purchase(self):
56 | self._mock_vps_options([self._create_option()])
57 | purchase = LineVast.purchase
58 | LineVast.purchase = MagicMock()
59 | command = ["vps", "purchase", "linevast", "-f", "-c", self.settings_file, "-rp", "asdf", "0"]
60 | cmdline.execute(command)
61 | LineVast.purchase.assert_called_once()
62 | LineVast.purchase = purchase
63 | self._restore_vps_options()
64 |
65 | @staticmethod
66 | def _create_option():
67 | return VpsOption(
68 | name="Option name",
69 | memory="Option ram",
70 | cores="Option cpu",
71 | storage="Option storage",
72 | bandwidth="Option bandwidth",
73 | price=12,
74 | connection="Option connection",
75 | purchase_url="Option url"
76 | )
77 |
78 | def test_execute_vps_purchase_verify_options_failure(self):
79 | self._mock_vps_options()
80 | command = ["vps", "purchase", "linevast", "-f", "-c", self.settings_file, "1"]
81 | self._check_exit_code(1, cmdline.execute, command)
82 | self._restore_vps_options()
83 |
84 | def test_execute_vps_purchase_unknown_provider(self):
85 | command = ["vps", "purchase", "nonode", "-f", "-rp", "asdf", "1"]
86 | self._check_exit_code(2, cmdline.execute, command)
87 |
88 | def test_execute_vps_options_unknown_provider(self):
89 | command = ["vps", "options", "nonode"]
90 | self._check_exit_code(2, cmdline.execute, command)
91 |
92 | def _check_exit_code(self, exit_code, method, args):
93 | try:
94 | method(args)
95 | except SystemExit as e:
96 | self.assertEqual(exit_code, e.code)
97 |
98 | def test_execute_vps_options_no_provider(self):
99 | command = ["vps", "options"]
100 | self._check_exit_code(2, cmdline.execute, command)
101 |
102 | def test_purchase_vps_unknown_provider(self):
103 | args = Namespace()
104 | args.provider = "sd"
105 | args.type = "vps"
106 | self._check_exit_code(2, cmdline.purchase, args)
107 |
108 | def test_purchase_no_provider(self):
109 | args = Namespace()
110 | self._check_exit_code(2, cmdline.purchase, args)
111 |
112 | def test_purchase_vps_bad_provider(self):
113 | args = Namespace()
114 | args.provider = False
115 | args.type = "vps"
116 | self._check_exit_code(2, cmdline.purchase, args)
117 |
118 | def test_purchase_bad_type(self):
119 | args = Namespace()
120 | args.provider = "azirevpn"
121 | args.type = False
122 | self._check_exit_code(2, cmdline.purchase, args)
123 |
124 | def test_execute_vps_purchase_high_id(self):
125 | self._mock_vps_options()
126 | command = ["vps", "purchase", "linevast", "-c", self.settings_file, "-rp", "asdf", "1000"]
127 | self._check_exit_code(1, cmdline.execute, command)
128 | self._restore_vps_options()
129 |
130 | def test_execute_vps_purchase_low_id(self):
131 | mock = self._mock_vps_options()
132 | command = ["vps", "purchase", "linevast", "-c", self.settings_file, "-rp", "asdf", "-1"]
133 | self._check_exit_code(1, cmdline.execute, command)
134 | mock.assert_called_once()
135 | self._restore_vps_options()
136 |
137 | def _mock_vps_options(self, items=None):
138 | if items is None:
139 | items = []
140 | self.vps_options = LineVast.get_options
141 | LineVast.get_options = MagicMock(return_value=items)
142 | return LineVast.get_options
143 |
144 | def _restore_vps_options(self):
145 | LineVast.get_options = self.vps_options
146 |
147 | def _mock_vpn_options(self, items=None):
148 | if items is None:
149 | items = []
150 | self.vpn_options = AzireVpn.get_options
151 | AzireVpn.get_options = MagicMock(return_value=items)
152 | return AzireVpn.get_options
153 |
154 | def _restore_vpn_options(self):
155 | AzireVpn.get_options = self.vpn_options
156 |
157 |
158 | if __name__ == '__main__':
159 | unittest.main(exit=False)
160 |
--------------------------------------------------------------------------------
/cloudomate/test/test_gateway.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | from __future__ import division
3 | from __future__ import print_function
4 | from __future__ import unicode_literals
5 |
6 | import os
7 | from unittest import TestCase
8 | from unittest import skip
9 | from builtins import open
10 |
11 | from mock import patch, Mock
12 |
13 | import requests
14 | from future import standard_library
15 | from future.moves.urllib import request
16 |
17 | from cloudomate.gateway.bitpay import BitPay
18 | from cloudomate.gateway.coinbase import Coinbase
19 | from cloudomate.util.bitcoinaddress import validate
20 |
21 | standard_library.install_aliases()
22 |
23 |
24 | class TestCoinbase(TestCase):
25 | # TODO find a new test coinbase url the old one isn't used anymore
26 | # test url from https://developers.coinbase.com/docs/merchants/payment-pages
27 | TEST_URL = 'https://www.coinbase.com/checkouts/2b30a03995ec62f15bdc54e8428caa87'
28 | amount = None
29 | address = None
30 |
31 | @classmethod
32 | @skip('the TEST_URL isn\t used anymore needs a replacement url')
33 | def setUpClass(cls):
34 | cls.amount, cls.address = Coinbase.extract_info(cls.TEST_URL)
35 |
36 | def test_address(self):
37 | self.assertTrue(validate(self.address))
38 |
39 | def test_amount(self):
40 | self.assertGreater(self.amount, 0)
41 |
42 |
43 | class TestBitPay(TestCase):
44 | amount = None
45 | address = None
46 | rate = None
47 |
48 | @classmethod
49 | @skip('the TEST_URL isn\t used anymore needs a replacement url')
50 | def setUpClass(cls):
51 | html_file = open(os.path.join(os.path.dirname(__file__), 'resources/bitpay_invoice_data.json'), 'r')
52 | data = html_file.read().encode('utf-8')
53 | html_file.close()
54 | response = requests.Response()
55 | response.read = Mock(return_value=data)
56 | with patch.object(request, 'urlopen', return_value=response):
57 | cls.amount, cls.address = BitPay.extract_info('https://bitpay.com/invoice?id=KXnWTnNsNUrHK2PEp8TpDC')
58 |
59 | def test_address(self):
60 | self.assertEqual(self.address, '12cWmVndhmD56dzYcRuYka3Vpgjb3qdRoL')
61 | pass
62 |
63 | def test_amount(self):
64 | self.assertEqual(self.amount, 0.001402)
65 |
66 | def test_address_valid(self):
67 | self.assertTrue(validate(self.address))
68 |
--------------------------------------------------------------------------------
/cloudomate/test/test_hoster.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | from __future__ import division
3 | from __future__ import print_function
4 | from __future__ import unicode_literals
5 |
6 | import unittest
7 |
8 | import requests
9 |
10 | from cloudomate.hoster.hoster import Hoster
11 |
12 |
13 | class TestHosterAbstract(unittest.TestCase):
14 |
15 | def test_create_browser(self):
16 | browser = Hoster._create_browser()
17 | if browser.session.headers['user-agent'] == requests.utils.default_user_agent():
18 | self.fail('No Custom User-agent set in browser')
19 |
20 |
21 | if __name__ == '__main__':
22 | unittest.main()
23 |
--------------------------------------------------------------------------------
/cloudomate/test/test_mullvad.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | from __future__ import division
3 | from __future__ import print_function
4 | from __future__ import unicode_literals
5 |
6 | import datetime
7 | import unittest
8 |
9 | from future import standard_library
10 | from mock.mock import MagicMock
11 |
12 | from cloudomate.hoster.vpn.mullvad import MullVad
13 | from cloudomate.hoster.vpn.vpn_hoster import VpnOption
14 | from cloudomate.util.settings import Settings
15 | from cloudomate.wallet import Wallet
16 |
17 | standard_library.install_aliases()
18 |
19 |
20 | class TestMullvad(unittest.TestCase):
21 |
22 | def setUp(self):
23 | self.settings = Settings()
24 | self.settings.put("user", "accountnumber", "2132sadfqf")
25 | self.wallet = MagicMock(Wallet)
26 | self.mullvad = MullVad(self.settings)
27 | self.option = MagicMock(VpnOption)
28 |
29 | def test_purchase(self):
30 | self.mullvad._login = MagicMock()
31 | self.mullvad._order = MagicMock()
32 | self.mullvad.pay = MagicMock()
33 |
34 | self.mullvad.purchase(self.wallet, self.option)
35 |
36 | self.assertTrue(self.mullvad._login.called)
37 | self.assertTrue(self.mullvad._order.called)
38 | self.assertTrue(self.mullvad.pay.called)
39 |
40 | def test_get_status(self):
41 | self.mullvad._get_expiration_date = MagicMock(return_value="2 January 2019")
42 | self.mullvad._login = MagicMock()
43 | now = datetime.datetime.strptime("9 December 2018", "%d %B %Y")
44 | self.mullvad._get_current_date = MagicMock(return_value=now)
45 |
46 | (online, expire_date) = self.mullvad.get_status()
47 |
48 | self.assertEqual(True, online)
49 | self.assertEqual(2019, expire_date.year)
50 | self.assertEqual(1, expire_date.month)
51 | self.assertEqual(2, expire_date.day)
52 |
53 |
54 | if __name__ == "__main__":
55 | unittest.main()
56 |
--------------------------------------------------------------------------------
/cloudomate/test/test_settings.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | from __future__ import division
3 | from __future__ import print_function
4 | from __future__ import unicode_literals
5 |
6 | import unittest
7 |
8 | import os
9 | from future import standard_library
10 |
11 | from cloudomate.util.settings import Settings
12 |
13 | standard_library.install_aliases()
14 |
15 |
16 | class TestSettings(unittest.TestCase):
17 | def setUp(self):
18 | self.settings = Settings()
19 | self.settings.read_settings(os.path.join(os.path.dirname(__file__), 'resources/test_settings.cfg'))
20 |
21 | def test_read_config(self):
22 | self.assertIsNotNone(self.settings)
23 |
24 | def test_has_first_name(self):
25 | self.assertIsNotNone(self.settings.get('user', 'firstname'))
26 |
27 | def test_has_email(self):
28 | self.assertTrue("@" in self.settings.get('user', 'email'))
29 |
30 | def test_verify_config(self):
31 | verification = {
32 | "user": [
33 | "email",
34 | 'firstname',
35 | 'lastname'
36 | ]
37 | }
38 | self.assertTrue(self.settings.verify_options(verification))
39 |
40 | def test_verify_bad_config(self):
41 | verification = {
42 | "user": [
43 | "email",
44 | 'firstname',
45 | 'lastname'
46 | "randomattribute"
47 | ]
48 | }
49 | self.assertFalse(self.settings.verify_options(verification))
50 |
51 | def test_put(self):
52 | key = "putkey"
53 | section = "putsection"
54 | value = "putvalue"
55 | self.settings.put(section, key, value)
56 | self.assertEqual(self.settings.get(section, key), value)
57 |
58 | def test_get_merge(self):
59 | key = 'email'
60 | sections = ['testhoster', 'user']
61 | value = 'test@test.net'
62 | self.assertEqual(self.settings.get_merge(sections, key), value)
63 |
64 | def test_get_merge_ordering(self):
65 | key = 'email'
66 | sections = ['user', 'testhoster']
67 | value = 'bot@pleb.net'
68 | self.assertEqual(self.settings.get_merge(sections, key), value)
69 |
70 | def test_custom_provider(self):
71 | self.assertEqual(self.settings.get("testhoster", "email"), "test@test.net")
72 |
73 |
74 | if __name__ == '__main__':
75 | unittest.main()
76 |
--------------------------------------------------------------------------------
/cloudomate/test/test_vpn_hosters.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | from __future__ import division
3 | from __future__ import print_function
4 | from __future__ import unicode_literals
5 |
6 | import unittest
7 |
8 | import os
9 | from future import standard_library
10 | from parameterized import parameterized
11 |
12 | from cloudomate.hoster.vpn.azirevpn import AzireVpn
13 | from cloudomate.util.settings import Settings
14 |
15 | standard_library.install_aliases()
16 |
17 | providers = [
18 | (AzireVpn,),
19 | ]
20 |
21 |
22 | class TestHosters(unittest.TestCase):
23 | def setUp(self):
24 | self.settings = Settings()
25 | self.settings.read_settings(os.path.join(os.path.dirname(__file__), 'resources/test_settings.cfg'))
26 |
27 | @parameterized.expand(providers)
28 | def test_vpn_hoster_options(self, hoster):
29 | options = hoster.get_options()
30 | self.assertTrue(len(options) > 0)
31 |
32 | @parameterized.expand(providers)
33 | def test_vpn_hoster_configuration(self, hoster):
34 | config = hoster(self.settings).get_configuration()
35 | self.assertTrue(len(config) > 0)
36 |
37 |
38 | if __name__ == '__main__':
39 | unittest.main()
40 |
--------------------------------------------------------------------------------
/cloudomate/test/test_vps_hosters.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | from __future__ import division
3 | from __future__ import print_function
4 | from __future__ import unicode_literals
5 |
6 | import sys
7 | import unittest
8 | from unittest import skip
9 |
10 | from future import standard_library
11 | from mock.mock import MagicMock
12 | from parameterized import parameterized
13 |
14 | from cloudomate.exceptions.vps_out_of_stock import VPSOutOfStockException
15 | from cloudomate.hoster.vps.blueangelhost import BlueAngelHost
16 | from cloudomate.hoster.vps.ccihosting import CCIHosting
17 | from cloudomate.hoster.vps.hostsailor import HostSailor
18 | # from cloudomate.hoster.vps.libertyvps import LibertyVPS
19 | from cloudomate.hoster.vps.libertyvps import LibertyVPS
20 | from cloudomate.hoster.vps.linevast import LineVast
21 | from cloudomate.hoster.vps.orangewebsite import OrangeWebsite
22 | # from cloudomate.hoster.vps.pulseservers import Pulseservers
23 | from cloudomate.hoster.vps.qhoster import QHoster
24 | from cloudomate.hoster.vps.routerhosting import RouterHosting
25 | from cloudomate.hoster.vps.twosync import TwoSync
26 | from cloudomate.hoster.vps.undergroundprivate import UndergroundPrivate
27 | from cloudomate.util.fakeuserscraper import UserScraper
28 | from cloudomate.util.settings import Settings
29 |
30 | standard_library.install_aliases()
31 |
32 | # Only the ones that currently work are uncommented
33 | providers = [
34 | #(BlueAngelHost,),
35 | #(CCIHosting,),
36 | #(HostSailor,),
37 | #(LibertyVPS,),
38 | (LineVast,),
39 | #(OrangeWebsite,),
40 | # (Pulseservers,),
41 | (QHoster,)
42 | #(RouterHosting,),
43 | #(TwoSync,),
44 | #(UndergroundPrivate,),
45 | # (CrownCloud,), Manually checking orders results in being banned after running tests
46 | # (UndergroundPrivate,),# find a way to combine the url and the invoice to be able to go to the payment page
47 | ]
48 |
49 |
50 | class TestHosters(unittest.TestCase):
51 | @parameterized.expand(providers)
52 | def test_hoster_options(self, hoster):
53 | options = hoster.get_options()
54 | self.assertTrue(len(options) > 0)
55 |
56 | @parameterized.expand(providers)
57 | @unittest.skipIf(len(sys.argv) >= 2 and sys.argv[1] == 'discover', 'Integration tests have to be run manually')
58 | @skip('These tests relies on webscraping and form filling of vps pages. these pages change and therefore these '
59 | 'tests are currently to unreliable')
60 | def test_hoster_purchase(self, hoster):
61 | user_settings = Settings()
62 | self._merge_random_user_data(user_settings)
63 |
64 | host = hoster(user_settings)
65 | options = list(host.get_options())[0]
66 | wallet = MagicMock()
67 | wallet.pay = MagicMock()
68 |
69 | try:
70 | host.purchase(wallet, options)
71 | wallet.pay.assert_called_once()
72 | except VPSOutOfStockException as exception:
73 | self.skipTest(exception)
74 |
75 | @staticmethod
76 | def _merge_random_user_data(user_settings):
77 | usergenerator = UserScraper()
78 | randomuser = usergenerator.get_user()
79 | for section in randomuser.keys():
80 | for key in randomuser[section].keys():
81 | user_settings.put(section, key, randomuser[section][key])
82 |
83 |
84 | if __name__ == '__main__':
85 | unittest.main()
86 |
--------------------------------------------------------------------------------
/cloudomate/util/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tribler/cloudomate/f41af871bdc6bd148d53f94a4d0062eef5d608dd/cloudomate/util/__init__.py
--------------------------------------------------------------------------------
/cloudomate/util/bitcoinaddress.py:
--------------------------------------------------------------------------------
1 | """Validate bitcoin/altcoin addresses
2 |
3 | Copied from:
4 | http://rosettacode.org/wiki/Bitcoin/address_validation#Python
5 | """
6 | from __future__ import absolute_import
7 | from __future__ import division
8 | from __future__ import print_function
9 | from __future__ import unicode_literals
10 |
11 | from builtins import bytes
12 | from builtins import int
13 | from builtins import range
14 | from hashlib import sha256
15 |
16 | from future import standard_library
17 |
18 | standard_library.install_aliases()
19 |
20 | digits58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
21 |
22 |
23 | def _bytes_to_long(bytestring, byteorder):
24 | """Convert a bytestring to a long
25 |
26 | For use in python version prior to 3.2
27 | """
28 | if byteorder == 'little':
29 | result = (v << i * 8 for (i, v) in enumerate(bytestring))
30 | else:
31 | result = (v << i * 8 for (i, v) in enumerate(reversed(bytestring)))
32 | return sum(result)
33 |
34 |
35 | def _long_to_bytes(n, length, byteorder):
36 | """Convert a long to a bytestring
37 |
38 | For use in python version prior to 3.2
39 | Source:
40 | http://bugs.python.org/issue16580#msg177208
41 | """
42 | if byteorder == 'little':
43 | indexes = list(range(length))
44 | else:
45 | indexes = reversed(list(range(length)))
46 | return bytearray((n >> i * 8) & 0xff for i in indexes)
47 |
48 |
49 | def decode_base58(bitcoin_address, length):
50 | """Decode a base58 encoded address
51 |
52 | This form of base58 decoding is bitcoind specific. Be careful outside of
53 | bitcoind context.
54 | """
55 | n = 0
56 | for char in bitcoin_address:
57 | try:
58 | n = n * 58 + digits58.index(char)
59 | except ValueError:
60 | msg = "Character not part of Bitcoin's base58: '%s'"
61 | raise ValueError(msg % (char,))
62 | try:
63 | return n.to_bytes(length, 'big')
64 | except AttributeError:
65 | # Python version < 3.2
66 | return _long_to_bytes(n, length, 'big')
67 |
68 |
69 | def encode_base58(bytestring):
70 | """Encode a bytestring to a base58 encoded string
71 | """
72 | # Count zero's
73 | zeros = 0
74 | for i in range(len(bytestring)):
75 | if bytestring[i] == 0:
76 | zeros += 1
77 | else:
78 | break
79 | try:
80 | n = int.from_bytes(bytestring, 'big')
81 | except AttributeError:
82 | # Python version < 3.2
83 | n = _bytes_to_long(bytestring, 'big')
84 | result = ''
85 | (n, rest) = divmod(n, 58)
86 | while n or rest:
87 | result += digits58[rest]
88 | (n, rest) = divmod(n, 58)
89 | return zeros * '1' + result[::-1] # reverse string
90 |
91 |
92 | def validate(bitcoin_address, magicbyte=0):
93 | """Check the integrity of a bitcoin address
94 |
95 | Returns False if the address is invalid.
96 | >>> validate('1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i')
97 | True
98 | >>> validate('')
99 | False
100 | """
101 | if isinstance(magicbyte, int):
102 | magicbyte = (magicbyte,)
103 | clen = len(bitcoin_address)
104 | if clen < 27 or clen > 35: # XXX or 34?
105 | return False
106 | try:
107 | bcbytes = decode_base58(bitcoin_address, 25)
108 | except ValueError:
109 | return False
110 | # Check magic byte (for other altcoins, fix by Frederico Reiven)
111 | for mb in magicbyte:
112 | if bcbytes.startswith(bytes(int(mb))):
113 | break
114 | else:
115 | return False
116 | # Compare checksum
117 | checksum = sha256(sha256(bcbytes[:-4]).digest()).digest()[:4]
118 | if bcbytes[-4:] != checksum:
119 | return False
120 | # Encoded bytestring should be equal to the original address,
121 | # for example '14oLvT2' has a valid checksum, but is not a valid btc
122 | # address
123 | return bitcoin_address == encode_base58(bcbytes)
124 |
--------------------------------------------------------------------------------
/cloudomate/util/captchasolver.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | from __future__ import division
3 | from __future__ import print_function
4 | from __future__ import unicode_literals
5 |
6 | import base64
7 | import json
8 | import os
9 | import time
10 | from builtins import open
11 | from builtins import str
12 |
13 | import requests
14 | from future import standard_library
15 |
16 | standard_library.install_aliases()
17 |
18 | """
19 | Usage:
20 |
21 | Feed Anti Captcha account API key to solver:
22 | c_solver = CaptchaSolver("fd58e13e22604e820052b44611d61d6c")
23 |
24 | Find out how much money is left:
25 | temp = c_solver.get_balance()
26 |
27 | Provide captcha image full path to solver method to get solution:
28 | solution = c_solver.solve_captcha_text_case_sensitive(
29 | "/home/testm2/Desktop/testcscr/php/pyhton/cap2.jpg")
30 | print(str(solution))
31 |
32 | Feed Anti-captcha account API key to solver:
33 | rc_solver = ReCaptchaSolver("fd58e13e22604e820052b44611d61d6c")
34 |
35 | Find out how much money is left:
36 | temp = rc_solver.get_balance()
37 |
38 | Provide solver method with Google ReCapthca URL and ReCaptcha website
39 | key usually found as data-sitekey attribute in an HTML element in the
40 | website containing the ReCaptcha:
41 | wUrl = "http://http.myjino.ru/recaptcha/test-get.php"
42 | wKey = "6Lc_aCMTAAAAABx7u2W0WPXnVbI_v6ZdbM6rYf16"
43 | rc_solution = rc_solver.solve_google_recaptcha(wUrl,wKey)
44 | print(rc_solution)
45 | """
46 |
47 |
48 | class CaptchaSolver(object):
49 | _client_key = "not set"
50 |
51 | def __init__(self, c_key):
52 | self._client_key = c_key
53 |
54 | def get_balance(self):
55 | # Query API for account balance
56 | response = requests.post("https://api.anti-captcha.com/getBalance",
57 | json={"clientKey": self._client_key})
58 |
59 | # Check response of HTTP request
60 | if response.status_code == requests.codes.ok:
61 | response_json = json.loads(response.text)
62 | if response_json["errorId"] == 0:
63 | print("Successful, account balance returned")
64 | return response_json["balance"]
65 | else:
66 | # Print API error
67 | print(response.text)
68 | else:
69 | # Print request error
70 | print(response.status_code)
71 |
72 | def solve_captcha_text_case_sensitive(self, full_image_file_path):
73 | # Encode captcha image before sending it
74 | if os.path.isfile(full_image_file_path):
75 | with open(full_image_file_path, "rb") as image_file:
76 | encoded_image_string = base64.b64encode(image_file.read())
77 | print("Captcha image sucessfully encoded")
78 | else:
79 | print("Error: file path not found")
80 | return
81 |
82 | # Create new Captcha solving task using the API
83 | task_id = self._create_task_captcha_text_case_sensitive(
84 | encoded_image_string)
85 |
86 | # Query API until task is finished
87 | current_status = self._get_task_status(task_id)
88 | while current_status == "processing":
89 | print("Sleeping 5 sec")
90 | time.sleep(5)
91 | current_status = self._get_task_status(task_id)
92 | print("Current status: " + str(current_status))
93 |
94 | # Get solution
95 | solution = self._get_task_result(task_id)
96 | return solution["text"]
97 |
98 | def _get_task_result(self, task_id):
99 | # Query API for the solution of the task
100 | response = requests.post("https://api.anti-captcha.com/getTaskResult",
101 | json={"clientKey": self._client_key,
102 | "taskId": task_id})
103 |
104 | # Check response of HTTP request
105 | if response.status_code == requests.codes.ok:
106 | response_json = json.loads(response.text)
107 | if response_json["errorId"] == 0:
108 | print("Successful, captcha solved:")
109 | return response_json["solution"]
110 | else:
111 | # Print API error
112 | print(response.text)
113 | else:
114 | # Print request error
115 | print(response.status_code)
116 |
117 | def _get_task_status(self, task_id):
118 | # Query API for the status of the task
119 | response = requests.post("https://api.anti-captcha.com/getTaskResult",
120 | json={"clientKey": self._client_key,
121 | "taskId": task_id})
122 |
123 | # Check response of HTTP request
124 | if response.status_code == requests.codes.ok:
125 | response_json = json.loads(response.text)
126 | if response_json["errorId"] == 0:
127 | print("Successful, task status returned")
128 | return response_json["status"]
129 | else:
130 | # Print API error
131 | print(response.text)
132 | else:
133 | # Print request error
134 | print(response.status_code)
135 |
136 | def _create_task_captcha_text_case_sensitive(self, base64_image_string):
137 | # Send task creation command to API
138 | response = requests.post("https://api.anti-captcha.com/createTask",
139 | json={"clientKey": self._client_key,
140 | "task":
141 | {
142 | "type": "ImageToTextTask",
143 | "body": base64_image_string,
144 | "phrase": False,
145 | "case": True,
146 | "numeric": False,
147 | "math": 0,
148 | "minLength": 0,
149 | "maxLength": 0
150 | }
151 | })
152 |
153 | # Check response of HTTP request
154 | if response.status_code == requests.codes.ok:
155 | response_json = json.loads(response.text)
156 | if response_json["errorId"] == 0:
157 | print("Successful, captcha task was created:" + response.text)
158 | return response_json["taskId"]
159 | elif response_json["errorCode"] == "ERROR_NO_SLOT_AVAILABLE":
160 | time.sleep(15)
161 | return self._create_task_captcha_text_case_sensitive(
162 | base64_image_string)
163 | else:
164 | # Print API error
165 | print(response.text)
166 | else:
167 | # Print request error
168 | print(response.status_code)
169 |
170 | def get_current_key(self):
171 | return self._client_key
172 |
173 |
174 | class ReCaptchaSolver(CaptchaSolver):
175 |
176 | def _create_task_google_recaptcha(self, website_url, website_key):
177 | # Send task creation command to API
178 | response = requests.post("https://api.anti-captcha.com/createTask",
179 | json={
180 | "clientKey": self._client_key,
181 | "task":
182 | {
183 | "type": "NoCaptchaTaskProxyless",
184 | "websiteURL": website_url,
185 | "websiteKey": website_key
186 | },
187 | "softId": 0,
188 | "languagePool": "en"
189 | })
190 |
191 | # Check response of HTTP request
192 | if response.status_code == requests.codes.ok:
193 | response_json = json.loads(response.text)
194 | if response_json["errorId"] == 0:
195 | print("Successful, task was created: " + response.text)
196 | return response_json["taskId"]
197 | elif response_json["errorCode"] == "ERROR_NO_SLOT_AVAILABLE":
198 | # In case task could not be created, create it again
199 | time.sleep(15)
200 | return self._create_task_google_recaptcha(website_url,
201 | website_key)
202 | else:
203 | # Print API errordd
204 | print(response.text)
205 | else:
206 | # Print request error
207 | print(response.status_code)
208 |
209 | def solve_google_recaptcha(self, website_url, website_key):
210 | # Create new Google ReCaptcha solving task using the API
211 | task_id = self._create_task_google_recaptcha(website_url, website_key)
212 | print("Sleeping 15 sec")
213 | time.sleep(15)
214 |
215 | # Query API until task is finished
216 | current_status = self._get_task_status(task_id)
217 | while current_status == "processing":
218 | print("Current status: " + str(current_status))
219 | print("Sleeping 5 sec")
220 | time.sleep(5)
221 | current_status = self._get_task_status(task_id)
222 | print("Current status: " + str(current_status))
223 |
224 | # Get solution of task
225 | solution = self._get_task_result(task_id)
226 | return solution["gRecaptchaResponse"]
227 |
--------------------------------------------------------------------------------
/cloudomate/util/fakeuserscraper.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | from __future__ import division
3 | from __future__ import print_function
4 | from __future__ import unicode_literals
5 |
6 | import random
7 | import string
8 | from builtins import object
9 |
10 | from future import standard_library
11 |
12 | from mechanicalsoup import StatefulBrowser
13 |
14 | standard_library.install_aliases()
15 |
16 |
17 | class UserScraper(object):
18 | """
19 | Scrapes fakeaddressgenerator.com for fake user data.
20 | It also adds some basic additional information for server configuration.
21 | """
22 |
23 | attributes = [
24 | 'Full Name',
25 | 'Street',
26 | 'City',
27 | 'State Full',
28 | 'Zip Code',
29 | 'Phone Number',
30 | 'Company',
31 | 'Username'
32 | ]
33 |
34 | pages = {
35 | 'NL': 'http://www.fakeaddressgenerator.com/World/Netherlands_address_generator',
36 | 'US': 'http://www.fakeaddressgenerator.com/World/us_address_generator',
37 | 'UK': 'http://www.fakeaddressgenerator.com/World/uk_address_generator',
38 | 'CA': 'http://www.fakeaddressgenerator.com/World/ca_address_generator',
39 | }
40 |
41 | def __init__(self, country='NL'):
42 | self.country_code = country
43 | self.browser = StatefulBrowser()
44 | self.page = UserScraper.pages.get(country)
45 |
46 | def get_user(self):
47 | self.browser.open(self.page)
48 | attrs = {}
49 |
50 | for attr in self.attributes:
51 | attrs[attr] = self._get_attribute(attr)
52 |
53 | attrs['country_code'] = self.country_code
54 | attrs['password'] = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(12))
55 | attrs['email'] = 'authentic8989+' + attrs['Username'] + '@gmail.com'
56 | attrs['rootpw'] = attrs['password']
57 | attrs['ns1'] = 'ns1'
58 | attrs['ns2'] = 'ns2'
59 | attrs['hostname'] = attrs['Username'] + '.hostname.com'
60 | attrs['testnet'] = 'off'
61 |
62 | return self._map_to_config(attrs)
63 |
64 | @staticmethod
65 | def _map_to_config(attrs):
66 | config = {}
67 | # Treat full name separately because it needs to be split
68 | if 'Full Name' in attrs:
69 | config['user'] = {}
70 | config['user']['firstname'] = attrs['Full Name'].split('\xa0')[0]
71 | config['user']['lastname'] = attrs['Full Name'].split('\xa0')[-1]
72 |
73 | # Map the possible user attributes to their config names and sections
74 | mapping = {
75 | 'Street': ('address', 'address'),
76 | 'City': ('address', 'city'),
77 | 'State Full': ('address', 'state'),
78 | 'Zip Code': ('address', 'zipcode'),
79 | 'Phone Number': ('user', 'phonenumber'),
80 | 'Company': ('user', 'companyname'),
81 | 'Username': ('user', 'username'),
82 | 'country_code': ('address', 'countrycode'),
83 | 'password': ('user', 'password'),
84 | 'email': ('user', 'email'),
85 | 'rootpw': ('server', 'root_password'),
86 | 'ns1': ('server', 'ns1'),
87 | 'ns2': ('server', 'ns2'),
88 | 'hostname': ('server', 'hostname'),
89 | 'testnet': ('user', 'testnet')
90 | }
91 |
92 | for attr in attrs.keys():
93 | if attr in mapping.keys():
94 | section, key = mapping[attr]
95 | if section not in config:
96 | config[section] = {}
97 | config[section][key] = attrs[attr]
98 | return config
99 |
100 | def _get_attribute(self, attribute):
101 | return self.browser.get_current_page() \
102 | .find(string=attribute) \
103 | .parent.parent.parent \
104 | .find('input') \
105 | .get('value')
106 |
--------------------------------------------------------------------------------
/cloudomate/util/settings.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | from __future__ import division
3 | from __future__ import print_function
4 | from __future__ import unicode_literals
5 |
6 | import os
7 | import sys
8 | from builtins import open
9 | from builtins import str
10 | from configparser import ConfigParser
11 | from configparser import NoOptionError
12 |
13 | from appdirs import user_config_dir
14 | from future import standard_library
15 |
16 | standard_library.install_aliases()
17 |
18 |
19 | class Settings(object):
20 | def __init__(self):
21 | self.settings = ConfigParser()
22 | config_dir = user_config_dir()
23 | self._default_filename = os.path.join(config_dir, 'cloudomate.cfg')
24 |
25 | def get_default_config_location(self):
26 | return self._default_filename
27 |
28 | def read_settings(self, filename=None):
29 | """Read the settings object from a file.
30 |
31 | If the filename is omitted it is read from the default /cloudomate.conf location.
32 |
33 | :param filename: The file to read it from
34 | :return: Whether or not the config was successfully read
35 | """
36 | if not filename:
37 | filename = self._default_filename
38 |
39 | if not os.path.exists(filename):
40 | print("Config file: '%s' not found" % filename)
41 | return False
42 | files = self.settings.read(filename, encoding='utf-8')
43 | return len(files) > 0
44 |
45 | def save_settings(self, filename=None):
46 | """Save this settings object to a file.
47 |
48 | If the filename is omitted it is saved to the default /cloudomate.conf location.
49 |
50 | :param filename: The file to save it to
51 | :return: Whether or not the config was successfully written
52 | """
53 | if not filename:
54 | filename = self._default_filename
55 |
56 | try:
57 | self.settings.write(open(filename, 'w', encoding='utf-8'))
58 | except IOError:
59 | print("Failed to write configuration to '{}', printing it to stdout:".format(filename), file=sys.stderr)
60 | self.settings.write(sys.stdout)
61 |
62 | def verify_options(self, options):
63 | valid = True
64 | for section, keys in options.items():
65 | if not self.settings.has_section(section):
66 | print("Section {} does not exist".format(section))
67 | valid = False
68 | else:
69 | for key in keys:
70 | if not self.settings.has_option(section, key):
71 | print("Setting {}.{} does not exist".format(section, key))
72 | valid = False
73 | return valid
74 |
75 | def get(self, section, key):
76 | return self.settings.get(section, key)
77 |
78 | def get_merge(self, sections, key):
79 | """Get a value from a merge of specified sections.
80 | The order of the passed sections denote their priority with the first having the highest priority.
81 | :param sections: The sections to look in for the value
82 | :param key: The key of the setting
83 | :return: The desired settings value
84 | """
85 | for section in sections:
86 | if self.settings.has_option(section, key):
87 | return self.settings.get(section, key)
88 | print("Setting {} does not exist in any of the given sections".format(key))
89 | raise NoOptionError(sections[-1], key)
90 |
91 | def put(self, section, key, value):
92 | if not self.settings.has_section(section):
93 | self.settings.add_section(section)
94 |
95 | self.settings.set(section, key, str(value))
96 |
97 | def has_key(self, section, key):
98 | return self.settings.has_option(section, key)
99 |
100 | def has_key_merge(self, sections, key):
101 | for section in sections:
102 | if self.settings.has_option(section, key):
103 | return True
104 | raise False
105 |
--------------------------------------------------------------------------------
/cloudomate/wallet.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from __future__ import absolute_import
4 | from __future__ import division
5 | from __future__ import print_function
6 | from __future__ import unicode_literals
7 |
8 | import json
9 | import os
10 | import subprocess
11 | import sys
12 | from builtins import object
13 | from builtins import str
14 |
15 | from forex_python.bitcoin import BtcConverter
16 | from future import standard_library
17 | from mechanicalsoup import StatefulBrowser
18 |
19 | standard_library.install_aliases()
20 |
21 | if sys.version_info > (3, 0):
22 | from urllib.request import urlopen
23 | else:
24 | from urllib2 import urlopen
25 |
26 | AVG_TX_SIZE = 226
27 | SATOSHI_TO_BTC = 0.00000001
28 |
29 |
30 | def determine_currency(text):
31 | """
32 | Determine currency of text
33 | :param text: text cointaining a currency symbol
34 | :return: currency name of symbol
35 | """
36 | # Naive approach, for example NZ$ also contains $
37 | if '$' in text or 'usd' in text.lower():
38 | return 'USD'
39 | elif '€' in text or 'eur' in text.lower():
40 | return 'EUR'
41 | else:
42 | return None
43 |
44 |
45 | def get_rate(currency='USD'):
46 | """
47 | Return price of 1 currency in BTC
48 | Supported currencies available at
49 | http://forex-python.readthedocs.io/en/latest/currencysource.html#list-of-supported-currency-codes
50 | :param currency: currency to convert to
51 | :return: conversion rate from currency to BTC
52 | """
53 | if currency is None:
54 | return None
55 | b = BtcConverter()
56 | factor = b.get_latest_price(currency)
57 | if factor is None:
58 | factor = 1.0 / fallback_get_rate(currency)
59 | return 1.0 / factor
60 |
61 |
62 | def fallback_get_rate(currency):
63 | # Sometimes the method above gets rate limited, in this case use
64 | # https: // blockchain.info / tobtc?currency = USD & value = 500
65 | return float(urlopen('https://blockchain.info/tobtc?currency={0}&value=1'.format(currency)).read())
66 |
67 |
68 | def get_rates(currencies):
69 | """
70 | Return rates for all currencies to BTC.
71 | :return: conversion rates from currencies to BTC
72 | """
73 | rates = {cur: get_rate(cur) for cur in currencies}
74 | return rates
75 |
76 |
77 | def get_price(amount, currency='USD'):
78 | """
79 | Convert price from one currency to bitcoins
80 | :param amount: number of currencies to convert
81 | :param currency: currency to convert from
82 | :return: price in bitcoins
83 | """
84 | price = amount * get_rate(currency)
85 | return price
86 |
87 |
88 | def _get_network_cost(speed):
89 | br = StatefulBrowser(user_agent='Firefox')
90 | page = br.open('https://bitcoinfees.earn.com/api/v1/fees/recommended')
91 | response = page.json()
92 | satoshirate = float(response[speed])
93 | return satoshirate
94 |
95 |
96 | def get_network_fee(speed='halfHourFee'):
97 | """
98 | Give an estimate of network fee for the average bitcoin transaction for given speed.
99 | Supported speeds are available at https://bitcoinfees.earn.com/api/v1/fees/recommended
100 | :return: network cost
101 | """
102 | network_fee = _get_network_cost(speed) * SATOSHI_TO_BTC
103 | return network_fee * AVG_TX_SIZE
104 |
105 |
106 | class Wallet(object):
107 | """
108 | Wallet implements an adapter to the wallet handler.
109 | Currently Wallet only supports electrum wallets without passwords for automated operation.
110 | Wallets with passwords may still be used, but passwords will have to be entered manually.
111 | """
112 |
113 | def __init__(self, wallet_command=None, wallet_path=None, testnet=None):
114 | if wallet_command is None:
115 | if os.path.exists('/usr/local/bin/electrum'):
116 | wallet_command = ['/usr/local/bin/electrum']
117 | else:
118 | wallet_command = ['/usr/bin/env', 'electrum']
119 | if testnet:
120 | wallet_command.append('--testnet')
121 | self.command = wallet_command
122 | self.wallet_handler = ElectrumWalletHandler(wallet_command, wallet_path)
123 |
124 | def get_balance(self, confirmed=True, unconfirmed=True):
125 | """
126 | Return the balance of the default electrum wallet
127 | Confirmed and unconfirmed can be set to indicate which balance to retrieve.
128 | :param confirmed: default: True
129 | :param unconfirmed: default: True
130 | :return: balance of default wallet
131 | """
132 | balance_output = self.wallet_handler.get_balance()
133 | balance = 0.0
134 | if confirmed:
135 | balance = balance + float(balance_output.get('confirmed', 0.0))
136 | if unconfirmed:
137 | balance = balance + float(balance_output.get('unconfirmed', 0.0))
138 | return balance
139 |
140 | def get_balance_confirmed(self):
141 | """
142 | Return confirmed balance of default electrum wallet
143 | :return:
144 | """
145 | return self.get_balance(confirmed=True, unconfirmed=False)
146 |
147 | def get_balance_unconfirmed(self):
148 | """
149 | Return unconfirmed balance of default electrum wallet
150 | :return:
151 | """
152 | return self.get_balance(confirmed=False, unconfirmed=True)
153 |
154 | def get_addresses(self):
155 | """
156 | Return the list of addresses of the default electrum wallet
157 | :return:
158 | """
159 | address_output = self.wallet_handler.get_addresses()
160 | return address_output
161 |
162 | def pay(self, address, amount, fee=None):
163 | tx_fee = 0 if fee is None else fee
164 | if self.get_balance() < amount + tx_fee:
165 | print('Not enough funds')
166 | return
167 |
168 | transaction_hex = self.wallet_handler.create_transaction(amount, address)
169 | transaction_hash = self.wallet_handler.broadcast(transaction_hex)
170 | # no/empty transaction hash means the broadcast was not successful
171 | if not transaction_hash:
172 | print(('Transaction not successfully broadcast, do error handling: {0}'.format(transaction_hash)))
173 | else:
174 | print('Transaction successful')
175 | print(transaction_hex)
176 | print(transaction_hash)
177 | return transaction_hash
178 |
179 |
180 | class ElectrumWalletHandler(object):
181 | """
182 | ElectrumWalletHandler ensures the correct opening and closing of the electrum wallet daemon
183 | """
184 |
185 | def __init__(self, wallet_command=None, wallet_path=None):
186 | """
187 | Allows wallet_command to be changed to for example electrum --testnet
188 | :param wallet_command: command to call wallet
189 | """
190 | self._wallet_path = wallet_path
191 |
192 | if wallet_command is None:
193 | if os.path.exists('/usr/local/bin/electrum'):
194 | wallet_command = ['/usr/local/bin/electrum']
195 | else:
196 | wallet_command = ['/usr/bin/env', 'electrum']
197 | self.command = wallet_command
198 | p, e = subprocess.Popen(self.command + ['daemon', 'status'], stdout=subprocess.PIPE).communicate()
199 | self.not_running_before = b'not running' in p
200 | if self.not_running_before:
201 | subprocess.call(self.command + ['daemon', 'start'])
202 |
203 | if wallet_path is not None:
204 | print('Using wallet: ', wallet_path)
205 | self._command(['daemon', 'load_wallet'], output=False)
206 |
207 | def __del__(self):
208 | if self.not_running_before:
209 | subprocess.call(self.command + ['daemon', 'stop'])
210 |
211 | def create_transaction(self, amount, address, fee=None):
212 | """
213 | Create a transaction
214 | :param amount: amount of bitcoins to be transferred
215 | :param address: address to transfer to
216 | :param fee: None for autofee, or specify own fee
217 | :return: transaction details
218 | """
219 | if fee is None:
220 | transaction = self._command(['payto', str(address), str(amount)])
221 | else:
222 | transaction = self._command(['payto', str(address), str(amount), '-f', str(fee)])
223 | jtrs = json.loads(transaction)
224 | return jtrs['hex']
225 |
226 | def broadcast(self, transaction):
227 | """
228 | Broadcast a transaction.
229 | If successful it returns a transaction_id, otherwise it doesn't
230 | :param transaction: hex of transaction
231 | :return: transaction_id (or also called transaction hash)
232 | """
233 | transaction_id = self._command(['broadcast', transaction])
234 | return transaction_id
235 |
236 | def get_balance(self):
237 | """
238 | Return the balance of the default electrum wallet
239 | :return: balance of default wallet
240 | """
241 | output = self._command(['getbalance'])
242 | print('\n\n', output, '\n\n')
243 | balance_dict = json.loads(output)
244 | return balance_dict
245 |
246 | def get_addresses(self):
247 | """
248 | Return the list of addresses of default wallet
249 | :return:
250 | """
251 | address = self._command(['listaddresses'])
252 | addr = json.loads(address)
253 | return addr
254 |
255 | def _command(self, c, output=True):
256 | command = self.command + c
257 | if self._wallet_path is not None:
258 | command += ['-w', self._wallet_path]
259 |
260 | if output:
261 | return subprocess.check_output(command).decode()
262 | else:
263 | subprocess.call(command)
264 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [bdist_wheel]
2 | python-tag = py3
3 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from cloudomate.globals import __version__
2 | from codecs import open
3 | from os import path
4 | import sys
5 |
6 |
7 | from setuptools import setup, find_packages
8 |
9 | here = path.abspath(path.dirname(__file__))
10 |
11 | with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
12 | long_description = f.read()
13 |
14 | if sys.version_info.major == 2:
15 | package_data = {
16 | b'cloudomate': [],
17 | }
18 | else:
19 | package_data = {
20 | 'cloudomate': [],
21 | }
22 |
23 | setup(
24 | name='cloudomate',
25 |
26 | version=__version__,
27 |
28 | description='Automate buying VPS instances with Bitcoin',
29 | long_description=long_description,
30 |
31 | url='https://github.com/tribler/cloudomate',
32 |
33 | author='PlebNet',
34 | author_email='authentic8989@gmail.com',
35 |
36 | license='LGPLv3',
37 |
38 | classifiers=[
39 | 'Development Status :: 3 - Alpha',
40 |
41 | 'Intended Audience :: Developers',
42 | 'Topic :: System :: Installation/Setup',
43 | 'Topic :: Software Development :: Libraries :: Python Modules',
44 |
45 | 'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)',
46 |
47 | 'Programming Language :: Python :: 3',
48 | 'Programming Language :: Python :: 3.6',
49 |
50 | 'Operating System :: POSIX :: Linux',
51 | 'Operating System :: MacOS',
52 | ],
53 |
54 | keywords='vps bitcoin',
55 |
56 | packages=find_packages(exclude=['docs', 'test']),
57 |
58 | install_requires=[
59 | 'appdirs',
60 | 'lxml',
61 | 'MechanicalSoup',
62 | 'CurrencyConverter',
63 | 'bs4',
64 | 'forex-python',
65 | 'parameterized',
66 | 'fake-useragent',
67 | 'CaseInsensitiveDict',
68 | 'ConfigParser',
69 | 'future',
70 | 'requests[security]',
71 | 'python-dateutil',
72 | 'websocket-client',
73 | 'selenium',
74 | 'geckodriver-autoinstaller'
75 | ],
76 |
77 | extras_require={
78 | 'dev': [],
79 | 'test': ['mock', 'parameterized'],
80 | },
81 |
82 | package_data=package_data,
83 |
84 | entry_points={
85 | 'console_scripts': [
86 | 'cloudomate=cloudomate.cmdline:execute',
87 | ],
88 | },
89 | )
90 |
--------------------------------------------------------------------------------