networking.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. var logging = require("./logging");
  2. var request = require("request");
  3. var config = require("./config");
  4. var skins = require("./skins");
  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. // exracts the skin url of a +profile+ object
  9. // returns null when no url found (user has no skin)
  10. function extract_skin_url(profile) {
  11. var url = null;
  12. if (profile && profile.properties) {
  13. profile.properties.forEach(function(prop) {
  14. if (prop.name == "textures") {
  15. var json = Buffer(prop.value, "base64").toString();
  16. var props = JSON.parse(json);
  17. url = props && props.textures && props.textures.SKIN && props.textures.SKIN.url || null;
  18. }
  19. });
  20. }
  21. return url;
  22. }
  23. // make a request to skins.miencraft.net
  24. // the skin url is taken from the HTTP redirect
  25. var get_username_url = function(name, callback) {
  26. request.get({
  27. url: skins_url + name + ".png",
  28. headers: {
  29. "User-Agent": "https://crafatar.com"
  30. },
  31. timeout: config.http_timeout,
  32. followRedirect: false
  33. }, function(error, response, body) {
  34. if (!error && response.statusCode == 301) {
  35. // skin_url received successfully
  36. logging.log(name + " skin url received");
  37. callback(null, response.headers.location);
  38. } else if (error) {
  39. callback(error, null);
  40. } else if (response.statusCode == 404) {
  41. // skin (or user) doesn't exist
  42. logging.log(name + " has no skin");
  43. callback(null, null);
  44. } else if (response.statusCode == 429) {
  45. // Too Many Requests
  46. // Never got this, seems like skins aren't limited
  47. logging.warn(name + body || "Too many requests");
  48. callback(null, null);
  49. } else {
  50. logging.error(name + " Unknown error:");
  51. logging.error(name + " " + response);
  52. callback(body || "Unknown error", null);
  53. }
  54. });
  55. };
  56. // make a request to sessionserver
  57. // the skin_url is taken from the profile
  58. var get_uuid_url = function(uuid, callback) {
  59. request.get({
  60. url: session_url + uuid,
  61. headers: {
  62. "User-Agent": "https://crafatar.com"
  63. },
  64. timeout: config.http_timeout // ms
  65. }, function (error, response, body) {
  66. if (!error && response.statusCode == 200) {
  67. // profile downloaded successfully
  68. logging.log(uuid + " profile downloaded");
  69. callback(null, extract_skin_url(JSON.parse(body)));
  70. } else if (error) {
  71. callback(error, null);
  72. } else if (response.statusCode == 204 || response.statusCode == 404) {
  73. // we get 204 No Content when UUID doesn't exist (including 404 in case they change that)
  74. logging.log(uuid + " uuid does not exist");
  75. callback(null, null);
  76. } else if (response.statusCode == 429) {
  77. // Too Many Requests
  78. callback(body || "Too many requests", null);
  79. } else {
  80. logging.error(uuid + " Unknown error:");
  81. logging.error(uuid + " " + response);
  82. callback(body || "Unknown error", null);
  83. }
  84. });
  85. };
  86. var exp = {};
  87. // download skin_url for +uuid+ (name or uuid)
  88. // callback contains error, skin_url
  89. exp.get_skin_url = function(uuid, callback) {
  90. if (uuid.length <= 16) {
  91. get_username_url(uuid, function(err, url) {
  92. callback(err, url);
  93. });
  94. } else {
  95. get_uuid_url(uuid, function(err, url) {
  96. callback(err, url);
  97. });
  98. }
  99. };
  100. // downloads skin file from +url+
  101. // callback contains error, image
  102. exp.get_skin = function(url, uuid, callback) {
  103. request.get({
  104. url: url,
  105. headers: {
  106. "User-Agent": "https://crafatar.com"
  107. },
  108. encoding: null, // encoding must be null so we get a buffer
  109. timeout: config.http_timeout // ms
  110. }, function (error, response, body) {
  111. if (!error && response.statusCode == 200) {
  112. // skin downloaded successfully
  113. logging.log(uuid + " downloaded skin");
  114. logging.debug(uuid + " " + url);
  115. callback(null, body);
  116. } else {
  117. if (error) {
  118. logging.error(uuid + " error downloading '" + url + "': " + error);
  119. } else if (response.statusCode == 404) {
  120. logging.warn(uuid + " texture not found (404): " + url);
  121. } else if (response.statusCode == 429) {
  122. // Too Many Requests
  123. // Never got this, seems like textures aren't limited
  124. logging.warn(uuid + " too many requests for " + url);
  125. logging.warn(uuid + " " + body);
  126. } else {
  127. logging.error(uuid + " unknown error for " + url);
  128. logging.error(uuid + " " + response);
  129. logging.error(uuid + " " + body);
  130. error = "unknown error"; // Error needs to be set, otherwise null in callback
  131. }
  132. callback(error, null);
  133. }
  134. });
  135. };
  136. exp.save_skin = function(uuid, hash, outpath, callback) {
  137. if (hash) {
  138. var skinurl = "http://textures.minecraft.net/texture/" + hash;
  139. exp.get_skin(skinurl, uuid, function(err, img) {
  140. if (err) {
  141. logging.error(uuid + " error while downloading skin");
  142. callback(err, null);
  143. } else {
  144. fs.writeFile(outpath, img, "binary", function(err){
  145. if (err) {
  146. logging.log(uuid + " error: " + err);
  147. }
  148. callback(null, img);
  149. });
  150. }
  151. });
  152. } else {
  153. callback(null, null);
  154. }
  155. };
  156. module.exports = exp;