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