twitter.js 6.1 KB

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