networking.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. var http_code = require("http").STATUS_CODES;
  2. var logging = require("./logging");
  3. var request = require("request");
  4. var config = require("./config");
  5. var fs = require("fs");
  6. var session_url = "https://sessionserver.mojang.com/session/minecraft/profile/";
  7. var skins_url = "https://skins.minecraft.net/MinecraftSkins/";
  8. var capes_url = "https://skins.minecraft.net/MinecraftCloaks/";
  9. var textures_url = "http://textures.minecraft.net/texture/";
  10. var mojang_urls = [skins_url, capes_url];
  11. var exp = {};
  12. function extract_url(profile, property) {
  13. var url = null;
  14. if (profile && profile.properties) {
  15. profile.properties.forEach(function(prop) {
  16. if (prop.name === "textures") {
  17. var json = new Buffer(prop.value, "base64").toString();
  18. var props = JSON.parse(json);
  19. url = props && props.textures && props.textures[property] && props.textures[property].url || null;
  20. }
  21. });
  22. }
  23. return url;
  24. }
  25. // exracts the skin url of a +profile+ object
  26. // returns null when no url found (user has no skin)
  27. exp.extract_skin_url = function(profile) {
  28. return extract_url(profile, 'SKIN');
  29. };
  30. // exracts the cape url of a +profile+ object
  31. // returns null when no url found (user has no cape)
  32. exp.extract_cape_url = function(profile) {
  33. return extract_url(profile, 'CAPE');
  34. };
  35. // makes a GET request to the +url+
  36. // +options+ hash includes these options:
  37. // encoding (string), default is to return a buffer
  38. // +callback+ contains the body, response,
  39. // and error buffer. get_from helper method is available
  40. exp.get_from_options = function(rid, url, options, callback) {
  41. request.get({
  42. url: url,
  43. headers: {
  44. "User-Agent": "https://crafatar.com"
  45. },
  46. timeout: config.http_timeout,
  47. followRedirect: false,
  48. encoding: (options.encoding || null),
  49. }, function(error, response, body) {
  50. // log url + code + description
  51. var code = response && response.statusCode;
  52. if (!error) {
  53. var logfunc = code && code < 405 ? logging.log : logging.warn;
  54. logfunc(rid + url + " " + code + " " + http_code[code]);
  55. }
  56. // 200 or 301 depending on content type
  57. if (!error && (code === 200 || code === 301)) {
  58. // response received successfully
  59. callback(body, response, null);
  60. } else if (error) {
  61. logging.error(error);
  62. callback(body || null, response, error);
  63. } else if (code === 404 || code === 204) {
  64. // page does not exist
  65. callback(null, response, null);
  66. } else if (code === 429) {
  67. // Too Many Requests exception - code 429
  68. // cause error so the image will not be cached
  69. callback(body || null, response, (error || "TooManyRequests"));
  70. } else {
  71. logging.error(rid + " Unknown reply:");
  72. logging.error(rid + JSON.stringify(response));
  73. callback(body || null, response, error);
  74. }
  75. });
  76. };
  77. // helper method for get_from_options, no options required
  78. exp.get_from = function(rid, url, callback) {
  79. exp.get_from_options(rid, url, {}, function(body, response, err) {
  80. callback(body, response, err);
  81. });
  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(rid, name, type, callback) {
  87. exp.get_from(rid, mojang_urls[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 === 0) {
  100. url = exp.extract_skin_url(profile);
  101. } else if (type === 1) {
  102. url = exp.extract_cape_url(profile);
  103. }
  104. callback(url || null);
  105. };
  106. // make a request to sessionserver for +uuid+
  107. // +callback+ contains error, profile
  108. exp.get_profile = function(rid, uuid, callback) {
  109. if (!uuid) {
  110. callback(null, null);
  111. } else {
  112. exp.get_from_options(rid, session_url + uuid, { encoding: "utf8" }, function(body, response, err) {
  113. callback(err || null, (body !== null ? JSON.parse(body) : null));
  114. });
  115. }
  116. };
  117. // get the skin URL for +userId+
  118. // +profile+ is used if +userId+ is a uuid
  119. exp.get_skin_url = function(rid, userId, profile, callback) {
  120. get_url(rid, userId, profile, 0, function(err, url) {
  121. callback(err, url);
  122. });
  123. };
  124. // get the cape URL for +userId+
  125. // +profile+ is used if +userId+ is a uuid
  126. exp.get_cape_url = function(rid, userId, profile, callback) {
  127. get_url(rid, userId, profile, 1, function(err, url) {
  128. callback(err, url);
  129. });
  130. };
  131. function get_url(rid, userId, profile, type, callback) {
  132. if (userId.length <= 16) {
  133. //username
  134. exp.get_username_url(rid, userId, type, function(err, url) {
  135. callback(err, url || null);
  136. });
  137. } else {
  138. exp.get_uuid_url(profile, type, function(url) {
  139. callback(null, url || null);
  140. });
  141. }
  142. }
  143. exp.save_texture = function(rid, tex_hash, outpath, callback) {
  144. if (tex_hash) {
  145. var textureurl = textures_url + tex_hash;
  146. exp.get_from(rid, textureurl, function(img, response, err) {
  147. if (err) {
  148. logging.error(rid + "error while downloading texture");
  149. callback(err, response, null);
  150. } else {
  151. fs.writeFile(outpath, img, "binary", function(err) {
  152. if (err) {
  153. logging.error(rid + "error: " + err.stack);
  154. }
  155. callback(err, response, img);
  156. });
  157. }
  158. });
  159. } else {
  160. callback(null, null, null);
  161. }
  162. };
  163. module.exports = exp;