Jelajahi Sumber

:beers: finished all except translation

LI JIAHAO 6 tahun lalu
induk
melakukan
60b60f4ba6
12 mengubah file dengan 214 tambahan dan 96 penghapusan
  1. 1 0
      config.example.json
  2. 1 1
      dist/command.js
  3. 5 0
      dist/main.js
  4. 53 27
      dist/twitter.js
  5. 42 18
      dist/webshot.js
  6. 0 0
      dist/webshot_test.js
  7. 1 1
      src/command.ts
  8. 5 0
      src/main.ts
  9. 57 25
      src/twitter.ts
  10. 45 23
      src/webshot.ts
  11. 1 1
      src/webshot_test.ts
  12. 3 0
      tslint.json

+ 1 - 0
config.example.json

@@ -7,5 +7,6 @@
   "twitter_access_token_key": "",
   "twitter_access_token_secret": "",
   "work_interval": 60,
+  "webshot_delay": 5000,
   "lockfile": "subscriber.lock"
 }

+ 1 - 1
dist/command.js

@@ -1,8 +1,8 @@
 "use strict";
 Object.defineProperty(exports, "__esModule", { value: true });
 const fs = require("fs");
-const path = require("path");
 const log4js = require("log4js");
+const path = require("path");
 const logger = log4js.getLogger('command');
 logger.level = 'info';
 function sub(chat, args, lock, lockfile) {

+ 5 - 0
dist/main.js

@@ -71,6 +71,9 @@ if (config.lockfile === undefined) {
 if (config.work_interval === undefined) {
     config.work_interval = 60;
 }
+if (config.webshot_delay === undefined) {
+    config.webshot_delay = 5000;
+}
 let lock;
 if (fs.existsSync(path.resolve(config.lockfile))) {
     try {
@@ -124,6 +127,8 @@ const worker = new twitter_1.default({
     lock,
     lockfile: config.lockfile,
     workInterval: config.work_interval,
+    bot: qq,
+    webshotDelay: config.webshot_delay,
 });
 setTimeout(worker.work, config.work_interval * 1000);
 qq.connect();

+ 53 - 27
dist/twitter.js

@@ -4,6 +4,7 @@ const fs = require("fs");
 const log4js = require("log4js");
 const path = require("path");
 const Twitter = require("twitter");
+const webshot_1 = require("./webshot");
 const logger = log4js.getLogger('twitter');
 logger.level = 'info';
 class default_1 {
@@ -29,41 +30,64 @@ class default_1 {
                 this.work();
                 return;
             }
-            let match = lock.feed[lock.workon].match(/https:\/\/twitter.com\/([^\/]+)\/lists\/([^\/]+)/);
-            if (match) {
-                const config = {
-                    owner_screen_name: match[1],
-                    slug: match[2],
-                };
-                const offset = lock.threads[lock.feed[lock.workon]].offset;
-                if (offset > 0)
-                    config.since_id = offset;
-                this.client.get('lists/statuses', config, (error, tweets, response) => {
-                    console.log(tweets);
-                });
-            }
-            else {
-                match = lock.feed[lock.workon].match(/https:\/\/twitter.com\/([^\/]+)/);
+            const promise = new Promise(resolve => {
+                let match = lock.feed[lock.workon].match(/https:\/\/twitter.com\/([^\/]+)\/lists\/([^\/]+)/);
                 if (match) {
                     const config = {
-                        screen_name: match[1],
+                        owner_screen_name: match[1],
+                        slug: match[2],
                     };
                     const offset = lock.threads[lock.feed[lock.workon]].offset;
                     if (offset > 0)
                         config.since_id = offset;
-                    this.client.get('statuses/user_timeline', config, (error, tweets, response) => {
-                        console.log(tweets);
+                    this.client.get('lists/statuses', config, (error, tweets, response) => {
+                        resolve(tweets);
                     });
                 }
-            }
-            lock.workon++;
-            let timeout = this.workInterval * 1000 / lock.feed.length;
-            if (timeout < 1000)
-                timeout = 1000;
-            fs.writeFileSync(path.resolve(this.lockfile), JSON.stringify(lock));
-            setTimeout(() => {
-                this.work();
-            }, timeout);
+                else {
+                    match = lock.feed[lock.workon].match(/https:\/\/twitter.com\/([^\/]+)/);
+                    if (match) {
+                        const config = {
+                            screen_name: match[1],
+                            exclude_replies: false,
+                        };
+                        const offset = lock.threads[lock.feed[lock.workon]].offset;
+                        if (offset > 0)
+                            config.since_id = offset;
+                        this.client.get('statuses/user_timeline', config, (error, tweets, response) => {
+                            resolve(tweets);
+                        });
+                    }
+                }
+            });
+            promise.then((tweets) => {
+                if (tweets.length === 0)
+                    return;
+                if (lock.threads[lock.feed[lock.workon]].offset !== -1) {
+                    webshot_1.default(tweets, msg => {
+                        lock.threads[lock.feed[lock.workon]].subscribers.forEach(subscriber => {
+                            this.bot.bot('send_msg', {
+                                message_type: subscriber.chatType,
+                                user_id: subscriber.chatID,
+                                group_id: subscriber.chatID,
+                                discuss_id: subscriber.chatID,
+                                message: msg,
+                            });
+                        });
+                    }, this.webshotDelay);
+                }
+                lock.threads[lock.feed[lock.workon]].offset = tweets[0].id;
+            })
+                .then(() => {
+                lock.workon++;
+                let timeout = this.workInterval * 1000 / lock.feed.length;
+                if (timeout < 1000)
+                    timeout = 1000;
+                fs.writeFileSync(path.resolve(this.lockfile), JSON.stringify(lock));
+                setTimeout(() => {
+                    this.work();
+                }, timeout);
+            });
         };
         this.client = new Twitter({
             consumer_key: opt.consumer_key,
@@ -74,6 +98,8 @@ class default_1 {
         this.lockfile = opt.lockfile;
         this.lock = opt.lock;
         this.workInterval = opt.workInterval;
+        this.bot = opt.bot;
+        this.webshotDelay = opt.webshotDelay;
     }
 }
 exports.default = default_1;

+ 42 - 18
dist/webshot.js

@@ -1,32 +1,33 @@
 "use strict";
 Object.defineProperty(exports, "__esModule", { value: true });
+const http = require("http");
 const log4js = require("log4js");
-const webshot = require("webshot");
-const read = require("read-all-stream");
 const pngjs_1 = require("pngjs");
+const read = require("read-all-stream");
+const webshot = require("webshot");
 const logger = log4js.getLogger('webshot');
 logger.level = 'info';
-function renderWebshot(url, height) {
-    let promise = new Promise(resolve => {
+function renderWebshot(url, height, webshotDelay) {
+    const promise = new Promise(resolve => {
         const options = {
             windowSize: {
                 width: 1080,
-                height: height,
+                height,
             },
             userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
-            renderDelay: 5000,
+            renderDelay: webshotDelay,
             quality: 100,
             customCSS: 'html{zoom:2}header{display:none!important}',
         };
         logger.info(`shooting ${options.windowSize.width}*${height} webshot for ${url}`);
         webshot(url, options).pipe(new pngjs_1.PNG({
-            filterType: 4
+            filterType: 4,
         }))
             .on('parsed', function () {
             let boundary = null;
             for (let y = 0; y < this.height; y++) {
                 const x = 0;
-                let idx = (this.width * y + x) << 2;
+                const idx = (this.width * y + x) << 2;
                 if (this.data[idx] !== 255) {
                     boundary = y;
                     break;
@@ -42,35 +43,58 @@ function renderWebshot(url, height) {
                 });
             }
             else if (height >= 8 * 1920) {
-                logger.warn(`too large, consider as a bug, returning`);
+                logger.warn('too large, consider as a bug, returning');
                 read(this.pack(), 'base64').then(data => {
                     logger.info(`finished webshot for ${url}`);
                     resolve({ data, boundary: 0 });
                 });
             }
             else {
-                logger.info(`unable to found boundary, try shooting a larger image`);
+                logger.info('unable to found boundary, try shooting a larger image');
                 resolve({ data: '', boundary });
             }
         });
     });
     return promise.then(data => {
         if (data.boundary === null)
-            return renderWebshot(url, height + 1920);
+            return renderWebshot(url, height + 1920, webshotDelay);
         else
             return data.data;
     });
 }
-/*function fetchImage(): Promise<string> {
-
-}*/
-function default_1(twitter, callback) {
-    twitter.forEach(twi => {
+function fetchImage(url) {
+    return new Promise(resolve => {
+        logger.info(`fetching ${url}`);
+        http.get(url, res => {
+            if (res.statusCode === 200) {
+                read(res, 'base64').then(data => {
+                    logger.info(`successfully fetched ${url}`);
+                    return data;
+                });
+            }
+        });
+    });
+}
+function default_1(tweets, callback, webshotDelay) {
+    tweets.forEach(twi => {
+        let cqstr = '';
         const url = `https://mobile.twitter.com/${twi.user.screen_name}/status/${twi.id_str}`;
-        renderWebshot(url, 1920)
+        let promise = renderWebshot(url, 1920, webshotDelay)
             .then(base64Webshot => {
-            console.log(base64Webshot);
+            if (base64Webshot)
+                cqstr += `[CQ:image,file=base64://${base64Webshot}]`;
         });
+        if (twi.extended_entities) {
+            twi.extended_entities.media.forEach(media => {
+                promise = promise.then(() => fetchImage(media.media_url_https))
+                    .then(base64Image => {
+                    if (base64Image)
+                        cqstr += `[CQ:image,file=base64://${base64Image}]`;
+                });
+            });
+        }
+        // TODO: Translate
+        promise.then(() => callback(cqstr));
     });
 }
 exports.default = default_1;

File diff ditekan karena terlalu besar
+ 0 - 0
dist/webshot_test.js


+ 1 - 1
src/command.ts

@@ -1,6 +1,6 @@
 import * as fs from 'fs';
+import * as log4js from 'log4js';
 import * as path from 'path';
-import * as log4js from "log4js";
 
 const logger = log4js.getLogger('command');
 logger.level = 'info';

+ 5 - 0
src/main.ts

@@ -78,6 +78,9 @@ if (config.lockfile === undefined) {
 if (config.work_interval === undefined) {
   config.work_interval = 60;
 }
+if (config.webshot_delay === undefined) {
+  config.webshot_delay = 5000;
+}
 
 let lock: ILock;
 if (fs.existsSync(path.resolve(config.lockfile))) {
@@ -132,6 +135,8 @@ const worker = new Worker({
   lock,
   lockfile: config.lockfile,
   workInterval: config.work_interval,
+  bot: qq,
+  webshotDelay: config.webshot_delay,
 });
 setTimeout(worker.work, config.work_interval * 1000);
 

+ 57 - 25
src/twitter.ts

@@ -3,10 +3,15 @@ import * as log4js from 'log4js';
 import * as path from 'path';
 import * as Twitter from 'twitter';
 
+import QQBot from './cqhttp';
+import webshot from './webshot';
+
 interface IWorkerOption {
   lock: ILock;
   lockfile: string;
+  bot: QQBot;
   workInterval: number;
+  webshotDelay: number;
   consumer_key: string;
   consumer_secret: string;
   access_token_key: string;
@@ -22,6 +27,8 @@ export default class {
   private lock: ILock;
   private lockfile: string;
   private workInterval: number;
+  private bot: QQBot;
+  private webshotDelay: number;
 
   constructor(opt: IWorkerOption) {
     this.client = new Twitter({
@@ -33,6 +40,8 @@ export default class {
     this.lockfile = opt.lockfile;
     this.lock = opt.lock;
     this.workInterval = opt.workInterval;
+    this.bot = opt.bot;
+    this.webshotDelay = opt.webshotDelay;
   }
 
   public work = () => {
@@ -55,37 +64,60 @@ export default class {
       return;
     }
 
-    let match = lock.feed[lock.workon].match(/https:\/\/twitter.com\/([^\/]+)\/lists\/([^\/]+)/);
-    if (match) {
-      const config: any = {
-        owner_screen_name: match[1],
-        slug: match[2],
-      };
-      const offset = lock.threads[lock.feed[lock.workon]].offset;
-      if (offset > 0) config.since_id =  offset;
-      this.client.get('lists/statuses', config, (error, tweets, response) => {
-        console.log(tweets);
-      });
-    } else {
-      match = lock.feed[lock.workon].match(/https:\/\/twitter.com\/([^\/]+)/);
+    const promise = new Promise(resolve => {
+      let match = lock.feed[lock.workon].match(/https:\/\/twitter.com\/([^\/]+)\/lists\/([^\/]+)/);
       if (match) {
         const config: any = {
-          screen_name: match[1],
+          owner_screen_name: match[1],
+          slug: match[2],
         };
         const offset = lock.threads[lock.feed[lock.workon]].offset;
-        if (offset > 0) config.since_id =  offset;
-        this.client.get('statuses/user_timeline', config, (error, tweets, response) => {
-          console.log(tweets);
+        if (offset > 0) config.since_id = offset;
+        this.client.get('lists/statuses', config, (error, tweets, response) => {
+          resolve(tweets);
         });
+      } else {
+        match = lock.feed[lock.workon].match(/https:\/\/twitter.com\/([^\/]+)/);
+        if (match) {
+          const config: any = {
+            screen_name: match[1],
+            exclude_replies: false,
+          };
+          const offset = lock.threads[lock.feed[lock.workon]].offset;
+          if (offset > 0) config.since_id = offset;
+          this.client.get('statuses/user_timeline', config, (error, tweets, response) => {
+            resolve(tweets);
+          });
+        }
       }
-    }
+    });
+
+    promise.then((tweets: any) => {
+      if (tweets.length === 0) return;
+      if (lock.threads[lock.feed[lock.workon]].offset !== -1) {
+        webshot(tweets, msg => {
+          lock.threads[lock.feed[lock.workon]].subscribers.forEach(subscriber => {
+            this.bot.bot('send_msg', {
+              message_type: subscriber.chatType,
+              user_id: subscriber.chatID,
+              group_id: subscriber.chatID,
+              discuss_id: subscriber.chatID,
+              message: msg,
+            });
+          });
+        }, this.webshotDelay);
+      }
+      lock.threads[lock.feed[lock.workon]].offset = tweets[0].id;
+    })
+      .then(() => {
+        lock.workon++;
+        let timeout = this.workInterval * 1000 / lock.feed.length;
+        if (timeout < 1000) timeout = 1000;
+        fs.writeFileSync(path.resolve(this.lockfile), JSON.stringify(lock));
+        setTimeout(() => {
+          this.work();
+        }, timeout);
+      });
 
-    lock.workon++;
-    let timeout = this.workInterval * 1000 / lock.feed.length;
-    if (timeout < 1000) timeout = 1000;
-    fs.writeFileSync(path.resolve(this.lockfile), JSON.stringify(lock));
-    setTimeout(() => {
-      this.work();
-    }, timeout);
   }
 }

+ 45 - 23
src/webshot.ts

@@ -1,32 +1,33 @@
+import * as http from 'http';
 import * as log4js from 'log4js';
-import * as webshot from 'webshot';
+import { PNG } from 'pngjs';
 import * as read from 'read-all-stream';
-import {PNG} from 'pngjs';
+import * as webshot from 'webshot';
 
 const logger = log4js.getLogger('webshot');
 logger.level = 'info';
 
-function renderWebshot(url: string, height: number): Promise<string> {
-  let promise = new Promise<{ data: string, boundary: null | number }>(resolve => {
+function renderWebshot(url: string, height: number, webshotDelay: number): Promise<string> {
+  const promise = new Promise<{ data: string, boundary: null | number }>(resolve => {
     const options = {
       windowSize: {
         width: 1080,
-        height: height,
+        height,
       },
       userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
-      renderDelay: 5000,
+      renderDelay: webshotDelay,
       quality: 100,
       customCSS: 'html{zoom:2}header{display:none!important}',
     };
     logger.info(`shooting ${options.windowSize.width}*${height} webshot for ${url}`);
     webshot(url, options).pipe(new PNG({
-      filterType: 4
+      filterType: 4,
     }))
       .on('parsed', function () {
         let boundary = null;
         for (let y = 0; y < this.height; y++) {
           const x = 0;
-          let idx = (this.width * y + x) << 2;
+          const idx = (this.width * y + x) << 2;
           if (this.data[idx] !== 255) {
             boundary = y;
             break;
@@ -38,36 +39,57 @@ function renderWebshot(url: string, height: number): Promise<string> {
           this.height = boundary;
           read(this.pack(), 'base64').then(data => {
             logger.info(`finished webshot for ${url}`);
-            resolve({ data, boundary });
+            resolve({data, boundary});
           });
         } else if (height >= 8 * 1920) {
-          logger.warn(`too large, consider as a bug, returning`);
+          logger.warn('too large, consider as a bug, returning');
           read(this.pack(), 'base64').then(data => {
             logger.info(`finished webshot for ${url}`);
-            resolve({ data, boundary: 0 });
+            resolve({data, boundary: 0});
           });
         } else {
-          logger.info(`unable to found boundary, try shooting a larger image`);
-          resolve({ data: '', boundary });
+          logger.info('unable to found boundary, try shooting a larger image');
+          resolve({data: '', boundary});
         }
       });
   });
   return promise.then(data => {
-    if (data.boundary === null) return renderWebshot(url, height + 1920);
+    if (data.boundary === null) return renderWebshot(url, height + 1920, webshotDelay);
     else return data.data;
-  })
+  });
 }
 
-/*function fetchImage(): Promise<string> {
-
-}*/
+function fetchImage(url: string): Promise<string> {
+  return new Promise<string>(resolve => {
+    logger.info(`fetching ${url}`);
+    http.get(url, res => {
+      if (res.statusCode === 200) {
+        read(res, 'base64').then(data => {
+          logger.info(`successfully fetched ${url}`);
+          return data;
+        });
+      }
+    });
+  });
+}
 
-export default function (twitter, callback) {
-  twitter.forEach(twi => {
+export default function (tweets, callback, webshotDelay: number) {
+  tweets.forEach(twi => {
+    let cqstr = '';
     const url = `https://mobile.twitter.com/${twi.user.screen_name}/status/${twi.id_str}`;
-    renderWebshot(url, 1920)
+    let promise = renderWebshot(url, 1920, webshotDelay)
       .then(base64Webshot => {
-        console.log(base64Webshot);
-      })
+        if (base64Webshot) cqstr += `[CQ:image,file=base64://${base64Webshot}]`;
+      });
+    if (twi.extended_entities) {
+      twi.extended_entities.media.forEach(media => {
+        promise = promise.then(() => fetchImage(media.media_url_https))
+          .then(base64Image => {
+            if (base64Image) cqstr += `[CQ:image,file=base64://${base64Image}]`;
+          });
+      });
+    }
+    // TODO: Translate
+    promise.then(() => callback(cqstr));
   });
 }

File diff ditekan karena terlalu besar
+ 1 - 1
src/webshot_test.ts


+ 3 - 0
tslint.json

@@ -24,6 +24,7 @@
     "comment-format": [true, "check-lowercase", {"ignore-words": ["TODO", "HACK", "BUG"]}],
     "no-require-imports": true,
     "no-var-requires": false,
+    "no-bitwise": false,
     "prefer-const": true,
     "prefer-method-signature": true,
     "trailing-comma": [
@@ -97,6 +98,8 @@
     "linebreak-style": [true, "LF"],
     "comment-format": [true, "check-lowercase", {"ignore-words": ["TODO", "HACK", "BUG"]}],
     "no-require-imports": true,
+    "no-var-requires": false,
+    "no-bitwise": false,
     "prefer-const": true,
     "trailing-comma": [
       true,

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini