twitter.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const fs = require("fs");
  4. const html_entities_1 = require("html-entities");
  5. const path = require("path");
  6. const sha1 = require("sha1");
  7. const Twitter = require("twitter");
  8. const loggers_1 = require("./loggers");
  9. const webshot_1 = require("./webshot");
  10. const logger = loggers_1.getLogger('twitter');
  11. const entities = new html_entities_1.XmlEntities();
  12. class default_1 {
  13. constructor(opt) {
  14. this.launch = () => {
  15. this.webshot = new webshot_1.default(() => setTimeout(this.work, this.workInterval * 1000));
  16. };
  17. this.work = () => {
  18. const lock = this.lock;
  19. if (this.workInterval < 1)
  20. this.workInterval = 1;
  21. if (lock.feed.length === 0) {
  22. setTimeout(() => {
  23. this.work();
  24. }, this.workInterval * 1000);
  25. return;
  26. }
  27. if (lock.workon >= lock.feed.length)
  28. lock.workon = 0;
  29. if (!lock.threads[lock.feed[lock.workon]] ||
  30. !lock.threads[lock.feed[lock.workon]].subscribers ||
  31. lock.threads[lock.feed[lock.workon]].subscribers.length === 0) {
  32. logger.warn(`nobody subscribes thread ${lock.feed[lock.workon]}, removing from feed`);
  33. delete lock.threads[lock.feed[lock.workon]];
  34. lock.feed.splice(lock.workon, 1);
  35. fs.writeFileSync(path.resolve(this.lockfile), JSON.stringify(lock));
  36. this.work();
  37. return;
  38. }
  39. logger.debug(`pulling feed ${lock.feed[lock.workon]}`);
  40. const promise = new Promise(resolve => {
  41. let match = lock.feed[lock.workon].match(/https:\/\/twitter.com\/([^\/]+)\/lists\/([^\/]+)/);
  42. let config;
  43. let endpoint;
  44. if (match) {
  45. config = {
  46. owner_screen_name: match[1],
  47. slug: match[2],
  48. tweet_mode: 'extended',
  49. };
  50. endpoint = 'lists/statuses';
  51. }
  52. else {
  53. match = lock.feed[lock.workon].match(/https:\/\/twitter.com\/([^\/]+)/);
  54. if (match) {
  55. config = {
  56. screen_name: match[1],
  57. exclude_replies: false,
  58. tweet_mode: 'extended',
  59. };
  60. endpoint = 'statuses/user_timeline';
  61. }
  62. }
  63. if (endpoint) {
  64. const offset = lock.threads[lock.feed[lock.workon]].offset;
  65. if (offset > 0)
  66. config.since_id = offset;
  67. this.client.get(endpoint, config, (error, tweets, response) => {
  68. if (error) {
  69. if (error instanceof Array && error.length > 0 && error[0].code === 34) {
  70. logger.warn(`error on fetching tweets for ${lock.feed[lock.workon]}: ${JSON.stringify(error)}`);
  71. lock.threads[lock.feed[lock.workon]].subscribers.forEach(subscriber => {
  72. logger.info(`sending notfound message of ${lock.feed[lock.workon]} to ${JSON.stringify(subscriber)}`);
  73. this.bot.sendTo(subscriber, `链接 ${lock.feed[lock.workon]} 指向的用户或列表不存在,请退订。`);
  74. });
  75. }
  76. else {
  77. logger.error(`unhandled error on fetching tweets for ${lock.feed[lock.workon]}: ${JSON.stringify(error)}`);
  78. }
  79. resolve();
  80. }
  81. else
  82. resolve(tweets);
  83. });
  84. }
  85. });
  86. promise.then((tweets) => {
  87. logger.debug(`api returned ${JSON.stringify(tweets)} for feed ${lock.feed[lock.workon]}`);
  88. if (!tweets || tweets.length === 0) {
  89. lock.threads[lock.feed[lock.workon]].updatedAt = new Date().toString();
  90. return;
  91. }
  92. if (lock.threads[lock.feed[lock.workon]].offset === -1) {
  93. lock.threads[lock.feed[lock.workon]].offset = tweets[0].id_str;
  94. return;
  95. }
  96. if (lock.threads[lock.feed[lock.workon]].offset === 0)
  97. tweets.splice(1);
  98. return this.webshot(this.mode, tweets, (msg, text, author) => {
  99. lock.threads[lock.feed[lock.workon]].subscribers.forEach(subscriber => {
  100. logger.info(`pushing data of thread ${lock.feed[lock.workon]} to ${JSON.stringify(subscriber)}`);
  101. let hash = JSON.stringify(subscriber) + text.replace(/\s+/gm, '');
  102. logger.debug(hash);
  103. hash = sha1(hash);
  104. logger.debug(hash);
  105. this.bot.sendTo(subscriber, this.mode === 0 ? msg : author + entities.decode(entities.decode(text)));
  106. });
  107. }, this.webshotDelay)
  108. .then(() => {
  109. lock.threads[lock.feed[lock.workon]].offset = tweets[0].id_str;
  110. lock.threads[lock.feed[lock.workon]].updatedAt = new Date().toString();
  111. });
  112. })
  113. .then(() => {
  114. lock.workon++;
  115. let timeout = this.workInterval * 1000 / lock.feed.length;
  116. if (timeout < 1000)
  117. timeout = 1000;
  118. fs.writeFileSync(path.resolve(this.lockfile), JSON.stringify(lock));
  119. setTimeout(() => {
  120. this.work();
  121. }, timeout);
  122. });
  123. };
  124. this.client = new Twitter({
  125. consumer_key: opt.consumer_key,
  126. consumer_secret: opt.consumer_secret,
  127. access_token_key: opt.access_token_key,
  128. access_token_secret: opt.access_token_secret,
  129. });
  130. this.lockfile = opt.lockfile;
  131. this.lock = opt.lock;
  132. this.workInterval = opt.workInterval;
  133. this.bot = opt.bot;
  134. this.webshotDelay = opt.webshotDelay;
  135. this.mode = opt.mode;
  136. }
  137. }
  138. exports.default = default_1;