webshot.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const fs_1 = require("fs");
  4. const axios_1 = require("axios");
  5. const CallableInstance = require("callable-instance");
  6. const html_entities_1 = require("html-entities");
  7. const temp = require("temp");
  8. const loggers_1 = require("./loggers");
  9. const koishi_1 = require("./koishi");
  10. const xmlEntities = new html_entities_1.XmlEntities();
  11. const ZHType = (type) => new class extends String {
  12. constructor() {
  13. super(...arguments);
  14. this.type = super.toString();
  15. this.toString = () => `[${super.toString()}]`;
  16. }
  17. }(type);
  18. const typeInZH = {
  19. photo: ZHType('图片'),
  20. video: ZHType('视频'),
  21. animated_gif: ZHType('GIF'),
  22. };
  23. const logger = loggers_1.getLogger('webshot');
  24. class Webshot extends CallableInstance {
  25. constructor(_wsUrl, mode, onready) {
  26. super('webshot');
  27. this.fetchMedia = (url) => new Promise((resolve, reject) => {
  28. logger.info(`fetching ${url}`);
  29. axios_1.default({
  30. method: 'get',
  31. url,
  32. responseType: 'arraybuffer',
  33. timeout: 150000,
  34. }).then(res => {
  35. if (res.status === 200) {
  36. logger.info(`successfully fetched ${url}`);
  37. resolve(res.data);
  38. }
  39. else {
  40. logger.error(`failed to fetch ${url}: ${res.status}`);
  41. reject();
  42. }
  43. }).catch(err => {
  44. logger.error(`failed to fetch ${url}: ${err instanceof Error ? err.message : err}`);
  45. reject();
  46. });
  47. }).then(data => {
  48. var _a;
  49. return (ext => {
  50. const mediaTempFilePath = temp.path({ suffix: `.${ext}` });
  51. fs_1.writeFileSync(mediaTempFilePath, Buffer.from(data));
  52. const path = `file://${mediaTempFilePath}`;
  53. switch (ext) {
  54. case 'jpg':
  55. case 'png':
  56. return koishi_1.Message.Image(path);
  57. case 'mp4':
  58. return koishi_1.Message.Video(path);
  59. }
  60. logger.warn('unable to find MIME type of fetched media, failing this fetch');
  61. throw Error();
  62. })(((_a = (/\?format=([a-z]+)&/.exec(url))) !== null && _a !== void 0 ? _a : (/.*\/.*\.([^?]+)/.exec(url)))[1]);
  63. });
  64. this.mode = mode;
  65. onready();
  66. }
  67. webshot(user, fleets, callback, webshotDelay) {
  68. let promise = new Promise(resolve => {
  69. resolve();
  70. });
  71. fleets.forEach(fleet => {
  72. var _a, _b;
  73. promise = promise.then(() => {
  74. logger.info(`working on ${user.screen_name}/${fleet.fleet_id}`);
  75. });
  76. let messageChain = '';
  77. const author = `${user.name} (@${user.screen_name}):\n`;
  78. const date = `${new Date(fleet.created_at)}\n`;
  79. let text = (_b = author + date + ((_a = fleet.media_bounding_boxes) === null || _a === void 0 ? void 0 : _a.map(box => box.entity.value).join('\n'))) !== null && _b !== void 0 ? _b : '';
  80. messageChain += author + date;
  81. if (1 - this.mode % 2)
  82. promise = promise.then(() => {
  83. const media = fleet.media_entity;
  84. let url;
  85. if (fleet.media_key.media_category === 'TWEET_IMAGE') {
  86. media.type = 'photo';
  87. url = media.media_url_https.replace(/\.([a-z]+)$/, '?format=$1') + '&name=orig';
  88. }
  89. else {
  90. media.type = fleet.media_key.media_category === 'TWEET_VIDEO' ? 'video' : 'animated_gif';
  91. media.video_info = media.media_info.video_info;
  92. text += `[${typeInZH[media.type].type}]`;
  93. url = media.video_info.variants
  94. .filter(variant => variant.bit_rate !== undefined)
  95. .sort((var1, var2) => var2.bit_rate - var1.bit_rate)
  96. .map(variant => variant.url)[0];
  97. }
  98. const altMessage = `\n[失败的${typeInZH[media.type].type}:${url}]`;
  99. return this.fetchMedia(url)
  100. .catch(error => {
  101. logger.warn('unable to fetch media, sending plain text instead...');
  102. return altMessage;
  103. })
  104. .then(msg => { messageChain += msg; });
  105. });
  106. promise.then(() => {
  107. logger.info(`done working on ${user.screen_name}/${fleet.fleet_id}, message chain:`);
  108. logger.info(JSON.stringify(messageChain));
  109. callback(messageChain, xmlEntities.decode(text));
  110. });
  111. });
  112. return promise;
  113. }
  114. }
  115. exports.default = Webshot;