浏览代码

init media only branch; further typing effort

Mike L 4 年之前
父节点
当前提交
919ddd86ad
共有 11 个文件被更改,包括 99 次插入98 次删除
  1. 9 10
      dist/command.js
  2. 1 1
      dist/main.js
  3. 9 9
      dist/mirai.js
  4. 28 21
      dist/twitter.js
  5. 2 4
      dist/webshot.js
  6. 9 10
      src/command.ts
  7. 1 1
      src/main.ts
  8. 9 9
      src/mirai.ts
  9. 1 1
      src/model.d.ts
  10. 27 28
      src/twitter.ts
  11. 3 4
      src/webshot.ts

+ 9 - 10
dist/command.js

@@ -42,14 +42,13 @@ function parseLink(link) {
 }
 }
 function sub(chat, args, lock, lockfile) {
 function sub(chat, args, lock, lockfile) {
     if (args.length === 0) {
     if (args.length === 0) {
-        return '找不到要订阅的链接。';
+        return '找不到要订阅媒体推文的链接。';
     }
     }
     const match = parseLink(args[0]);
     const match = parseLink(args[0]);
     if (!match) {
     if (!match) {
         return `订阅链接格式错误:
         return `订阅链接格式错误:
 示例:
 示例:
-https://twitter.com/Saito_Shuka
-https://twitter.com/rikakomoe/lists/lovelive`;
+https://twitter.com/Saito_Shuka`;
     }
     }
     const link = match.link;
     const link = match.link;
     let flag = false;
     let flag = false;
@@ -61,7 +60,7 @@ https://twitter.com/rikakomoe/lists/lovelive`;
         lock.feed.push(link);
         lock.feed.push(link);
     if (!lock.threads[link]) {
     if (!lock.threads[link]) {
         lock.threads[link] = {
         lock.threads[link] = {
-            offset: 0,
+            offset: '0',
             subscribers: [],
             subscribers: [],
             updatedAt: '',
             updatedAt: '',
         };
         };
@@ -75,12 +74,12 @@ https://twitter.com/rikakomoe/lists/lovelive`;
         lock.threads[link].subscribers.push(chat);
         lock.threads[link].subscribers.push(chat);
     logger.warn(`chat ${JSON.stringify(chat)} has subscribed ${link}`);
     logger.warn(`chat ${JSON.stringify(chat)} has subscribed ${link}`);
     fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
     fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
-    return `已为此聊天订阅 ${link}`;
+    return `已为此聊天订阅 ${link} 的媒体推文`;
 }
 }
 exports.sub = sub;
 exports.sub = sub;
 function unsub(chat, args, lock, lockfile) {
 function unsub(chat, args, lock, lockfile) {
     if (args.length === 0) {
     if (args.length === 0) {
-        return '找不到要退订的链接。';
+        return '找不到要退订媒体推文的链接。';
     }
     }
     const match = parseLink(args[0]);
     const match = parseLink(args[0]);
     if (!match) {
     if (!match) {
@@ -88,7 +87,7 @@ function unsub(chat, args, lock, lockfile) {
     }
     }
     const link = match.link;
     const link = match.link;
     if (!lock.threads[link]) {
     if (!lock.threads[link]) {
-        return '您没有订阅此链接。\n' + list(chat, args, lock);
+        return '您没有订阅此链接的媒体推文。\n' + list(chat, args, lock);
     }
     }
     let flag = false;
     let flag = false;
     lock.threads[link].subscribers.forEach((c, index) => {
     lock.threads[link].subscribers.forEach((c, index) => {
@@ -100,9 +99,9 @@ function unsub(chat, args, lock, lockfile) {
     if (flag) {
     if (flag) {
         fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
         fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
         logger.warn(`chat ${JSON.stringify(chat)} has unsubscribed ${link}`);
         logger.warn(`chat ${JSON.stringify(chat)} has unsubscribed ${link}`);
-        return `已为此聊天退订 ${link}`;
+        return `已为此聊天退订 ${link} 的媒体推文`;
     }
     }
-    return '您没有订阅此链接。\n' + list(chat, args, lock);
+    return '您没有订阅此链接的媒体推文。\n' + list(chat, args, lock);
 }
 }
 exports.unsub = unsub;
 exports.unsub = unsub;
 function list(chat, args, lock) {
 function list(chat, args, lock) {
@@ -113,6 +112,6 @@ function list(chat, args, lock) {
                 links.push(`${key} ${datetime_1.relativeDate(lock.threads[key].updatedAt)}`);
                 links.push(`${key} ${datetime_1.relativeDate(lock.threads[key].updatedAt)}`);
         });
         });
     });
     });
-    return '此聊天中订阅的链接:\n' + links.join('\n');
+    return '此聊天中订阅媒体推文的链接:\n' + links.join('\n');
 }
 }
 exports.list = list;
 exports.list = list;

+ 1 - 1
dist/main.js

@@ -115,7 +115,7 @@ else {
     }
     }
 }
 }
 Object.keys(lock.threads).forEach(key => {
 Object.keys(lock.threads).forEach(key => {
-    lock.threads[key].offset = -1;
+    lock.threads[key].offset = '-1';
 });
 });
 const qq = new mirai_1.default({
 const qq = new mirai_1.default({
     access_token: config.mirai_access_token,
     access_token: config.mirai_access_token,

+ 9 - 9
dist/mirai.js

@@ -70,23 +70,23 @@ class default_1 {
                 }
                 }
                 const cmdObj = helper_1.default(msg.plain);
                 const cmdObj = helper_1.default(msg.plain);
                 switch (cmdObj.cmd) {
                 switch (cmdObj.cmd) {
-                    case 'twitter_sub':
-                    case 'twitter_subscribe':
+                    case 'twitterpic_sub':
+                    case 'twitterpic_subscribe':
                         msg.reply(this.botInfo.sub(chat, cmdObj.args));
                         msg.reply(this.botInfo.sub(chat, cmdObj.args));
                         break;
                         break;
-                    case 'twitter_unsub':
-                    case 'twitter_unsubscribe':
+                    case 'twitterpic_unsub':
+                    case 'twitterpic_unsubscribe':
                         msg.reply(this.botInfo.unsub(chat, cmdObj.args));
                         msg.reply(this.botInfo.unsub(chat, cmdObj.args));
                         break;
                         break;
                     case 'ping':
                     case 'ping':
-                    case 'twitter':
+                    case 'twitterpic':
                         msg.reply(this.botInfo.list(chat, cmdObj.args));
                         msg.reply(this.botInfo.list(chat, cmdObj.args));
                         break;
                         break;
                     case 'help':
                     case 'help':
-                        msg.reply(`推特搬运机器人:
-/twitter - 查询当前聊天中的订阅
-/twitter_subscribe [链接] - 订阅 Twitter 搬运
-/twitter_unsubscribe [链接] - 退订 Twitter 搬运`);
+                        msg.reply(`推特图片搬运机器人:
+/twitterpic - 查询当前聊天中的订阅
+/twitterpic_subscribe [链接] - 订阅 Twitter 图片搬运
+/twitterpic_unsubscribe [链接] - 退订 Twitter 图片搬运`);
                 }
                 }
             });
             });
         };
         };

+ 28 - 21
dist/twitter.js

@@ -34,9 +34,10 @@ class default_1 {
                 this.work();
                 this.work();
                 return;
                 return;
             }
             }
-            logger.debug(`pulling feed ${lock.feed[lock.workon]}`);
+            const currentFeed = lock.feed[lock.workon];
+            logger.debug(`pulling feed ${currentFeed}`);
             const promise = new Promise(resolve => {
             const promise = new Promise(resolve => {
-                let match = lock.feed[lock.workon].match(/https:\/\/twitter.com\/([^\/]+)\/lists\/([^\/]+)/);
+                let match = currentFeed.match(/https:\/\/twitter.com\/([^\/]+)\/lists\/([^\/]+)/);
                 let config;
                 let config;
                 let endpoint;
                 let endpoint;
                 if (match) {
                 if (match) {
@@ -48,7 +49,7 @@ class default_1 {
                     endpoint = 'lists/statuses';
                     endpoint = 'lists/statuses';
                 }
                 }
                 else {
                 else {
-                    match = lock.feed[lock.workon].match(/https:\/\/twitter.com\/([^\/]+)/);
+                    match = currentFeed.match(/https:\/\/twitter.com\/([^\/]+)/);
                     if (match) {
                     if (match) {
                         config = {
                         config = {
                             screen_name: match[1],
                             screen_name: match[1],
@@ -59,20 +60,20 @@ class default_1 {
                     }
                     }
                 }
                 }
                 if (endpoint) {
                 if (endpoint) {
-                    const offset = lock.threads[lock.feed[lock.workon]].offset;
+                    const offset = lock.threads[currentFeed].offset;
                     if (offset > 0)
                     if (offset > 0)
                         config.since_id = offset;
                         config.since_id = offset;
                     this.client.get(endpoint, config, (error, tweets, response) => {
                     this.client.get(endpoint, config, (error, tweets, response) => {
                         if (error) {
                         if (error) {
                             if (error instanceof Array && error.length > 0 && error[0].code === 34) {
                             if (error instanceof Array && error.length > 0 && error[0].code === 34) {
-                                logger.warn(`error on fetching tweets for ${lock.feed[lock.workon]}: ${JSON.stringify(error)}`);
-                                lock.threads[lock.feed[lock.workon]].subscribers.forEach(subscriber => {
-                                    logger.info(`sending notfound message of ${lock.feed[lock.workon]} to ${JSON.stringify(subscriber)}`);
-                                    this.bot.sendTo(subscriber, `链接 ${lock.feed[lock.workon]} 指向的用户或列表不存在,请退订。`).catch();
+                                logger.warn(`error on fetching tweets for ${currentFeed}: ${JSON.stringify(error)}`);
+                                lock.threads[currentFeed].subscribers.forEach(subscriber => {
+                                    logger.info(`sending notfound message of ${currentFeed} to ${JSON.stringify(subscriber)}`);
+                                    this.bot.sendTo(subscriber, `链接 ${currentFeed} 指向的用户或列表不存在,请退订。`).catch();
                                 });
                                 });
                             }
                             }
                             else {
                             else {
-                                logger.error(`unhandled error on fetching tweets for ${lock.feed[lock.workon]}: ${JSON.stringify(error)}`);
+                                logger.error(`unhandled error on fetching tweets for ${currentFeed}: ${JSON.stringify(error)}`);
                             }
                             }
                             resolve();
                             resolve();
                         }
                         }
@@ -82,16 +83,26 @@ class default_1 {
                 }
                 }
             });
             });
             promise.then((tweets) => {
             promise.then((tweets) => {
-                logger.debug(`api returned ${JSON.stringify(tweets)} for feed ${lock.feed[lock.workon]}`);
+                logger.debug(`api returned ${JSON.stringify(tweets)} for feed ${currentFeed}`);
+                const currentThread = lock.threads[currentFeed];
+                const updateDate = () => currentThread.updatedAt = new Date().toString();
                 if (!tweets || tweets.length === 0) {
                 if (!tweets || tweets.length === 0) {
-                    lock.threads[lock.feed[lock.workon]].updatedAt = new Date().toString();
+                    updateDate();
                     return;
                     return;
                 }
                 }
-                if (lock.threads[lock.feed[lock.workon]].offset === -1) {
-                    lock.threads[lock.feed[lock.workon]].offset = tweets[0].id_str;
+                const topOfFeed = tweets[0].id_str;
+                const updateOffset = () => currentThread.offset = topOfFeed;
+                tweets = tweets.filter(twi => !twi.retweeted_status && twi.extended_entities);
+                if (tweets.length === 0) {
+                    updateDate();
+                    updateOffset();
                     return;
                     return;
                 }
                 }
-                if (lock.threads[lock.feed[lock.workon]].offset === 0)
+                if (currentThread.offset === '-1') {
+                    updateOffset();
+                    return;
+                }
+                if (currentThread.offset === '0')
                     tweets.splice(1);
                     tweets.splice(1);
                 const maxCount = 3;
                 const maxCount = 3;
                 let sendTimeout = 10000;
                 let sendTimeout = 10000;
@@ -109,8 +120,8 @@ class default_1 {
                     }
                     }
                 };
                 };
                 const sendTweets = (msg, text, author) => {
                 const sendTweets = (msg, text, author) => {
-                    lock.threads[lock.feed[lock.workon]].subscribers.forEach(subscriber => {
-                        logger.info(`pushing data of thread ${lock.feed[lock.workon]} to ${JSON.stringify(subscriber)}`);
+                    currentThread.subscribers.forEach(subscriber => {
+                        logger.info(`pushing data of thread ${currentFeed} to ${JSON.stringify(subscriber)}`);
                         const retry = (reason, count) => {
                         const retry = (reason, count) => {
                             if (count <= maxCount)
                             if (count <= maxCount)
                                 sendTimeout *= (count + 2) / (count + 1);
                                 sendTimeout *= (count + 2) / (count + 1);
@@ -133,11 +144,7 @@ class default_1 {
                         this.bot.sendTo(subscriber, msg, sendTimeout).catch(error => retry(error, 1));
                         this.bot.sendTo(subscriber, msg, sendTimeout).catch(error => retry(error, 1));
                     });
                     });
                 };
                 };
-                return this.webshot(tweets, sendTweets, this.webshotDelay)
-                    .then(() => {
-                    lock.threads[lock.feed[lock.workon]].offset = tweets[0].id_str;
-                    lock.threads[lock.feed[lock.workon]].updatedAt = new Date().toString();
-                });
+                return this.webshot(tweets, sendTweets, this.webshotDelay).then(updateDate).then(updateOffset);
             })
             })
                 .then(() => {
                 .then(() => {
                 lock.workon++;
                 lock.workon++;

+ 2 - 4
dist/webshot.js

@@ -198,12 +198,10 @@ class Webshot extends CallableInstance {
             promise = promise.then(() => {
             promise = promise.then(() => {
                 logger.info(`working on ${twi.user.screen_name}/${twi.id_str}`);
                 logger.info(`working on ${twi.user.screen_name}/${twi.id_str}`);
             });
             });
-            const originTwi = twi.retweeted_status || twi;
+            const originTwi = twi;
             const messageChain = [];
             const messageChain = [];
             // text processing
             // text processing
-            let author = `${twi.user.name} (@${twi.user.screen_name}):\n`;
-            if (twi.retweeted_status)
-                author += `RT @${twi.retweeted_status.user.screen_name}: `;
+            const author = `${twi.user.name} (@${twi.user.screen_name}):\n`;
             let text = originTwi.full_text;
             let text = originTwi.full_text;
             promise = promise.then(() => {
             promise = promise.then(() => {
                 if (originTwi.entities && originTwi.entities.urls && originTwi.entities.urls.length) {
                 if (originTwi.entities && originTwi.entities.urls && originTwi.entities.urls.length) {

+ 9 - 10
src/command.ts

@@ -44,14 +44,13 @@ function parseLink(link: string): { link: string, match: string[] } | undefined
 
 
 function sub(chat: IChat, args: string[], lock: ILock, lockfile: string): string {
 function sub(chat: IChat, args: string[], lock: ILock, lockfile: string): string {
   if (args.length === 0) {
   if (args.length === 0) {
-    return '找不到要订阅的链接。';
+    return '找不到要订阅媒体推文的链接。';
   }
   }
   const match = parseLink(args[0]);
   const match = parseLink(args[0]);
   if (!match) {
   if (!match) {
     return `订阅链接格式错误:
     return `订阅链接格式错误:
 示例:
 示例:
-https://twitter.com/Saito_Shuka
-https://twitter.com/rikakomoe/lists/lovelive`;
+https://twitter.com/Saito_Shuka`;
   }
   }
   const link = match.link;
   const link = match.link;
   let flag = false;
   let flag = false;
@@ -61,7 +60,7 @@ https://twitter.com/rikakomoe/lists/lovelive`;
   if (!flag) lock.feed.push(link);
   if (!flag) lock.feed.push(link);
   if (!lock.threads[link]) {
   if (!lock.threads[link]) {
     lock.threads[link] = {
     lock.threads[link] = {
-      offset: 0,
+      offset: '0',
       subscribers: [],
       subscribers: [],
       updatedAt: '',
       updatedAt: '',
     };
     };
@@ -73,12 +72,12 @@ https://twitter.com/rikakomoe/lists/lovelive`;
   if (!flag) lock.threads[link].subscribers.push(chat);
   if (!flag) lock.threads[link].subscribers.push(chat);
   logger.warn(`chat ${JSON.stringify(chat)} has subscribed ${link}`);
   logger.warn(`chat ${JSON.stringify(chat)} has subscribed ${link}`);
   fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
   fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
-  return `已为此聊天订阅 ${link}`;
+  return `已为此聊天订阅 ${link} 的媒体推文`;
 }
 }
 
 
 function unsub(chat: IChat, args: string[], lock: ILock, lockfile: string): string {
 function unsub(chat: IChat, args: string[], lock: ILock, lockfile: string): string {
   if (args.length === 0) {
   if (args.length === 0) {
-    return '找不到要退订的链接。';
+    return '找不到要退订媒体推文的链接。';
   }
   }
   const match = parseLink(args[0]);
   const match = parseLink(args[0]);
   if (!match) {
   if (!match) {
@@ -86,7 +85,7 @@ function unsub(chat: IChat, args: string[], lock: ILock, lockfile: string): stri
   }
   }
   const link = match.link;
   const link = match.link;
   if (!lock.threads[link]) {
   if (!lock.threads[link]) {
-    return '您没有订阅此链接。\n' + list(chat, args, lock);
+    return '您没有订阅此链接的媒体推文。\n' + list(chat, args, lock);
   }
   }
   let flag = false;
   let flag = false;
   lock.threads[link].subscribers.forEach((c, index) => {
   lock.threads[link].subscribers.forEach((c, index) => {
@@ -98,9 +97,9 @@ function unsub(chat: IChat, args: string[], lock: ILock, lockfile: string): stri
   if (flag) {
   if (flag) {
     fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
     fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
     logger.warn(`chat ${JSON.stringify(chat)} has unsubscribed ${link}`);
     logger.warn(`chat ${JSON.stringify(chat)} has unsubscribed ${link}`);
-    return `已为此聊天退订 ${link}`;
+    return `已为此聊天退订 ${link} 的媒体推文`;
   }
   }
-  return '您没有订阅此链接。\n' + list(chat, args, lock);
+  return '您没有订阅此链接的媒体推文。\n' + list(chat, args, lock);
 }
 }
 
 
 function list(chat: IChat, args: string[], lock: ILock): string {
 function list(chat: IChat, args: string[], lock: ILock): string {
@@ -110,7 +109,7 @@ function list(chat: IChat, args: string[], lock: ILock): string {
       if (c.chatID === chat.chatID && c.chatType === chat.chatType) links.push(`${key} ${relativeDate(lock.threads[key].updatedAt)}`);
       if (c.chatID === chat.chatID && c.chatType === chat.chatType) links.push(`${key} ${relativeDate(lock.threads[key].updatedAt)}`);
     });
     });
   });
   });
-  return '此聊天中订阅的链接:\n' + links.join('\n');
+  return '此聊天中订阅媒体推文的链接:\n' + links.join('\n');
 }
 }
 
 
 export { sub, list, unsub };
 export { sub, list, unsub };

+ 1 - 1
src/main.ts

@@ -122,7 +122,7 @@ if (fs.existsSync(path.resolve(config.lockfile))) {
 }
 }
 
 
 Object.keys(lock.threads).forEach(key => {
 Object.keys(lock.threads).forEach(key => {
-  lock.threads[key].offset = -1;
+  lock.threads[key].offset = '-1';
 });
 });
 
 
 const qq = new QQBot({
 const qq = new QQBot({

+ 9 - 9
src/mirai.ts

@@ -78,23 +78,23 @@ export default class {
       }
       }
       const cmdObj = command(msg.plain);
       const cmdObj = command(msg.plain);
       switch (cmdObj.cmd) {
       switch (cmdObj.cmd) {
-        case 'twitter_sub':
-        case 'twitter_subscribe':
+        case 'twitterpic_sub':
+        case 'twitterpic_subscribe':
           msg.reply(this.botInfo.sub(chat, cmdObj.args));
           msg.reply(this.botInfo.sub(chat, cmdObj.args));
           break;
           break;
-        case 'twitter_unsub':
-        case 'twitter_unsubscribe':
+        case 'twitterpic_unsub':
+        case 'twitterpic_unsubscribe':
           msg.reply(this.botInfo.unsub(chat, cmdObj.args));
           msg.reply(this.botInfo.unsub(chat, cmdObj.args));
           break;
           break;
         case 'ping':
         case 'ping':
-        case 'twitter':
+        case 'twitterpic':
           msg.reply(this.botInfo.list(chat, cmdObj.args));
           msg.reply(this.botInfo.list(chat, cmdObj.args));
           break;
           break;
         case 'help':
         case 'help':
-          msg.reply(`推特搬运机器人:
-/twitter - 查询当前聊天中的订阅
-/twitter_subscribe [链接] - 订阅 Twitter 搬运
-/twitter_unsubscribe [链接] - 退订 Twitter 搬运`);
+          msg.reply(`推特图片搬运机器人:
+/twitterpic - 查询当前聊天中的订阅
+/twitterpic_subscribe [链接] - 订阅 Twitter 图片搬运
+/twitterpic_unsubscribe [链接] - 退订 Twitter 图片搬运`);
       }
       }
     });
     });
 }
 }

+ 1 - 1
src/model.d.ts

@@ -16,7 +16,7 @@ interface ILock {
   threads: {
   threads: {
     [key: string]:
     [key: string]:
       {
       {
-        offset: number,
+        offset: string,
         updatedAt: string,
         updatedAt: string,
         subscribers: IChat[],
         subscribers: IChat[],
       }
       }

+ 27 - 28
src/twitter.ts

@@ -33,7 +33,6 @@ interface ITweet {
   extended_entities: ExtendedEntities;
   extended_entities: ExtendedEntities;
   full_text: string;
   full_text: string;
   display_text_range: [number, number];
   display_text_range: [number, number];
-  id: number;
   id_str: string;
   id_str: string;
   retweeted_status?: Tweet;
   retweeted_status?: Tweet;
 }
 }
@@ -98,10 +97,11 @@ export default class {
       return;
       return;
     }
     }
 
 
-    logger.debug(`pulling feed ${lock.feed[lock.workon]}`);
+    const currentFeed = lock.feed[lock.workon];
+    logger.debug(`pulling feed ${currentFeed}`);
 
 
     const promise = new Promise(resolve => {
     const promise = new Promise(resolve => {
-      let match = lock.feed[lock.workon].match(/https:\/\/twitter.com\/([^\/]+)\/lists\/([^\/]+)/);
+      let match = currentFeed.match(/https:\/\/twitter.com\/([^\/]+)\/lists\/([^\/]+)/);
       let config: any;
       let config: any;
       let endpoint: string;
       let endpoint: string;
       if (match) {
       if (match) {
@@ -112,7 +112,7 @@ export default class {
         };
         };
         endpoint = 'lists/statuses';
         endpoint = 'lists/statuses';
       } else {
       } else {
-        match = lock.feed[lock.workon].match(/https:\/\/twitter.com\/([^\/]+)/);
+        match = currentFeed.match(/https:\/\/twitter.com\/([^\/]+)/);
         if (match) {
         if (match) {
           config = {
           config = {
             screen_name: match[1],
             screen_name: match[1],
@@ -124,18 +124,18 @@ export default class {
       }
       }
 
 
       if (endpoint) {
       if (endpoint) {
-        const offset = lock.threads[lock.feed[lock.workon]].offset;
+        const offset = lock.threads[currentFeed].offset as unknown as number;
         if (offset > 0) config.since_id = offset;
         if (offset > 0) config.since_id = offset;
         this.client.get(endpoint, config, (error, tweets, response) => {
         this.client.get(endpoint, config, (error, tweets, response) => {
           if (error) {
           if (error) {
             if (error instanceof Array && error.length > 0 && error[0].code === 34) {
             if (error instanceof Array && error.length > 0 && error[0].code === 34) {
-              logger.warn(`error on fetching tweets for ${lock.feed[lock.workon]}: ${JSON.stringify(error)}`);
-              lock.threads[lock.feed[lock.workon]].subscribers.forEach(subscriber => {
-                logger.info(`sending notfound message of ${lock.feed[lock.workon]} to ${JSON.stringify(subscriber)}`);
-                this.bot.sendTo(subscriber, `链接 ${lock.feed[lock.workon]} 指向的用户或列表不存在,请退订。`).catch();
+              logger.warn(`error on fetching tweets for ${currentFeed}: ${JSON.stringify(error)}`);
+              lock.threads[currentFeed].subscribers.forEach(subscriber => {
+                logger.info(`sending notfound message of ${currentFeed} to ${JSON.stringify(subscriber)}`);
+                this.bot.sendTo(subscriber, `链接 ${currentFeed} 指向的用户或列表不存在,请退订。`).catch();
               });
               });
             } else {
             } else {
-              logger.error(`unhandled error on fetching tweets for ${lock.feed[lock.workon]}: ${JSON.stringify(error)}`);
+              logger.error(`unhandled error on fetching tweets for ${currentFeed}: ${JSON.stringify(error)}`);
             }
             }
             resolve();
             resolve();
           } else resolve(tweets);
           } else resolve(tweets);
@@ -143,17 +143,20 @@ export default class {
       }
       }
     });
     });
 
 
-    promise.then((tweets: any) => {
-      logger.debug(`api returned ${JSON.stringify(tweets)} for feed ${lock.feed[lock.workon]}`);
-      if (!tweets || tweets.length === 0) {
-        lock.threads[lock.feed[lock.workon]].updatedAt = new Date().toString();
-        return;
-      }
-      if (lock.threads[lock.feed[lock.workon]].offset === -1) {
-        lock.threads[lock.feed[lock.workon]].offset = tweets[0].id_str;
-        return;
-      }
-      if (lock.threads[lock.feed[lock.workon]].offset === 0) tweets.splice(1);
+    promise.then((tweets: Tweets) => {
+      logger.debug(`api returned ${JSON.stringify(tweets)} for feed ${currentFeed}`);
+      const currentThread = lock.threads[currentFeed];
+
+      const updateDate = () => currentThread.updatedAt = new Date().toString();
+      if (!tweets || tweets.length === 0) { updateDate(); return; }
+
+      const topOfFeed = tweets[0].id_str;
+      const updateOffset = () => currentThread.offset = topOfFeed;
+      tweets = tweets.filter(twi => !twi.retweeted_status && twi.extended_entities);
+      if (tweets.length === 0) { updateDate(); updateOffset(); return; }
+
+      if (currentThread.offset === '-1') { updateOffset(); return; }
+      if (currentThread.offset === '0') tweets.splice(1);
 
 
       const maxCount = 3;
       const maxCount = 3;
       let sendTimeout = 10000;
       let sendTimeout = 10000;
@@ -171,8 +174,8 @@ export default class {
         }
         }
       };
       };
       const sendTweets = (msg: MessageChain, text: string, author: string) => {
       const sendTweets = (msg: MessageChain, text: string, author: string) => {
-        lock.threads[lock.feed[lock.workon]].subscribers.forEach(subscriber => {
-          logger.info(`pushing data of thread ${lock.feed[lock.workon]} to ${JSON.stringify(subscriber)}`);
+        currentThread.subscribers.forEach(subscriber => {
+          logger.info(`pushing data of thread ${currentFeed} to ${JSON.stringify(subscriber)}`);
           const retry = (reason, count: number) => { // workaround for https://github.com/mamoe/mirai/issues/194
           const retry = (reason, count: number) => { // workaround for https://github.com/mamoe/mirai/issues/194
             if (count <= maxCount) sendTimeout *= (count + 2) / (count + 1);
             if (count <= maxCount) sendTimeout *= (count + 2) / (count + 1);
             setTimeout(() => {
             setTimeout(() => {
@@ -193,11 +196,7 @@ export default class {
           this.bot.sendTo(subscriber, msg, sendTimeout).catch(error => retry(error, 1));
           this.bot.sendTo(subscriber, msg, sendTimeout).catch(error => retry(error, 1));
         });
         });
       };
       };
-      return (this.webshot as any)(tweets, sendTweets, this.webshotDelay)
-      .then(() => {
-        lock.threads[lock.feed[lock.workon]].offset = tweets[0].id_str;
-        lock.threads[lock.feed[lock.workon]].updatedAt = new Date().toString();
-      });
+      return this.webshot(tweets, sendTweets, this.webshotDelay).then(updateDate).then(updateOffset);
     })
     })
       .then(() => {
       .then(() => {
         lock.workon++;
         lock.workon++;

+ 3 - 4
src/webshot.ts

@@ -30,7 +30,7 @@ const logger = getLogger('webshot');
 const mkdirP = dir => { if (!existsSync(dir)) mkdirSync(dir, {recursive: true}); };
 const mkdirP = dir => { if (!existsSync(dir)) mkdirSync(dir, {recursive: true}); };
 const baseName = path => path.split(/[/\\]/).slice(-1)[0];
 const baseName = path => path.split(/[/\\]/).slice(-1)[0];
 
 
-class Webshot extends CallableInstance<[number], Promise<void>> {
+class Webshot extends CallableInstance<[Tweets, (...args) => void, number], Promise<void>> {
 
 
   private browser: Browser;
   private browser: Browser;
   private outDir: string;
   private outDir: string;
@@ -218,12 +218,11 @@ class Webshot extends CallableInstance<[number], Promise<void>> {
       promise = promise.then(() => {
       promise = promise.then(() => {
         logger.info(`working on ${twi.user.screen_name}/${twi.id_str}`);
         logger.info(`working on ${twi.user.screen_name}/${twi.id_str}`);
       });
       });
-      const originTwi = twi.retweeted_status || twi;
+      const originTwi = twi;
       const messageChain: MessageChain = [];
       const messageChain: MessageChain = [];
 
 
       // text processing
       // text processing
-      let author = `${twi.user.name} (@${twi.user.screen_name}):\n`;
-      if (twi.retweeted_status) author += `RT @${twi.retweeted_status.user.screen_name}: `;
+      const author = `${twi.user.name} (@${twi.user.screen_name}):\n`;
 
 
       let text = originTwi.full_text;
       let text = originTwi.full_text;