├── .gitignore
├── MANIFEST.in
├── setup.py
├── UNLICENSE
├── README.md
└── aboki
/.gitignore:
--------------------------------------------------------------------------------
1 | *.egg-info
2 | dist
3 | build
4 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include README.md setup.py UNLICENSE
2 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from os.path import abspath, dirname, join, normpath
2 | from setuptools import setup
3 |
4 |
5 | setup(
6 |
7 | # Basic package information:
8 | name = 'aboki',
9 | version = '0.3',
10 | scripts = ('aboki', ),
11 |
12 | # Packaging options:
13 | zip_safe = False,
14 | include_package_data = True,
15 |
16 | # Package dependencies:
17 | install_requires = ['docopt>=0.6.2', 'requests>=2.13.0', 'beautifulsoup4>=4.5.3', 'html5lib>=0.999999999'],
18 |
19 | # Metadata for PyPI:
20 | author = 'Akinjide Bankole',
21 | author_email = 'r@akinjide.me',
22 | license = 'UNLICENSE',
23 | url = 'https://github.com/akinjide/aboki',
24 | keywords = 'forex cli utility fx currency abokifx aboki market rate',
25 | description = 'Black market currency rate instantly in your terminal!',
26 | long_description = open(normpath(join(dirname(abspath(__file__)), 'README.md'))).read()
27 | )
--------------------------------------------------------------------------------
/UNLICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # aboki
2 |
3 | Black market currency rate instantly in your terminal! (Powered by
4 | [AbokiFx](https://abokifx.com/).)
5 |
6 | NOTE: I am in no way affiliated with AbokiFx. I don't know anyone that works there, have no relationship with them -- nothing.
7 |
8 |
9 | ## Prerequisites
10 |
11 | Before using `aboki`, you should be familiar with foreign exchange and black market... *Duh!*
12 |
13 |
14 | ## Installation
15 |
16 | You can install `aboki` via [pip](http://pip.readthedocs.org/en/latest/):
17 |
18 | ```bash
19 | $ pip install aboki
20 | ```
21 |
22 | Once `aboki` has been installed, you'll can start by running:
23 |
24 | ```bash
25 | $ aboki test
26 | ```
27 |
28 | ## Usage
29 |
30 | If you simply run `aboki` on the command line, you'll get a list of help.
31 |
32 | ```bash
33 | $ aboki recent # show recent exchange rates
34 | $ aboki rates # show current exchange rates
35 | $ aboki rate # show exchange rate for currency
36 | $ aboki convert # see how much money you'll get if you sell
37 | $ aboki test # test aboki
38 | $ aboki (-h | --help) # display help information
39 | ```
40 |
41 | All commands that have side effects will prompt you for action before doing anything for added security.
42 |
43 | ## Changelog
44 |
45 | v0.3: 05-09-2017
46 |
47 | - Python 3 Support and Error handling.
48 |
49 | v0.2: 04-16-2017
50 |
51 | - Fixing some small documentation issues.
52 |
53 | v0.1: 04-12-2017
54 |
55 | - First release!
56 |
57 |
58 | ## Like This?
59 |
60 | If you've enjoyed using `aboki`, feel free to star this project, send me some bitcoin! My address is:
61 |
62 | **17AcFdn8kTpGw1R34MC5U5SyZHrMbZK4Sq**
63 |
64 | Or... You could tip me on [paypal](https://www.paypal.me/akinjide) :)
65 |
66 | <3
67 |
68 | -Akinjide
69 |
--------------------------------------------------------------------------------
/aboki:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """
3 | NAME:
4 | aboki -
5 |
6 | DESCRIPTION:
7 | Black market currency rate instantly in your terminal! (Powered by AbokiFx:
8 | https://abokifx.com/).
9 |
10 | USAGE:
11 | aboki recent
12 | aboki rates
13 | aboki rate
14 | aboki convert
15 | aboki test
16 | aboki (-h | --help)
17 | aboki --version
18 |
19 | OPTIONS:
20 | (string) Specify rate type for this operation: cbn, movement,
21 | lagos_previous, moneygram, westernunion, otherparallel. This
22 | defaults to 'cbn'.
23 | (string) Specify what currency rate to show.
24 | (float) The amount you'd like to convert.
25 | (string) Specify the currency you are converting from.
26 | (string) Specify the currency you are converting to.
27 | -h --help Show this screen.
28 | --version Show version.
29 |
30 | EXAMPLES:
31 | o Convert usd to ngn
32 |
33 | The following convert command converts a currency to a specified currency:
34 |
35 | aboki convert 500 usd ngn
36 |
37 | Output:
38 |
39 | Conversion Successful
40 | SEE HOW MUCH YOU GET IF YOU SELL
41 | ================================
42 | {
43 | "ngn": 202500.0,
44 | "rate": 405.0,
45 | "usd": 500.0
46 | }
47 | ================================
48 |
49 | o Retrieve usd rate
50 |
51 | The following rate command retrieves currency current rate:
52 |
53 | aboki rate usd
54 |
55 | Output:
56 |
57 | USD Exchange Rate
58 | =================
59 | 387.0
60 | =================
61 |
62 | Written by Akinjide Bankole . Like the software? Star
63 | on Github
64 | Or... Send a tip to Akinjide: 17AcFdn8kTpGw1R34MC5U5SyZHrMbZK4Sq
65 | """
66 |
67 |
68 | from __future__ import absolute_import, print_function
69 | from json import dumps
70 | from sys import exit
71 |
72 | from bs4 import BeautifulSoup
73 | from docopt import docopt
74 | from requests import get, post, exceptions
75 | from pkg_resources import get_distribution
76 |
77 |
78 | # Globals
79 | API_URI = 'https://www.abokifx.com'
80 | VERSION = 'aboki %s' % get_distribution("aboki").version
81 |
82 |
83 | class Aboki:
84 |
85 | def __init__(self):
86 | self.currencies = ('usd', 'gbp', 'eur')
87 | self.types = ['cbn', 'movement', 'lagos_previous', 'moneygram', 'westernunion', 'otherparallel']
88 | self.quotes = 'Quotes:\t*morning\t**midday\t***evening'
89 | self.note = '**NOTE**: Buy / Sell => 90 / 100\n'
90 |
91 | def make_request(self, path, params={}, data={}, method='GET'):
92 | """Make the specified API request, and return the HTML data, or quit
93 | with an error.
94 | """
95 |
96 | try:
97 | if method.lower() == 'post':
98 | resp = post('%s/%s' % (API_URI, path), params=params, data=dumps(data), headers={'Content-Type': 'application/json'})
99 | else:
100 | resp = get('%s/%s' % (API_URI, path), params=params)
101 |
102 | if resp.status_code != 200:
103 | print('Error connecting. Please try again.')
104 | print('If the problem persists, please email r@akinjide.me.')
105 | exit(1)
106 | return resp.text
107 |
108 | except (exceptions.RequestException, e): # When exceptions are more than one, they need to be wrapped in parenthesis in py3
109 | print('Error connecting. Please check network and try again.')
110 | print('If the problem persists, please email r@akinjide.me.')
111 | exit(1)
112 |
113 | def parse(self, content):
114 | soup = BeautifulSoup(content, "html5lib")
115 | records = soup.select("body .lagos-market-rates table tbody > tr")
116 | data = [[str(j) for j in record.stripped_strings
117 | if str(j) not in ('NGN', 'Buy / Sell')] for record in records]
118 | return {'title': str(soup.title.string), 'data': data}
119 |
120 | def get_current_rate(self):
121 | """Return the current exchange rate for USD, GBP, EUR."""
122 |
123 | resp = self.make_request('')
124 | rjson = self.parse(resp)
125 | rates = ' / '.join(rjson['data'][0][1:]).split(' / ')
126 | return dict(zip(self.currencies, [float(rate) for rate in rates if '*' not in rate]))
127 |
128 | def recent(self):
129 | """List recent exchange rates for USD, GBP, EUR."""
130 |
131 | resp = self.make_request('')
132 | rjson = self.parse(resp)
133 | underline = '==' * len(rjson['title'])
134 |
135 | print(self.quotes)
136 | print(self.note)
137 | print(rjson['title'])
138 | print(underline)
139 | print('\t\t\t\t%s\t\t\t%s\t\t%s' % ('USD', 'GBP', 'EUR'))
140 |
141 | for datum in rjson['data']:
142 | print('\t|\t'.join(datum))
143 |
144 | print(underline)
145 |
146 | def rates(self, type):
147 | """List current exchange rates.
148 |
149 | Supported types: cbn, movement, lagos_previous, moneygram, westernunion and otherparallel.
150 | """
151 |
152 | param = None
153 | if type in self.types:
154 | param = {'rates': self.types[self.types.index(type)]}
155 | else:
156 | print('\nNot sure which type?')
157 | print('You can specify any of this: '
158 | 'cbn, movement, lagos_previous, moneygram, westernunion or otherparallel\n'
159 | '[default: cbn]\n')
160 | param = {'rates': 'cbn'}
161 |
162 | resp = self.make_request('ratetypes', param)
163 | rjson = self.parse(resp)
164 | underline = '==' * len(rjson['title'])
165 |
166 | if type in ('otherparallel', 'movement'):
167 | print(self.quotes)
168 | print(self.note)
169 | elif type == 'lagos_previous':
170 | print(self.note)
171 |
172 | print(rjson['title'])
173 | print(underline)
174 |
175 | for index, data in enumerate(rjson['data']):
176 | if index == 0:
177 | print('\t\t\t%s' % '\t\t\t'.join(data))
178 | else:
179 | print('\t|\t'.join(data))
180 |
181 | print(underline)
182 |
183 | def rate(self, currency):
184 | """List current exchange rate for currency."""
185 |
186 | rates = self.get_current_rate()
187 | xc = rates.get(currency)
188 |
189 | if xc:
190 | print('%s Exchange Rate' % (currency.upper()))
191 | print('================')
192 | print(xc)
193 | print('================')
194 | else:
195 | print('\nNot sure which type?')
196 | print('You can specify any of this: usd, gbp, eur or ngn\n')
197 |
198 | def convert(self, amount, FROM, TO):
199 | """Convert currency with current rate."""
200 |
201 | rates = self.get_current_rate()
202 | FROM = FROM.strip().lower()
203 | TO = TO.strip().lower()
204 | error = 'Oops! You are trying to do something useful. Please use any available currencies for conversion'
205 |
206 | try:
207 | assert FROM and TO and 'ngn' in (FROM, TO), error
208 | assert FROM in self.currencies or TO in self.currencies, error
209 | except (AssertionError): # Wrapped exceptions in parenthesis
210 | print(error) # AssertionError isn't handled during conversion because 'e' is undefined
211 | exit(1)
212 |
213 | ojson = {FROM: amount}
214 | if FROM == 'ngn':
215 | ojson[TO] = amount / rates[TO]
216 | ojson['rate'] = rates[TO]
217 | elif FROM in self.currencies:
218 | ojson['ngn'] = amount * rates[FROM]
219 | ojson['rate'] = rates[FROM]
220 |
221 | print('Conversion Successful')
222 | print('SEE HOW MUCH YOU GET IF YOU SELL')
223 | print('================================')
224 | print(dumps(ojson, sort_keys=True, indent=2, separators=(',', ': ')))
225 | print('================================')
226 |
227 | def test(self):
228 | """Test to make sure everything's working."""
229 |
230 | resp = self.make_request('')
231 | if resp:
232 | print("Yippe! you've broken nothing!")
233 | else:
234 | print("Oops! Catastrophic Failure")
235 | print('If the problem persists, please email r@akinjide.me.')
236 | exit(1)
237 |
238 |
239 | def main():
240 | """Handle programmer input, and do stuffs."""
241 |
242 | arguments = docopt(__doc__, version=VERSION)
243 | aboki = Aboki()
244 |
245 | if arguments['recent']:
246 | aboki.recent()
247 | elif arguments['rates']:
248 | aboki.rates(arguments[''])
249 | elif arguments['rate']:
250 | aboki.rate(arguments[''])
251 | elif arguments['convert']:
252 | amount = float(arguments[''])
253 | aboki.convert(amount, arguments[''], arguments[''])
254 | elif arguments['test']:
255 | aboki.test()
256 |
257 |
258 | if __name__ == '__main__':
259 | main()
260 |
--------------------------------------------------------------------------------