Pārlūkot izejas kodu

:bug: write lockfile

LI JIAHAO 6 gadi atpakaļ
vecāks
revīzija
731dec0834
9 mainītis faili ar 160 papildinājumiem un 116 dzēšanām
  1. 2 1
      .gitignore
  2. 6 2
      dist/command.js
  3. 20 13
      dist/main.js
  4. 58 49
      dist/qq.js
  5. 8 4
      dist/twitter.js
  6. 7 2
      src/command.ts
  7. 19 14
      src/main.ts
  8. 32 27
      src/qq.ts
  9. 8 4
      src/twitter.ts

+ 2 - 1
.gitignore

@@ -1,3 +1,4 @@
 yarn-error.log
 node_modules
-.idea
+.idea
+*.lock

+ 6 - 2
dist/command.js

@@ -1,6 +1,8 @@
 "use strict";
 Object.defineProperty(exports, "__esModule", { value: true });
-function sub(chat, args, lock) {
+const fs = require("fs");
+const path = require("path");
+function sub(chat, args, lock, lockfile) {
     if (args.length === 0) {
         return '找不到要订阅的链接。';
     }
@@ -40,10 +42,11 @@ https://twitter.com/rikakomoe/lists/lovelive`;
     });
     if (!flag)
         lock.threads[link].subscribers.push(chat);
+    fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
     return `已为此聊天订阅 ${link}`;
 }
 exports.sub = sub;
-function unsub(chat, args, lock) {
+function unsub(chat, args, lock, lockfile) {
     if (args.length === 0) {
         return '找不到要退订的链接。';
     }
@@ -59,6 +62,7 @@ function unsub(chat, args, lock) {
         }
     });
     if (flag) {
+        fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
         return `已为此聊天退订 ${link}`;
     }
     return '您没有订阅此链接。\n' + list(chat, args, lock);

+ 20 - 13
dist/main.js

@@ -61,25 +61,25 @@ if (config.cq_access_token === undefined) {
 if (config.lockfile === undefined) {
     config.lockfile = 'subscriber.lock';
 }
-fs.access(path.resolve(config.lockfile), fs.constants.W_OK, err => {
-    if (err) {
-        logger.fatal(`cannot write lockfile ${path.resolve(config.lockfile)}, permission denied`);
-        process.exit(1);
-    }
-});
 let lock;
 if (fs.existsSync(path.resolve(config.lockfile))) {
     try {
-        lock = require(path.resolve(config.lockfile));
+        lock = JSON.parse(fs.readFileSync(path.resolve(config.lockfile), 'utf8'));
     }
-    catch (e) {
-        logger.error('Failed to parse lockfile: ', config.lockfile);
+    catch (err) {
+        logger.error(`Failed to parse lockfile ${config.lockfile}: `, err);
         lock = {
             workon: 0,
             feed: [],
             threads: {},
         };
     }
+    fs.access(path.resolve(config.lockfile), fs.constants.W_OK, err => {
+        if (err) {
+            logger.fatal(`cannot write lockfile ${path.resolve(config.lockfile)}, permission denied`);
+            process.exit(1);
+        }
+    });
 }
 else {
     lock = {
@@ -87,16 +87,23 @@ else {
         feed: [],
         threads: {},
     };
+    try {
+        fs.writeFileSync(path.resolve(config.lockfile), JSON.stringify(lock));
+    }
+    catch (err) {
+        logger.fatal(`cannot write lockfile ${path.resolve(config.lockfile)}, permission denied`);
+        process.exit(1);
+    }
 }
 const qq = new qq_1.default({
     access_token: config.cq_access_token,
     host: config.cq_ws_host,
     port: config.cq_ws_port,
     list: (c, a) => command_1.list(c, a, lock),
-    sub: (c, a) => command_1.sub(c, a, lock),
-    unsub: (c, a) => command_1.unsub(c, a, lock),
+    sub: (c, a) => command_1.sub(c, a, lock, config.lockfile),
+    unsub: (c, a) => command_1.unsub(c, a, lock, config.lockfile),
 });
 setTimeout(() => {
-    twitter_1.default(lock);
+    twitter_1.default(lock, config.lockfile);
 }, 60000);
-qq.bot.connect();
+qq.connect();

+ 58 - 49
dist/qq.js

@@ -8,9 +8,64 @@ logger.level = 'info';
 class default_1 {
     constructor(opt) {
         this.retryInterval = 1000;
+        this.initWebsocket = () => {
+            this.bot = new CQWebsocket(this.botInfo);
+            this.bot.on('socket.connect', () => {
+                logger.info('websocket connected');
+                this.retryInterval = 1000;
+            });
+            this.bot.on('socket.close', () => {
+                logger.error('websocket closed');
+                this.reconnect();
+            });
+            this.bot.on('socket.error', () => {
+                logger.error('websocket connect error');
+                this.reconnect();
+            });
+            this.bot.on('message', (e, context) => {
+                e.cancel();
+                const chat = {
+                    chatType: context.message_type,
+                    chatID: 0,
+                };
+                switch (context.message_type) {
+                    case ChatType.Private:
+                        chat.chatID = context.user_id;
+                        break;
+                    case ChatType.Group:
+                        chat.chatID = context.group_id;
+                        break;
+                    case ChatType.Discuss:
+                        chat.chatID = context.discuss_id;
+                }
+                const cmdObj = helper_1.default(context.raw_message);
+                switch (cmdObj.cmd) {
+                    case 'twitter_sub':
+                    case 'twitter_subscribe':
+                        return this.botInfo.sub(chat, cmdObj.args);
+                    case 'twitter_unsub':
+                    case 'twitter_unsubscribe':
+                        return this.botInfo.unsub(chat, cmdObj.args);
+                    case 'ping':
+                    case 'twitter':
+                        return this.botInfo.list(chat, cmdObj.args);
+                    case 'help':
+                        return `推特搬运机器人:
+/twitter - 查询当前聊天中的订阅
+/twitter_subscribe [链接] - 订阅 Twitter 搬运
+/twitter_unsubscribe [链接] - 退订 Twitter 搬运`;
+                }
+            });
+        };
         this.connect = () => {
+            this.initWebsocket();
             logger.warn('connecting to websocket...');
-            this.bot.connect();
+            try {
+                this.bot.connect();
+            }
+            catch (err) {
+                this.reconnect();
+            }
         };
         this.reconnect = () => {
             this.retryInterval *= 2;
@@ -23,59 +78,13 @@ class default_1 {
             }, this.retryInterval);
         };
         logger.info(`init cqwebsocket for ${opt.host}:${opt.port}, with access_token ${opt.access_token}`);
-        this.bot = new CQWebsocket({
+        this.botInfo = {
             access_token: opt.access_token,
             enableAPI: true,
             enableEvent: true,
             host: opt.host,
             port: opt.port,
-        });
-        this.bot.on('socket.connect', () => {
-            logger.info('websocket connected');
-            this.retryInterval = 1000;
-        });
-        this.bot.on('socket.close', () => {
-            logger.error('websocket closed');
-            this.reconnect();
-        });
-        this.bot.on('socket.error', () => {
-            logger.error('websocket connect error');
-            this.reconnect();
-        });
-        this.bot.on('message', (e, context) => {
-            e.cancel();
-            const chat = {
-                chatType: context.message_type,
-                chatID: 0,
-            };
-            switch (context.message_type) {
-                case ChatType.Private:
-                    chat.chatID = context.user_id;
-                    break;
-                case ChatType.Group:
-                    chat.chatID = context.group_id;
-                    break;
-                case ChatType.Discuss:
-                    chat.chatID = context.discuss_id;
-            }
-            const cmdObj = helper_1.default(context.raw_message);
-            switch (cmdObj.cmd) {
-                case 'twitter_sub':
-                case 'twitter_subscribe':
-                    return opt.sub(chat, cmdObj.args);
-                case 'twitter_unsub':
-                case 'twitter_unsubscribe':
-                    return opt.unsub(chat, cmdObj.args);
-                case 'ping':
-                case 'twitter':
-                    return opt.list(chat, cmdObj.args);
-                case 'help':
-                    return `推特搬运机器人:
-/twitter - 查询当前聊天中的订阅
-/twitter_subscribe [链接] - 订阅 Twitter 搬运
-/twitter_unsubscribe [链接] - 退订 Twitter 搬运`;
-            }
-        });
+        };
     }
 }
 exports.default = default_1;

+ 8 - 4
dist/twitter.js

@@ -1,12 +1,14 @@
 "use strict";
 Object.defineProperty(exports, "__esModule", { value: true });
+const fs = require("fs");
 const log4js = require("log4js");
+const path = require("path");
 const logger = log4js.getLogger('twitter');
 logger.level = 'info';
-function work(lock) {
+function work(lock, lockfile) {
     if (lock.feed.length === 0) {
         setTimeout(() => {
-            work(lock);
+            work(lock, lockfile);
         }, 60000);
         return;
     }
@@ -17,13 +19,15 @@ function work(lock) {
         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);
-        work(lock);
+        fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
+        work(lock, lockfile);
         return;
     }
     // TODO: Work on lock.feed[lock.workon]
     lock.workon++;
+    fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
     setTimeout(() => {
-        work(lock);
+        work(lock, lockfile);
     }, 60000);
 }
 exports.default = work;

+ 7 - 2
src/command.ts

@@ -1,4 +1,7 @@
-function sub(chat: IChat, args: string[], lock: ILock): string {
+import * as fs from 'fs';
+import * as path from 'path';
+
+function sub(chat: IChat, args: string[], lock: ILock, lockfile: string): string {
   if (args.length === 0) {
     return '找不到要订阅的链接。';
   }
@@ -32,10 +35,11 @@ https://twitter.com/rikakomoe/lists/lovelive`;
     if (c.chatID === chat.chatID && c.chatType === chat.chatType) flag = true;
   });
   if (!flag) lock.threads[link].subscribers.push(chat);
+  fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
   return `已为此聊天订阅 ${link}`;
 }
 
-function unsub(chat: IChat, args: string[], lock: ILock): string {
+function unsub(chat: IChat, args: string[], lock: ILock, lockfile: string): string {
   if (args.length === 0) {
     return '找不到要退订的链接。';
   }
@@ -51,6 +55,7 @@ function unsub(chat: IChat, args: string[], lock: ILock): string {
     }
   });
   if (flag) {
+    fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
     return `已为此聊天退订 ${link}`;
   }
   return '您没有订阅此链接。\n' + list(chat, args, lock);

+ 19 - 14
src/main.ts

@@ -69,31 +69,36 @@ if (config.lockfile === undefined) {
   config.lockfile = 'subscriber.lock';
 }
 
-fs.access(path.resolve(config.lockfile), fs.constants.W_OK, err => {
-  if (err) {
-    logger.fatal(`cannot write lockfile ${path.resolve(config.lockfile)}, permission denied`);
-    process.exit(1);
-  }
-});
-
 let lock: ILock;
 if (fs.existsSync(path.resolve(config.lockfile))) {
   try {
-    lock = require(path.resolve(config.lockfile));
-  } catch (e) {
-    logger.error('Failed to parse lockfile: ', config.lockfile);
+    lock = JSON.parse(fs.readFileSync(path.resolve(config.lockfile), 'utf8'));
+  } catch (err) {
+    logger.error(`Failed to parse lockfile ${config.lockfile}: `, err);
     lock = {
       workon: 0,
       feed: [],
       threads: {},
     };
   }
+  fs.access(path.resolve(config.lockfile), fs.constants.W_OK, err => {
+    if (err) {
+      logger.fatal(`cannot write lockfile ${path.resolve(config.lockfile)}, permission denied`);
+      process.exit(1);
+    }
+  });
 } else {
   lock = {
     workon: 0,
     feed: [],
     threads: {},
   };
+  try {
+    fs.writeFileSync(path.resolve(config.lockfile), JSON.stringify(lock));
+  } catch (err) {
+    logger.fatal(`cannot write lockfile ${path.resolve(config.lockfile)}, permission denied`);
+    process.exit(1);
+  }
 }
 
 const qq = new QQBot({
@@ -101,12 +106,12 @@ const qq = new QQBot({
   host: config.cq_ws_host,
   port: config.cq_ws_port,
   list: (c, a) => list(c, a, lock),
-  sub: (c, a) => sub(c, a, lock),
-  unsub: (c, a) => unsub(c, a, lock),
+  sub: (c, a) => sub(c, a, lock, config.lockfile),
+  unsub: (c, a) => unsub(c, a, lock, config.lockfile),
 });
 
 setTimeout(() => {
-  work(lock);
+  work(lock, config.lockfile);
 }, 60000);
 
-qq.bot.connect();
+qq.connect();

+ 32 - 27
src/qq.ts

@@ -17,33 +17,12 @@ interface IQQProps {
 
 export default class {
 
+  private botInfo;
   public bot: CQWebsocket;
   private retryInterval = 1000;
-  private connect = () => {
-    logger.warn('connecting to websocket...');
-    this.bot.connect();
-  }
-
-  private reconnect = () => {
-    this.retryInterval *= 2;
-    if (this.retryInterval > 300000) this.retryInterval = 300000;
-    logger.info(`retrying in ${this.retryInterval / 1000}s...`);
-    setTimeout(() => {
-      logger.warn('reconnecting to websocket...');
-      this.connect();
-    }, this.retryInterval);
-  }
-
-  constructor(opt: IQQProps) {
-    logger.info(`init cqwebsocket for ${opt.host}:${opt.port}, with access_token ${opt.access_token}`);
 
-    this.bot = new CQWebsocket({
-      access_token: opt.access_token,
-      enableAPI: true,
-      enableEvent: true,
-      host: opt.host,
-      port: opt.port,
-    });
+  private initWebsocket = () => {
+    this.bot = new CQWebsocket(this.botInfo);
 
     this.bot.on('socket.connect', () => {
       logger.info('websocket connected');
@@ -80,13 +59,13 @@ export default class {
       switch (cmdObj.cmd) {
         case 'twitter_sub':
         case 'twitter_subscribe':
-          return opt.sub(chat, cmdObj.args);
+          return this.botInfo.sub(chat, cmdObj.args);
         case 'twitter_unsub':
         case 'twitter_unsubscribe':
-          return opt.unsub(chat, cmdObj.args);
+          return this.botInfo.unsub(chat, cmdObj.args);
         case 'ping':
         case 'twitter':
-          return opt.list(chat, cmdObj.args);
+          return this.botInfo.list(chat, cmdObj.args);
         case 'help':
           return `推特搬运机器人:
 /twitter - 查询当前聊天中的订阅
@@ -94,6 +73,32 @@ export default class {
 /twitter_unsubscribe [链接] - 退订 Twitter 搬运`;
       }
     });
+}
+
+  public connect = () => {
+    this.initWebsocket();
+    logger.warn('connecting to websocket...');
+    this.bot.connect();
+  }
+
+  private reconnect = () => {
+    this.retryInterval *= 2;
+    if (this.retryInterval > 300000) this.retryInterval = 300000;
+    logger.info(`retrying in ${this.retryInterval / 1000}s...`);
+    setTimeout(() => {
+      logger.warn('reconnecting to websocket...');
+      this.connect();
+    }, this.retryInterval);
+  }
 
+  constructor(opt: IQQProps) {
+    logger.info(`init cqwebsocket for ${opt.host}:${opt.port}, with access_token ${opt.access_token}`);
+    this.botInfo = {
+      access_token: opt.access_token,
+      enableAPI: true,
+      enableEvent: true,
+      host: opt.host,
+      port: opt.port,
+    };
   }
 }

+ 8 - 4
src/twitter.ts

@@ -1,12 +1,14 @@
+import * as fs from 'fs';
 import * as log4js from 'log4js';
+import * as path from 'path';
 
 const logger = log4js.getLogger('twitter');
 logger.level = 'info';
 
-function work(lock: ILock) {
+function work(lock: ILock, lockfile: string) {
   if (lock.feed.length === 0) {
     setTimeout(() => {
-      work(lock);
+      work(lock, lockfile);
     }, 60000);
     return;
   }
@@ -16,15 +18,17 @@ function work(lock: ILock) {
     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);
-    work(lock);
+    fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
+    work(lock, lockfile);
     return;
   }
 
   // TODO: Work on lock.feed[lock.workon]
 
   lock.workon++;
+  fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
   setTimeout(() => {
-    work(lock);
+    work(lock, lockfile);
   }, 60000);
 }