networking.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. var logging = require("./logging");
  2. var request = require("requestretry");
  3. var config = require("./config");
  4. var fs = require("fs");
  5. var session_url = "https://sessionserver.mojang.com/session/minecraft/profile/";
  6. var skins_url = "https://skins.minecraft.net/MinecraftSkins/";
  7. var capes_url = "https://skins.minecraft.net/MinecraftCloaks/";
  8. var exp = {};
  9. function extract_url(profile, property) {
  10. var url = null;
  11. if (profile && profile.properties) {
  12. profile.properties.forEach(function(prop) {
  13. if (prop.name === "textures") {
  14. var json = new Buffer(prop.value, "base64").toString();
  15. var props = JSON.parse(json);
  16. url = props && props.textures && props.textures[property] && props.textures[property].url || null;
  17. }
  18. });
  19. }
  20. return url;
  21. };
  22. // exracts the skin url of a +profile+ object
  23. // returns null when no url found (user has no skin)
  24. exp.extract_skin_url = function(profile) {
  25. return extract_url(profile, 'SKIN');
  26. };
  27. // exracts the cape url of a +profile+ object
  28. // returns null when no url found (user has no cape)
  29. exp.extract_cape_url = function(profile) {
  30. return extract_url(profile, 'CAPE');
  31. };
  32. // makes a GET request to the +url+
  33. // +options+ hash includes various options for
  34. // encoding and timeouts, defaults are already
  35. // specified. +callback+ contains the body, response,
  36. // and error buffer. get_from helper method is available
  37. exp.get_from_options = function(url, options, callback) {
  38. request({
  39. url: url,
  40. headers: {
  41. "User-Agent": "https://crafatar.com"
  42. },
  43. timeout: (options.timeout || config.http_timeout),
  44. encoding: (options.encoding || null),
  45. followRedirect: (options.folow_redirect || false),
  46. maxAttempts: 2,
  47. retryDelay: 2000,
  48. retryStrategy: request.RetryStrategies.NetworkError
  49. }, function(error, response, body) {
  50. // 200 or 301 depending on content type
  51. if (!error && (response.statusCode === 200 || response.statusCode === 301)) {
  52. // response received successfully
  53. logging.log(url + " url received");
  54. callback(body, response, null);
  55. } else if (error) {
  56. callback(body || null, response, error);
  57. } else if (response.statusCode === 404) {
  58. // page doesn't exist
  59. logging.log(url + " url does not exist");
  60. callback(null, response, null);
  61. } else if (response.statusCode === 429) {
  62. // Too Many Requests exception - code 429
  63. logging.warn(body || "Too many requests");
  64. callback(body || null, response, error);
  65. } else {
  66. logging.error(url + " Unknown error:");
  67. //logging.error(response);
  68. callback(body || null, response, error);
  69. }
  70. });
  71. };
  72. // helper method for get_from_options, no options required
  73. exp.get_from = function(url, callback) {
  74. exp.get_from_options(url, {}, function(body, response, err) {
  75. callback(body, response, err);
  76. });
  77. };
  78. // specifies which numbers identify what url
  79. var mojang_url_types = {
  80. 1: skins_url,
  81. 2: capes_url
  82. };
  83. // make a request to skins.miencraft.net
  84. // the skin url is taken from the HTTP redirect
  85. // type reference is above
  86. exp.get_username_url = function(name, type, callback) {
  87. exp.get_from(mojang_url_types[type] + name + ".png", function(body, response, err) {
  88. if (!err) {
  89. callback(err, response ? (response.statusCode === 404 ? null : response.headers.location) : null);
  90. } else {
  91. callback(err, null);
  92. }
  93. });
  94. };
  95. // gets the URL for a skin/cape from the profile
  96. // +type+ specifies which to retrieve
  97. exp.get_uuid_url = function(profile, type, callback) {
  98. var url = null;
  99. if (type === 1) {
  100. url = exp.extract_skin_url(profile);
  101. } else if (type === 2) {
  102. url = exp.extract_cape_url(profile);
  103. }
  104. callback(url || null);
  105. };
  106. // make a request to sessionserver
  107. // profile is returned as json
  108. exp.get_profile = function(uuid, callback) {
  109. if (!uuid) {
  110. callback(null, null);
  111. } else {
  112. exp.get_from_options(session_url + uuid, {encoding: "utf8"}, function(body, response, err) {
  113. callback(err !== null ? err : null, (body !== null ? JSON.parse(body) : null));
  114. });
  115. }
  116. };
  117. // todo remove middleman
  118. // +uuid+ is likely a username and if so
  119. // +uuid+ is used to get the url, otherwise
  120. // +profile+ will be used to get the url
  121. exp.get_skin_url = function(uuid, profile, callback) {
  122. getUrl(uuid, profile, 1, function(url) {
  123. callback(url);
  124. });
  125. };
  126. // +uuid+ is likely a username and if so
  127. // +uuid+ is used to get the url, otherwise
  128. // +profile+ will be used to get the url
  129. exp.get_cape_url = function(uuid, profile, callback) {
  130. getUrl(uuid, profile, 2, function(url) {
  131. callback(url);
  132. });
  133. };
  134. function getUrl(uuid, profile, type, callback) {
  135. if (uuid.length <= 16) {
  136. //username
  137. exp.get_username_url(uuid, type, function(err, url) {
  138. callback(url || null);
  139. });
  140. } else {
  141. exp.get_uuid_url(profile, type, function(url) {
  142. callback(url || null);
  143. });
  144. }
  145. }
  146. // downloads skin file from +url+
  147. // callback contains error, image
  148. exp.get_skin = function(url, callback) {
  149. exp.get_from(url, function(body, response, err) {
  150. callback(body, err);
  151. });
  152. };
  153. exp.save_texture = function(uuid, hash, outpath, callback) {
  154. if (hash) {
  155. var textureurl = "http://textures.minecraft.net/texture/" + hash;
  156. exp.get_from(textureurl, function(img, response, err) {
  157. if (err) {
  158. logging.error(uuid + "error while downloading texture");
  159. callback(err, response, null);
  160. } else {
  161. fs.writeFile(outpath, img, "binary", function(err) {
  162. if (err) {
  163. logging.log(uuid + " error: " + err);
  164. }
  165. callback(err, response, img);
  166. });
  167. }
  168. });
  169. } else {
  170. callback(null, null, null);
  171. }
  172. };
  173. exp.get_cape = function(url, callback) {
  174. exp.get_from(url, function(body, response, err) {
  175. callback(err, body);
  176. });
  177. };
  178. module.exports = exp;