webshot.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  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. };
  22. const logger = (0, loggers_1.getLogger)('webshot');
  23. class Webshot extends CallableInstance {
  24. constructor(_wsUrl, _mode, onready) {
  25. super('webshot');
  26. this.fetchMedia = (url) => new Promise((resolve, reject) => {
  27. logger.info(`fetching ${url}`);
  28. (0, axios_1.default)({
  29. method: 'get',
  30. url,
  31. responseType: 'arraybuffer',
  32. timeout: 150000,
  33. }).then(res => {
  34. if (res.status === 200) {
  35. logger.info(`successfully fetched ${url}`);
  36. resolve(res.data);
  37. }
  38. else {
  39. logger.error(`failed to fetch ${url}: ${res.status}`);
  40. reject();
  41. }
  42. }).catch(err => {
  43. logger.error(`failed to fetch ${url}: ${err instanceof Error ? err.message : err}`);
  44. reject();
  45. });
  46. }).then(data => (ext => {
  47. const mediaTempFilePath = temp.path({ suffix: `.${ext}` });
  48. (0, fs_1.writeFileSync)(mediaTempFilePath, Buffer.from(data));
  49. const path = `file://${mediaTempFilePath}`;
  50. switch (ext) {
  51. case 'jpg':
  52. case 'png':
  53. return koishi_1.Message.Image(path);
  54. case 'mp4':
  55. return koishi_1.Message.Video(path);
  56. }
  57. logger.warn('unable to find MIME type of fetched media, failing this fetch');
  58. throw Error();
  59. })(/\/.+\.(?:.*?(?<=[?&])stp=dst-(jpg)|(.+?)\?)/.exec(url).filter(g => g)[1]));
  60. this.fetchBestCandidate = ({ image_versions2, video_versions }) => {
  61. const candidates = video_versions || image_versions2.candidates;
  62. const url = candidates
  63. .sort((var1, var2) => var2.width + ((var2 === null || var2 === void 0 ? void 0 : var2.type) || 0) - var1.width - ((var1 === null || var1 === void 0 ? void 0 : var1.type) || 0))
  64. .map(variant => variant.url)[0];
  65. const altMessage = `\n[失败的${typeInZH[video_versions ? 'video' : 'photo'].type}:${url}]`;
  66. return this.fetchMedia(url)
  67. .catch(error => {
  68. logger.warn('unable to fetch media, sending plain text instead...');
  69. return altMessage;
  70. });
  71. };
  72. onready();
  73. }
  74. webshot(mediaItems, callback, webshotDelay) {
  75. const promises = mediaItems.map(item => {
  76. let promise = Promise.resolve();
  77. logger.info(`working on ${item.user.username}/${item.code}`);
  78. let messageChain = '';
  79. const author = `${item.user.full_name} (@${item.user.username}):\n`;
  80. const date = `${new Date(item.taken_at * 1000)}\n`;
  81. messageChain += author + date;
  82. promise = promise.then(() => this.fetchBestCandidate(item))
  83. .then(msg => { messageChain += msg; });
  84. return promise.then(() => {
  85. logger.info(`done working on ${item.user.username}/${item.code}, message chain:`);
  86. logger.info(JSON.stringify(koishi_1.Message.ellipseBase64(messageChain)));
  87. callback(messageChain, xmlEntities.decode(item.caption), author);
  88. });
  89. });
  90. return Promise.all(promises).then();
  91. }
  92. }
  93. exports.default = Webshot;