Browse Source

:construction: work on twitter

LI JIAHAO 6 years ago
parent
commit
619417b9f4
5 changed files with 135 additions and 37 deletions
  1. 2 0
      package.json
  2. 18 8
      src/main.ts
  3. 74 28
      src/twitter.ts
  4. 30 0
      src/webshot.ts
  5. 11 1
      yarn.lock

+ 2 - 0
package.json

@@ -7,9 +7,11 @@
     "lint": "tslint --fix -c tslint.json --project ./"
   },
   "dependencies": {
+    "base64-stream": "^0.1.3",
     "command-line-usage": "^5.0.5",
     "cq-websocket": "^1.2.5",
     "log4js": "^2.10.0",
+    "pngjs": "^3.3.3",
     "twitter": "^1.7.1",
     "typescript": "^2.9.2",
     "webshot": "^0.18.0"

+ 18 - 8
src/main.ts

@@ -7,7 +7,7 @@ import * as path from 'path';
 
 import { list, sub, unsub } from './command';
 import QQBot from './cqhttp';
-import work from './twitter';
+import Worker from './twitter';
 
 const logger = log4js.getLogger();
 logger.level = 'info';
@@ -53,6 +53,13 @@ try {
   process.exit(1);
 }
 
+if (config.twitter_consumer_key === undefined ||
+  config.twitter_consumer_secret === undefined ||
+  config.twitter_access_token_key === undefined ||
+  config.twitter_access_token_secret === undefined) {
+  console.log('twitter_consumer_key twitter_consumer_secret twitter_access_token_key twitter_access_token_secret are required');
+  process.exit(1);
+}
 if (config.cq_ws_host === undefined) {
   config.cq_ws_host = '127.0.0.1';
   logger.warn('cq_ws_host is undefined, use 127.0.0.1 as default');
@@ -117,12 +124,15 @@ const qq = new QQBot({
   unsub: (c, a) => unsub(c, a, lock, config.lockfile),
 });
 
-setTimeout(() => {
-  work({
-    lock,
-    lockfile: config.lockfile,
-    workInterval: config.work_interval,
-  });
-}, config.work_interval * 1000);
+const worker = new Worker({
+  consumer_key: config.twitter_consumer_key,
+  consumer_secret: config.twitter_consumer_secret,
+  access_token_key: config.twitter_access_token_key,
+  access_token_secret: config.twitter_access_token_secret,
+  lock,
+  lockfile: config.lockfile,
+  workInterval: config.work_interval,
+});
+setTimeout(worker.work, config.work_interval * 1000);
 
 qq.connect();

+ 74 - 28
src/twitter.ts

@@ -1,45 +1,91 @@
 import * as fs from 'fs';
 import * as log4js from 'log4js';
 import * as path from 'path';
+import * as Twitter from 'twitter';
 
 interface IWorkerOption {
   lock: ILock;
   lockfile: string;
   workInterval: number;
+  consumer_key: string;
+  consumer_secret: string;
+  access_token_key: string;
+  access_token_secret: string;
 }
 
 const logger = log4js.getLogger('twitter');
 logger.level = 'info';
 
-function work(opt: IWorkerOption) {
-  const lock = opt.lock;
-  if (opt.workInterval < 1) opt.workInterval = 1;
-  if (lock.feed.length === 0) {
-    setTimeout(() => {
-      work(opt);
-    }, opt.workInterval * 1000);
-    return;
-  }
-  if (lock.workon >= lock.feed.length) lock.workon = 0;
-  if (!lock.threads[lock.feed[lock.workon]] ||
-    !lock.threads[lock.feed[lock.workon]].subscribers ||
-    lock.threads[lock.feed[lock.workon]].subscribers.length === 0) {
-    logger.error(`nobody subscribes thread ${lock.feed[lock.workon]}, removing from feed`);
-    lock.feed.splice(lock.workon, 1);
-    fs.writeFileSync(path.resolve(opt.lockfile), JSON.stringify(lock));
-    work(opt);
-    return;
+export default class {
+
+  private client;
+  private lock: ILock;
+  private lockfile: string;
+  private workInterval: number;
+
+  constructor(opt: IWorkerOption) {
+    this.client = new Twitter({
+      consumer_key: opt.consumer_key,
+      consumer_secret: opt.consumer_secret,
+      access_token_key: opt.access_token_key,
+      access_token_secret: opt.access_token_secret,
+    });
+    this.lockfile = opt.lockfile;
+    this.lock = opt.lock;
+    this.workInterval = opt.workInterval;
   }
 
-  // TODO: Work on lock.feed[lock.workon]
+  public work = () => {
+    const lock = this.lock;
+    if (this.workInterval < 1) this.workInterval = 1;
+    if (lock.feed.length === 0) {
+      setTimeout(() => {
+        this.work();
+      }, this.workInterval * 1000);
+      return;
+    }
+    if (lock.workon >= lock.feed.length) lock.workon = 0;
+    if (!lock.threads[lock.feed[lock.workon]] ||
+      !lock.threads[lock.feed[lock.workon]].subscribers ||
+      lock.threads[lock.feed[lock.workon]].subscribers.length === 0) {
+      logger.error(`nobody subscribes thread ${lock.feed[lock.workon]}, removing from feed`);
+      lock.feed.splice(lock.workon, 1);
+      fs.writeFileSync(path.resolve(this.lockfile), JSON.stringify(lock));
+      this.work();
+      return;
+    }
 
-  lock.workon++;
-  let timeout = opt.workInterval * 1000 / lock.feed.length;
-  if (timeout < 1000) timeout = 1000;
-  fs.writeFileSync(path.resolve(opt.lockfile), JSON.stringify(lock));
-  setTimeout(() => {
-    work(opt);
-  }, timeout);
-}
+    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\/([^\/]+)/);
+      if (match) {
+        const config: any = {
+          screen_name: match[1],
+        };
+        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);
+        });
+      }
+    }
 
-export default work;
+    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);
+  }
+}

+ 30 - 0
src/webshot.ts

@@ -0,0 +1,30 @@
+import * as log4js from 'log4js';
+import * as webshot from 'webshot';
+import * as base64 from 'base64-stream';
+
+const logger = log4js.getLogger('webshot');
+logger.level = 'info';
+
+export default function (twitter, callback) {
+  twitter.forEach(twi => {
+    const url = `https://mobile.twitter.com/${twi.user.screen_name}/status/${twi.id_str}`;
+    const options = {
+      windowSize: {
+        width: 1080,
+        height: 1920,
+      },
+      shotSize: {
+        width: 'window',
+        height: 'window',
+      },
+      userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
+      renderDelay: 5000,
+      quality: 100,
+      customCSS: 'html{zoom:2}header{display:none!important}',
+    };
+    const renderStream = webshot(url, options).pipe(base64.encode());
+    renderStream.on('data', data => {
+      data = 'base64://' + data;
+    });
+  });
+}

+ 11 - 1
yarn.lock

@@ -117,6 +117,12 @@ balanced-match@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
 
+base64-stream@^0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/base64-stream/-/base64-stream-0.1.3.tgz#76b0370b779bb816d12fd41764a6b8573eb5fec3"
+  dependencies:
+    readable-stream "^2.0.2"
+
 bcrypt-pbkdf@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
@@ -1045,6 +1051,10 @@ pinkie@^2.0.0:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
 
+pngjs@^3.3.3:
+  version "3.3.3"
+  resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.3.3.tgz#85173703bde3edac8998757b96e5821d0966a21b"
+
 prelude-ls@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
@@ -1118,7 +1128,7 @@ readable-stream@1.1.x, "readable-stream@1.x >=1.1.9":
     isarray "0.0.1"
     string_decoder "~0.10.x"
 
-readable-stream@2, readable-stream@^2.2.2, readable-stream@^2.3.0:
+readable-stream@2, readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@^2.3.0:
   version "2.3.6"
   resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
   dependencies: