main.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. #!/usr/bin/env node
  2. "use strict";
  3. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  4. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  5. return new (P || (P = Promise))(function (resolve, reject) {
  6. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  7. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  8. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  9. step((generator = generator.apply(thisArg, _arguments || [])).next());
  10. });
  11. };
  12. Object.defineProperty(exports, "__esModule", { value: true });
  13. const fs = require("fs");
  14. const path = require("path");
  15. const commandLineUsage = require("command-line-usage");
  16. const exampleConfig = require("../config.example.json");
  17. const command_1 = require("./command");
  18. const json = require("./json");
  19. const loggers_1 = require("./loggers");
  20. const koishi_1 = require("./koishi");
  21. const twitter_1 = require("./twitter");
  22. const logger = (0, loggers_1.getLogger)();
  23. const sections = [
  24. {
  25. header: 'GoCQHTTP Instagram Stories Bot',
  26. content: 'The QQ Bot that forwards Instagram stories.',
  27. },
  28. {
  29. header: 'Synopsis',
  30. content: [
  31. '$ cq-igstory-bot {underline config.json}',
  32. '$ cq-igstory-bot {bold --help}',
  33. ],
  34. },
  35. {
  36. header: 'Documentation',
  37. content: [
  38. 'Project home: {underline https://github.com/CL-Jeremy/mirai-twitter-bot}',
  39. 'Example config: {underline https://git.io/JJ0jN}',
  40. ],
  41. },
  42. ];
  43. const usage = commandLineUsage(sections);
  44. const args = process.argv.slice(2);
  45. if (args.length === 0 || args[0] === 'help' || args[0] === '-h' || args[0] === '--help') {
  46. console.log(usage);
  47. process.exit(0);
  48. }
  49. const configPath = args[0];
  50. let config;
  51. try {
  52. config = JSON.parse(fs.readFileSync(path.resolve(configPath), 'utf8'));
  53. }
  54. catch (e) {
  55. console.log('Failed to parse config file: ', configPath);
  56. console.log(usage);
  57. process.exit(1);
  58. }
  59. const requiredFields = [
  60. 'ig_username', 'ig_password',
  61. 'cq_bot_qq', ...(config.mode || exampleConfig.mode) === 0 ? ['playwright_ws_spec_endpoint'] : [],
  62. ];
  63. const warningFields = [
  64. 'cq_ws_host', 'cq_ws_port', 'cq_access_token',
  65. ];
  66. const optionalFields = [
  67. 'lockfile', 'cachefile', 'inactive_hours', 'work_interval', 'webshot_delay', 'loglevel', 'mode', 'resume_on_start', 'ig_socks_proxy',
  68. ].concat(warningFields);
  69. if (requiredFields.some((value) => config[value] === undefined)) {
  70. console.log(`${requiredFields.join(', ')} are required`);
  71. process.exit(1);
  72. }
  73. optionalFields.forEach(key => {
  74. if (config[key] === undefined || typeof (config[key]) !== typeof (exampleConfig[key])) {
  75. if (warningFields.includes(key))
  76. logger.warn(`${key} is undefined, use ${exampleConfig[key] || 'empty string'} as default`);
  77. config[key] = exampleConfig[key];
  78. }
  79. });
  80. ['ig_session_lockfile', ...(config.mode || exampleConfig.mode) === 0 ? ['webshot_cookies_lockfile'] : []].forEach(key => {
  81. if (!config[key]) {
  82. logger.warn(`${key} is undefined, use <username>.${key.replace('_lockfile', '.lock')} as default`);
  83. config[key] = `${config.ig_username}.${key.replace('_lockfile', '.lock')}`;
  84. }
  85. });
  86. const k = 'ig_2fa_code_receiver_port';
  87. if (!config[k] || config[k] < 2048 || config[k] > 65536) {
  88. logger.warn(`invalid value of config.${k}, use ${exampleConfig[k]} as default`);
  89. config[k] = exampleConfig[k];
  90. }
  91. (0, loggers_1.setLogLevels)(config.loglevel);
  92. (() => __awaiter(void 0, void 0, void 0, function* () {
  93. const deserialized = {
  94. lockfile: {
  95. workon: 0,
  96. feed: [],
  97. threads: {},
  98. },
  99. cachefile: {},
  100. };
  101. const fileEntries = Object.keys(deserialized).map(file => [file, path.resolve(config[file])]);
  102. for (const [file, filePath] of fileEntries) {
  103. if (fs.existsSync(filePath))
  104. try {
  105. deserialized[file] = yield json.readFile(filePath);
  106. fs.access(filePath, fs.constants.W_OK, err => {
  107. if (err) {
  108. logger.fatal(`cannot write ${file} ${filePath}, permission denied`);
  109. process.exit(1);
  110. }
  111. });
  112. continue;
  113. }
  114. catch (err) {
  115. logger.error(`Failed to parse ${file} ${config[file]}: `, err);
  116. }
  117. try {
  118. yield json.writeFile(filePath, deserialized[file]);
  119. }
  120. catch (err) {
  121. logger.fatal(`cannot write ${file} ${filePath}, permission denied`);
  122. process.exit(1);
  123. }
  124. }
  125. const { lockfile: lock, cachefile: cache } = deserialized;
  126. if (!config.resume_on_start) {
  127. Object.keys(lock.threads).forEach(key => {
  128. lock.threads[key].offset = '-1';
  129. });
  130. }
  131. const qq = new koishi_1.default({
  132. access_token: config.cq_access_token,
  133. host: config.cq_ws_host,
  134. port: config.cq_ws_port,
  135. bot_id: config.cq_bot_qq,
  136. list: (c, a, cb) => (0, command_1.list)(c, a, cb, lock),
  137. sub: (c, a, cb) => (0, command_1.sub)(c, a, cb, lock, config.lockfile),
  138. unsub: (c, a, cb) => (0, command_1.unsub)(c, a, cb, lock, config.lockfile),
  139. unsubAll: (c, a, cb) => (0, command_1.unsubAll)(c, a, cb, lock, config.lockfile),
  140. });
  141. const worker = new twitter_1.default({
  142. sessionLockfile: config.ig_session_lockfile,
  143. credentials: [config.ig_username, config.ig_password],
  144. codeServicePort: config.ig_2fa_code_receiver_port,
  145. proxyUrl: config.ig_socks_proxy,
  146. lock,
  147. lockfile: config.lockfile,
  148. cache,
  149. cachefile: config.cachefile,
  150. inactiveHours: config.inactive_hours,
  151. workInterval: config.work_interval,
  152. bot: qq,
  153. webshotDelay: config.webshot_delay,
  154. webshotCookiesLockfile: config.webshot_cookies_lockfile,
  155. mode: config.mode,
  156. wsUrl: config.playwright_ws_spec_endpoint,
  157. });
  158. worker.session.init().then(worker.launch);
  159. qq.connect();
  160. }))();