1 module coinmarketcap_api.coinmarketcap_api;
2 
3 import std.array: join;
4 import std.conv: to;
5 import std.json;
6 import std.net.curl;
7 import std.uri;
8 import std.zlib;
9 
10 class CoinmarketcapAPI {
11     protected:
12     const string BASE_URL = "https://pro-api.coinmarketcap.com";
13     HTTP client;
14     string url;
15     string versionApi;
16     string _version = "0.1.1";
17 
18     
19     public:
20     
21     this (in string apiKey, in string versionApi = "v1") {
22         this.versionApi = versionApi;
23         this.url = BASE_URL ~ "/" ~ versionApi ~ "/";
24 
25         this.client = HTTP();
26         client.method = HTTP.Method.get;
27         client.addRequestHeader("X-CMC_PRO_API_KEY", apiKey);
28         client.addRequestHeader("Accept", "application/json");
29         client.addRequestHeader("Accept-Charset", "UTF-8");
30         client.addRequestHeader("Accept-Encoding", "deflate, gzip");
31         client.addRequestHeader("User-Agent", "coinmarketcap-d-api/"~this._version);
32     }
33 
34     HTTP getClient () {
35         return client;
36     }
37 
38 
39     /**
40     * Get a paginated list of all cryptocurrencies with latest market data
41 
42     * @param {int=} Return results from rank start and above
43     * @param {int=} Only returns limit number of results [1..5000]
44     * @param {string[]=} Return info in terms of another currency
45     * @param {string=} Sort results by the options at https://pro.coinmarketcap.com/api/v1#operation/getV1CryptocurrencyListingsLatest
46     * @param {string=} Direction in which to order cryptocurrencies ("asc" | "desc")
47     * @param {string=} Type of cryptocurrency to include ("all" | "coins" | "tokens")
48     *
49     * @example
50     * getList(1, 10)
51     * getList(1, 10, ["EUR", "USD"])
52     */
53     auto getList (T = string)(
54         in int start = 1, in int limit = 100, in string[] convert = ["USD"],
55         in string sort = "market_cap", in string sortDir = "", in string cryptocurrencyType = "all"
56     ) {
57         string[string] data = [
58             "start": to!string(start),
59             "limit": to!string(limit),
60             "convert": join(convert, ","),
61             "sort": sort,
62             "cryptocurrency_type": cryptocurrencyType
63         ];
64         if (sortDir != "") data["sort_dir"] = sortDir;
65         return request!T("cryptocurrency/listings/latest", data);
66     }
67 
68 
69     /**
70     * Get static metadata for one or more cryptocurrencies
71     *
72     * @param {string|string[]|int|int[]=} One or more comma separated cryptocurrency IDs or symbols
73     * 
74     * @example
75     * metadata(2);
76     * metadata(1, 2);
77     * metadata([1, 2]);
78     * metadata("BTC,ETH");
79     * metadata("BTC", "ETH");
80     * metadata(["BTC", "ETH"]);
81     */
82     auto getMetadata (T = string)(in int[] ids) {
83         return request!T("cryptocurrency/info", ["symbol", join(to!(string[])(ids), ",")]);
84     }
85     auto getMetadata (T = string)(in int[] ids...) {
86         return request!T("cryptocurrency/info", ["symbol", join(to!(string[])(ids), ",")]);
87     }
88     auto getMetadata (T = string)(in string[] symbols) {
89         return request!T("cryptocurrency/info", ["symbol": join(symbols, ",")]);
90     }
91     auto getMetadata (T = string)(in string[] symbols...) {
92         return request!T("cryptocurrency/info", ["symbol": join(symbols, ",")]);
93     }
94 
95 
96     /**
97     * Returns a paginated list of all cryptocurrencies by CoinMarketCap ID
98     *
99     * @param {string=} active or inactive coins
100     * @param {int=} Optionally offset the start (1-based index) of the paginated list of items to return.
101     * @param {int=} Optionally specify the number of results to return
102     * @param {string=|string[]} Optionally pass a comma-separated list of cryptocurrency symbols to return CoinMarketCap IDs for.
103     * If this option is passed, other options will be ignored.
104     * 
105     * @example
106     * getMap();
107     * getMap("active", 1, 10);
108     * getMap("active", 1, 1, ["BTC", "ETH"]);
109     */
110     auto getMap (T = string)(in string status = "active", in int start = 1, in int limit = 100, in string symbols = "") {
111         auto data = [
112             "listing_status": status,
113             "start": to!string(start),
114             "limit": to!string(limit)
115         ];
116         if (symbols != "") data["symbol"] = symbols;
117         return request!T("cryptocurrency/map", data);
118     }
119     auto getMap (T = string)(in string status, in int start, in int limit, in string[] symbols) {
120 
121         return getMap!T(status, start, limit, join(symbols, ","));
122     }
123 
124 
125     /**
126     * Get latest market quote for 1 or more cryptocurrencies
127 
128     * @param {string|string[]|int|int[]}  One or more comma separated cryptocurrency symbols
129     * @param {string|string[]=} Return quotes in terms of another currency
130     *
131     * @example
132     * getQuotes("BTC")
133     * getQuotes(1)
134     * getQuotes("BTC,ETH", "EUR")
135     * getQuotes(["BTC", "ETH"], ["EUR, "USD"])
136     * getQuotes(1, ["EUR, "USD"])
137     * getQuotes([1, 2], ["EUR, "USD"])
138     */
139     auto getQuotes (T = string)(in string symbol, in string convert = "USD") {
140         auto data = [
141             "symbol": symbol,
142             "convert": convert
143         ];
144         return request!T("cryptocurrency/quotes/latest", data);
145     }
146     auto getQuotes (T = string)(in string symbol, in string[] convert) {
147         return getQuotes!T(symbol, join(convert, ","));
148     }
149     auto getQuotes (T = string)(in string[] symbols, in string convert) {
150         return getQuotes!T(join(symbols, ","), convert);
151     }
152     auto getQuotes (T = string)(in string[] symbols, in string[] convert) {
153         return getQuotes!T(join(symbols, ","), join(convert, ","));
154     }
155 
156     auto getQuotes (T = string)(in int id, in string convert = "USD") {
157         auto data = [
158             "id": to!string(id),
159             "convert": convert
160         ];
161         return request!T("cryptocurrency/quotes/latest", data);
162     }
163     auto getQuotes (T = string)(in int id, in string[] convert) {
164         return getQuotes!T(to!string(id), join(convert, ","));
165     }
166     auto getQuotes (T = string)(in int[] ids, in string convert) {
167         return getQuotes!T(join(to!(string[])(ids), ","), convert);
168     }
169     auto getQuotes (T = string)(in int[] ids, in string[] convert) {
170         return getQuotes!T(join(to!(string[])(ids), ","), join(convert, ","));
171     }
172 
173 
174     /**
175     * Get global information
176     * 
177     * @param {string|string[]=} Return quotes in terms of another currency
178     *
179     * @example
180     * getGlobal("ETH")
181     * getGlobal(["ETH", "LTC"])
182     */
183     auto getGlobal (T = string)(in string convert = "USD") {
184         return request!T("global-metrics/quotes/latest", ["convert": convert]);
185     }
186     auto getGlobal (T = string)(in string[] convert) {
187         return getGlobal!T(join(convert, ","));
188     }
189 
190 
191     string paramsToStr (in string[string] params) {
192         string[] ar;
193         foreach(key, val; params) {
194             ar ~= key ~ "=" ~ val.encode;
195         }
196         return join(ar, "&");
197     }
198 
199     T request (T = string)(in string method, in string[string] params) {
200         return request!T(method, paramsToStr(params));
201     }
202 
203     T request (T: JSONValue)(in string method, in string[string] params) {
204         return request!T(method, paramsToStr(params));
205     }
206     
207     T request (T = string)(in string method, in string param = "") {
208         bool isGzip = false;
209         ubyte[] result = [];
210         UnCompress decompressor = new UnCompress;
211 
212         if (param == "") client.url = this.url ~ method;
213         else client.url = this.url ~ method ~ "?" ~ param;
214 
215         client.onReceiveHeader = (in char[] key, in char[] value) {
216             string v = cast(string)value;
217             if (cast(string)key == "content-encoding" && (v == "gzip" || v == "deflate")) isGzip = true;
218         };
219         client.onReceive = (ubyte[] data)
220         {
221             result ~= data;
222             return data.length;
223         };
224         client.perform();
225         return cast(string) (isGzip ? decompressor.uncompress(result) : result);
226     }
227 
228     T request (T: JSONValue)(in string method, in string param = "") {
229         return parseJSON(request(method, param));
230     }
231 }