From 9c7b34e18eb74b90b6d1f609c7a7873b398170ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Huan=20=28=E6=9D=8E=E5=8D=93=E6=A1=93=29?= Date: Wed, 1 Sep 2021 23:41:42 +0800 Subject: [PATCH] Enable ESM (#150) * Enable ESM * fix eslint under win32 * 0.29.1 --- .eslintrc.js => .eslintrc.cjs | 0 .github/workflows/npm.yml | 33 ++- README.md | 9 +- commonjs/code-root.d.ts | 1 + commonjs/code-root.js | 10 + commonjs/code-root.spec.ts | 9 + commonjs/package.json | 1 + examples/demo.ts | 13 +- package.json | 85 ++++--- scripts/generate-package-json.sh | 17 ++ scripts/generate-version.sh | 18 -- scripts/npm-pack-testing.sh | 61 +++-- scripts/retry-unit-tests.ts | 57 ----- src/bridge.spec.ts | 34 ++- src/bridge.ts | 230 ++++++++---------- src/cjs.spec.ts | 11 + src/cjs.ts | 6 + src/config.ts | 49 +--- src/event.spec.ts | 10 +- src/event.ts | 23 +- src/firer.spec.ts | 22 +- src/firer.ts | 61 +++-- src/mod.ts | 4 +- src/package-json.spec.ts | 10 + src/package-json.ts | 11 + src/puppet-wechat.spec.ts | 36 ++- src/puppet-wechat.ts | 171 +++++++------ src/pure-function-helpers/message-extname.ts | 2 +- src/pure-function-helpers/message-filename.ts | 6 +- .../message-raw-payload-parser.ts | 17 +- src/pure-function-helpers/mod.ts | 13 +- .../normalize-scan-status.spec.ts | 8 +- src/pure-function-helpers/retry-policy.ts | 45 ++++ src/pure-function-helpers/web-message-type.ts | 2 +- src/version.spec.ts | 10 - src/version.ts | 6 - src/web-schemas.ts | 227 +++++++++-------- src/wechaty-bro.js | 5 +- tests/fixtures/smoke-testing.ts | 18 +- tests/puppeteer-contact.spec.ts | 23 +- tests/puppeteer-friendship.spec.ts | 22 +- tests/puppeteer-message.spec.ts | 40 ++- tests/puppeteer-room.spec.ts | 43 ++-- tests/puppeteer.spec.ts | 85 ++++--- tsconfig.cjs.json | 7 + tsconfig.json | 2 +- 46 files changed, 807 insertions(+), 766 deletions(-) rename .eslintrc.js => .eslintrc.cjs (100%) create mode 100644 commonjs/code-root.d.ts create mode 100644 commonjs/code-root.js create mode 100755 commonjs/code-root.spec.ts create mode 100644 commonjs/package.json create mode 100755 scripts/generate-package-json.sh delete mode 100755 scripts/generate-version.sh delete mode 100755 scripts/retry-unit-tests.ts create mode 100755 src/cjs.spec.ts create mode 100644 src/cjs.ts create mode 100755 src/package-json.spec.ts create mode 100644 src/package-json.ts create mode 100644 src/pure-function-helpers/retry-policy.ts delete mode 100755 src/version.spec.ts delete mode 100644 src/version.ts create mode 100644 tsconfig.cjs.json diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 100% rename from .eslintrc.js rename to .eslintrc.cjs diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 3fd6456..e078e51 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -7,16 +7,23 @@ jobs: name: Build strategy: matrix: - os: [ubuntu-latest] - node: [14] + os: + - ubuntu-latest + - windows-latest + - macos-latest + node: + - 14 + - 16 runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} + cache: 'npm' + cache-dependency-path: package.json - name: Install Dependencies run: npm install @@ -30,15 +37,17 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v2 with: - node-version: 14 + node-version: 16 + cache: 'npm' + cache-dependency-path: package.json - name: Install Dependencies run: npm install - - name: Generate Version - run: ./scripts/generate-version.sh + - name: Generate Package JSON + run: ./scripts/generate-package-json.sh - name: Pack Testing run: ./scripts/npm-pack-testing.sh @@ -52,16 +61,18 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v2 with: - node-version: 14 + node-version: 16 registry-url: https://registry.npmjs.org/ + cache: 'npm' + cache-dependency-path: package.json - name: Install Dependencies run: npm install - - name: Generate Version - run: ./scripts/generate-version.sh + - name: Generate Package JSON + run: ./scripts/generate-package-json.sh - name: Set Publish Config run: ./scripts/package-publish-config-tag.sh diff --git a/README.md b/README.md index b0f5e68..fc5653d 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,9 @@ We use [stealth](https://www.npmjs.com/package/puppeteer-extra-plugin-stealth) t ## HISTORY -### master +### master v0.29 + +1. ESM support. ### v0.28 (Apr 13, 2021) @@ -132,14 +134,15 @@ See: ## AUTHOR -[Huan LI](http://linkedin.com/in/zixia) \ +[Huan LI](http://linkedin.com/in/zixia) Tencent TVP of Chatbot \ + profile for zixia on Stack Exchange, a network of free, community-driven Q&A sites ## COPYRIGHT & LICENSE -- Code & Docs © 2016-2019 Huan LI \ +- Code & Docs © 2016-now Huan LI \ - Code released under the Apache-2.0 License - Docs released under Creative Commons diff --git a/commonjs/code-root.d.ts b/commonjs/code-root.d.ts new file mode 100644 index 0000000..5b16a98 --- /dev/null +++ b/commonjs/code-root.d.ts @@ -0,0 +1 @@ +export declare const codeRoot: string diff --git a/commonjs/code-root.js b/commonjs/code-root.js new file mode 100644 index 0000000..9d6d72d --- /dev/null +++ b/commonjs/code-root.js @@ -0,0 +1,10 @@ +const path = require('path') + +const codeRoot = path.join( + __dirname, + '..', +) + +module.exports = { + codeRoot, +} diff --git a/commonjs/code-root.spec.ts b/commonjs/code-root.spec.ts new file mode 100755 index 0000000..eea8094 --- /dev/null +++ b/commonjs/code-root.spec.ts @@ -0,0 +1,9 @@ +#!/usr/bin/env -S ts-node --project tsconfig.cjs.json + +import { test } from 'tstest' + +import { codeRoot } from './code-root' + +test('CJS: codeRoot()', async t => { + t.ok(codeRoot, 'should exist codeRoot') +}) diff --git a/commonjs/package.json b/commonjs/package.json new file mode 100644 index 0000000..a3c15a7 --- /dev/null +++ b/commonjs/package.json @@ -0,0 +1 @@ +{ "type": "commonjs" } diff --git a/examples/demo.ts b/examples/demo.ts index 5951146..5104f14 100644 --- a/examples/demo.ts +++ b/examples/demo.ts @@ -16,12 +16,7 @@ * limitations under the License. * */ - -// tslint:disable:no-console - -import { PuppetWeChat } from '../src/mod' - -import { +import type { EventLogoutPayload, EventLoginPayload, EventScanPayload, @@ -29,6 +24,8 @@ import { EventMessagePayload, } from 'wechaty-puppet' +import { PuppetWeChat } from '../src/mod.js' + /** * * 1. Declare your Bot! @@ -55,7 +52,7 @@ puppet */ puppet.start() .catch(async e => { - console.error('Bot start() fail:', e) + console.error('Bot start() fail:', e as Error) await puppet.stop() process.exit(-1) }) @@ -100,7 +97,7 @@ function onError (payload: EventErrorPayload) { console.error('Bot error:', payload.data) /* if (bot.logonoff()) { - bot.say('Wechaty error: ' + e.message).catch(console.error) + bot.say('Wechaty error: ' + (e as Error).message).catch(console.error) } */ } diff --git a/package.json b/package.json index 716ac12..f95e0ae 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,35 @@ { "name": "wechaty-puppet-wechat", - "version": "0.28.4", + "version": "0.29.1", "description": "Puppet WeChat for Wechaty", - "main": "dist/src/mod.js", - "typings": "dist/src/mod.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./dist/esm/src/mod.js", + "require": "./dist/cjs/src/mod.js" + } + }, + "typings": "./dist/esm/src/mod.d.ts", "engines": { - "wechaty": ">=0.35" + "wechaty": ">=0.69", + "wechaty-puppet": ">=0.43" }, "scripts": { "clean": "shx rm -fr dist/*", - "dist": "npm run clean && tsc && shx cp src/*.js dist/src/", - "pack": "npm pack", + "dist": "npm-run-all clean build dist:copy", + "build": "tsc && tsc -p tsconfig.cjs.json && shx echo '{\"type\": \"commonjs\"}' > dist/cjs/package.json", + "dist:copy": "npm-run-all copy:esm copy:cjs", + "copy:js": "shx cp src/*.js dist/src/", + "copy:esm": "shx cp -R commonjs/ dist/esm/ && shx cp src/*.js dist/esm/src/", + "copy:cjs": "shx cp -R commonjs/ dist/cjs/ && shx cp src/*.js dist/cjs/src/", "lint": "npm run lint:es && npm run lint:ts && npm run lint:md", - "lint:es": "eslint '{bin,examples,scripts,src,tests}/**/*.{js,ts}' --ignore-pattern='tests/fixtures/**'", + "lint:es": "eslint --ignore-pattern fixtures/ \"src/**/*.ts\" \"tests/**/*.ts\"", "lint:md": "markdownlint README.md", - "lint:ts": "tsc --noEmit", - "start": "ts-node examples/demo.ts", - "test": "npm run lint && npm run test:unit:retry", + "lint:ts": "tsc --isolatedModules --noEmit", + "start": "node examples/demo.ts", + "test": "npm run lint && npm run test:unit", "test:pack": "bash -x scripts/npm-pack-testing.sh", - "test:unit": "blue-tape -r ts-node/register 'src/**/*.spec.ts' 'tests/**/*.spec.ts'", - "test:unit:retry": "ts-node scripts/retry-unit-tests" + "test:unit": "tap --node-arg=--loader=ts-node/esm --node-arg=--no-warnings \"src/**/*.spec.ts\" \"tests/**/*.spec.ts\"" }, "repository": { "type": "git", @@ -41,47 +51,52 @@ "url": "https://github.com/wechaty/wechaty-puppet-wechat/issues" }, "devDependencies": { - "@chatie/eslint-config": "^0.12.3", + "@chatie/eslint-config": "^0.13.5", "@chatie/git-scripts": "^0.6.2", "@chatie/semver": "^0.4.7", - "@chatie/tsconfig": "^0.14.1", - "@types/bl": "^2.1.0", - "@types/md5": "^2.3.0", + "@chatie/tsconfig": "^0.19.6", + "@types/bl": "^2", + "@types/md5": "^2.3.1", "@types/mime": "^2.0.3", - "@types/normalize-package-data": "^2.4.0", - "@types/promise-retry": "^1.1.3", - "@types/qr-image": "^3.2.3", - "@types/raven": "^2.5.3", - "@types/request": "^2.48.5", - "@types/retry": "0.12.0", - "@types/xml2js": "^0.4.8", - "normalize-package-data": "^3.0.0", + "@types/qr-image": "^3.2.4", + "@types/raven": "^2.5.4", + "@types/request": "^2.48.7", + "@types/xml2js": "^0.4.9", + "npm-run-all": "^4.1.5", "pkg-jq": "^0.2.11", "shx": "^0.3.3", - "tstest": "^0.4.10", - "wechaty-puppet": "^0.26.2" + "tstest": "^0.5.16", + "typescript": "^4.4.2", + "wechaty-puppet": "^0.43.10" }, "peerDependencies": { "wechaty-puppet": ">=0.34" }, "homepage": "https://github.com/wechaty/wechaty-puppet-wechat#readme", "dependencies": { - "bl": "^4.1.0", - "brolog": "^1.12.4", + "bl": "^2", + "cockatiel": "^2.0.2", "md5": "^2.3.0", "mime": "^2.5.2", - "promise-retry": "^2.0.1", - "puppeteer": "^7.1.0", - "puppeteer-extra": "^3.1.17", - "puppeteer-extra-plugin-stealth": "^2.7.5", + "puppeteer": "^10.2.0", + "puppeteer-extra": "^3.1.18", + "puppeteer-extra-plugin-stealth": "^2.7.8", "qr-image": "^3.2.0", "request": "^2.88.2", - "rx-queue": "^0.8.5", - "rxjs": "^6.6.3", - "state-switch": "^0.6.18", + "rx-queue": "^0.12.6", + "rxjs": "^7.3.0", + "state-switch": "^0.14.1", "watchdog": "^0.8.17", "xml2js": "^0.4.23" }, + "files": [ + "bin/", + "dist/", + "src/" + ], + "tap": { + "check-coverage": false + }, "publishConfig": { "access": "public", "tag": "next" diff --git a/scripts/generate-package-json.sh b/scripts/generate-package-json.sh new file mode 100755 index 0000000..111e894 --- /dev/null +++ b/scripts/generate-package-json.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -e + +SRC_PACKAGE_JSON_TS_FILE='src/package-json.ts' + +[ -f ${SRC_PACKAGE_JSON_TS_FILE} ] || { + echo ${SRC_PACKAGE_JSON_TS_FILE}" not found" + exit 1 +} + +cat <<_SRC_ > ${SRC_PACKAGE_JSON_TS_FILE} +/** + * This file was auto generated from scripts/generate-version.sh + */ +import type { PackageJson } from 'type-fest' +export const packageJson: PackageJson = $(cat package.json) as any +_SRC_ diff --git a/scripts/generate-version.sh b/scripts/generate-version.sh deleted file mode 100755 index e8ef7f9..0000000 --- a/scripts/generate-version.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash -set -e - -SRC_VERSION_TS_FILE='src/version.ts' - -[ -f ${SRC_VERSION_TS_FILE} ] || { - echo ${SRC_VERSION_TS_FILE}" not found" - exit 1 -} - -VERSION=$(npx pkg-jq -r .version) - -cat <<_SRC_ > ${SRC_VERSION_TS_FILE} -/** - * This file was auto generated from scripts/generate-version.sh - */ -export const VERSION: string = '${VERSION}' -_SRC_ diff --git a/scripts/npm-pack-testing.sh b/scripts/npm-pack-testing.sh index af61b9a..581f71f 100755 --- a/scripts/npm-pack-testing.sh +++ b/scripts/npm-pack-testing.sh @@ -3,47 +3,66 @@ set -e VERSION=$(npx pkg-jq -r .version) -if npx --package @chatie/semver semver-is-prod $VERSION; then +if npx --package @chatie/semver semver-is-prod "$VERSION"; then NPM_TAG=latest else NPM_TAG=next fi npm run dist -npm run pack +npm pack TMPDIR="/tmp/npm-pack-testing.$$" mkdir "$TMPDIR" -mv *-*.*.*.tgz "$TMPDIR" +mv ./*-*.*.*.tgz "$TMPDIR" cp tests/fixtures/smoke-testing.ts "$TMPDIR" cd $TMPDIR + npm init -y -npm install *-*.*.*.tgz \ - @chatie/tsconfig \ - @types/normalize-package-data \ - @types/promise-retry \ - @types/puppeteer \ - brolog \ - file-box \ - memory-card \ - normalize-package-data \ - rxjs \ - rx-queue \ - state-switch \ +npm install --production *-*.*.*.tgz \ + @chatie/tsconfig@$NPM_TAG \ "wechaty-puppet@$NPM_TAG" \ - qr-image \ - promise-retry \ - watchdog \ + "wechaty@$NPM_TAG" \ +# +# CommonJS +# ./node_modules/.bin/tsc \ + --target es6 \ + --module CommonJS \ + \ + --moduleResolution node \ --esModuleInterop \ - --lib dom,esnext \ + --lib esnext \ + --noEmitOnError \ + --noImplicitAny \ --skipLibCheck \ + smoke-testing.ts + +echo +echo "CommonJS: pack testing..." +node smoke-testing.js + +# +# ES Modules +# + +# https://stackoverflow.com/a/59203952/1123955 +echo "`jq '.type="module"' package.json`" > package.json + +./node_modules/.bin/tsc \ + --target es2020 \ + --module es2020 \ + \ + --moduleResolution node \ + --esModuleInterop \ + --lib esnext \ --noEmitOnError \ --noImplicitAny \ - --target es6 \ - --module commonjs \ + --skipLibCheck \ smoke-testing.ts +echo +echo "ES Module: pack testing..." node smoke-testing.js diff --git a/scripts/retry-unit-tests.ts b/scripts/retry-unit-tests.ts deleted file mode 100755 index 9a33019..0000000 --- a/scripts/retry-unit-tests.ts +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env ts-node -/** - * https://github.com/Chatie/wechaty/issues/1084 - * WebDriver / Puppeteer sometimes will fail(i.e. timeout) with no reason. - * That will cause the unit tests fail randomly. - * So we need to retry again when unit tests fail, - * and treat it's really fail after MAX_RETRY_NUM times. - */ - -// tslint:disable:no-console - -import { spawn } from 'child_process' - -const MAX_RETRY_NUM = 3 - -async function main (): Promise { - console.info('Safe Test: starting...') - - let round = 0 - let succ = false - do { - console.info(`Safe Test: running for round #${round}`) - succ = await unitTest() - if (succ) { // success! - console.info(`Safe Test: successed at round #${round}!`) - return 0 - } - } while (round++ < MAX_RETRY_NUM) - return 1 // fail finally :( -} - -async function unitTest () { - const child = spawn( - 'npm', - [ - 'run', - 'test:unit', - ], - { - shell: true, // https://stackoverflow.com/a/39682805/1123955 - stdio: 'inherit', - }, - ) - return new Promise((resolve, reject) => { - child.once('exit', (code) => - code === 0 ? resolve(true) : resolve(false), - ) - child.once('error', reject) - }) -} - -main() - .then(process.exit) - .catch((e) => { - console.error(e) - process.exit(1) - }) diff --git a/src/bridge.spec.ts b/src/bridge.spec.ts index 570746a..bdaf5a2 100755 --- a/src/bridge.spec.ts +++ b/src/bridge.spec.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env ts-node +#!/usr/bin/env node --no-warnings --loader ts-node/esm /** * Wechaty - https://github.com/chatie/wechaty * @@ -17,16 +17,11 @@ * limitations under the License. * */ -// tslint:disable:arrow-parens -// tslint:disable:no-shadowed-variable - -import test from 'blue-tape' +import { test } from 'tstest' // import sinon from 'sinon' -import { - launch, -} from 'puppeteer' +import puppeteer from 'puppeteer' // import { spy } from 'sinon' import { @@ -38,7 +33,7 @@ import { // } from './config' // log.silly('BridgeTesting', 'import typings for Brolog') -import Bridge from './bridge' +import Bridge from './bridge.js' const PUPPETEER_LAUNCH_OPTIONS = { args: [ @@ -108,7 +103,6 @@ test('testBlockedMessage()', async t => { '当前登录环境异常。为了你的帐号安全,暂时不能登录web微信。', '你可以通过手机客户端或者windows微信登录。', ].join('') - // tslint:disable:max-line-length const BLOCKED_XML_EN = ` 1203 @@ -120,7 +114,7 @@ test('testBlockedMessage()', async t => { ' To use WeChat on a computer, use Windows WeChat or Mac WeChat at http://wechat.com', ].join('') - t.test('not blocked', async t => { + void t.test('not blocked', async t => { const memory = new MemoryCard() const bridge = new Bridge({ memory }) @@ -128,7 +122,7 @@ test('testBlockedMessage()', async t => { t.equal(msg, false, 'should return false when no block message') }) - t.test('html', async t => { + void t.test('html', async t => { const memory = new MemoryCard() const bridge = new Bridge({ memory }) @@ -136,7 +130,7 @@ test('testBlockedMessage()', async t => { t.equal(msg, BLOCKED_TEXT_ZH, 'should get zh blocked message') }) - t.test('zh', async t => { + void t.test('zh', async t => { const memory = new MemoryCard() const bridge = new Bridge({ memory }) @@ -165,8 +159,8 @@ test('clickSwitchAccount()', async t => { const memory = new MemoryCard() const bridge = new Bridge({ memory }) - t.test('switch account needed', async t => { - const browser = await launch(PUPPETEER_LAUNCH_OPTIONS) + void t.test('switch account needed', async t => { + const browser = await puppeteer.launch(PUPPETEER_LAUNCH_OPTIONS) const page = await browser.newPage() await page.setContent(SWITCH_ACCOUNT_HTML) @@ -178,8 +172,8 @@ test('clickSwitchAccount()', async t => { t.equal(clicked, true, 'should click the switch account button') }) - t.test('switch account not needed', async t => { - const browser = await launch(PUPPETEER_LAUNCH_OPTIONS) + void t.test('switch account not needed', async t => { + const browser = await puppeteer.launch(PUPPETEER_LAUNCH_OPTIONS) const page = await browser.newPage() await page.setContent('

ok

') @@ -209,15 +203,15 @@ test('WechatyBro.ding()', async t => { return WechatyBro.ding() }) as string - t.is(retDing, 'dong', 'should got dong after execute WechatyBro.ding()') + t.equal(retDing, 'dong', 'should got dong after execute WechatyBro.ding()') const retCode = await bridge.proxyWechaty('loginState') - t.is(typeof retCode, 'boolean', 'should got a boolean after call proxyWechaty(loginState)') + t.equal(typeof retCode, 'boolean', 'should got a boolean after call proxyWechaty(loginState)') await bridge.stop() t.pass('b.quit()') } catch (err) { - t.fail('exception: ' + err.message) + t.fail('exception: ' + (err as Error).message) } finally { await memory.destroy() } diff --git a/src/bridge.ts b/src/bridge.ts index cf4f536..9c9bfc2 100644 --- a/src/bridge.ts +++ b/src/bridge.ts @@ -16,48 +16,40 @@ * limitations under the License. * */ -// tslint:disable:arrow-parens - import { EventEmitter } from 'events' import fs from 'fs' import path from 'path' -import { - Browser, - ChromeArgOptions, - Dialog, - launch, - LaunchOptions, - Page, - Protocol, -} from 'puppeteer' +import puppeteer from 'puppeteer' import puppeteerExtra from 'puppeteer-extra' import stealthPlugin from 'puppeteer-extra-plugin-stealth' import { StateSwitch } from 'state-switch' import { parseString } from 'xml2js' -import { +import type { MemoryCard, -} from 'memory-card' - -/* tslint:disable:no-var-requires */ -// const retryPromise = require('retry-promise').default - +} from 'wechaty-puppet' import { log, - MEMORY_SLOT, - retry, -} from './config' +} from 'wechaty-puppet' import { + MEMORY_SLOT, +} from './config.js' +import { + codeRoot, +} from './cjs.js' + +import type { WebContactRawPayload, WebMessageMediaPayload, WebMessageRawPayload, WebRoomRawPayload, -} from './web-schemas' +} from './web-schemas.js' import { unescapeHtml, -} from './pure-function-helpers/mod' + retryPolicy, +} from './pure-function-helpers/mod.js' export interface InjectResult { code: number, @@ -67,16 +59,18 @@ export interface InjectResult { export interface BridgeOptions { endpoint? : string, head? : boolean, - launchOptions? : LaunchOptions, + launchOptions? : puppeteer.LaunchOptions, memory : MemoryCard, stealthless? : boolean, extspam? : string } +export type Cookie = puppeteer.Protocol.Network.Cookie + export class Bridge extends EventEmitter { - private browser : undefined | Browser - private page : undefined | Page + private browser : undefined | puppeteer.Browser + private page : undefined | puppeteer.Page private state : StateSwitch constructor ( @@ -85,7 +79,7 @@ export class Bridge extends EventEmitter { super() log.verbose('PuppetWeChatBridge', 'constructor()') - this.state = new StateSwitch('PuppetWeChatBridge', log) + this.state = new StateSwitch('PuppetWeChatBridge', { log }) } public async start (): Promise { @@ -105,7 +99,7 @@ export class Bridge extends EventEmitter { this.state.on(true) log.verbose('PuppetWeChatBridge', 'start() initPage() done') } catch (e) { - log.error('PuppetWeChatBridge', 'start() exception: %s', e) + log.error('PuppetWeChatBridge', 'start() exception: %s', e as Error) this.state.off(true) try { @@ -119,14 +113,14 @@ export class Bridge extends EventEmitter { log.error('PuppetWeChatBridge', 'start() exception %s, close page/browser exception %s', e, e2) } - this.emit('error', e) + this.emit('error', e as Error) throw e } } - public async initBrowser (): Promise { + public async initBrowser (): Promise { log.verbose('PuppetWeChatBridge', 'initBrowser()') - const launchOptions = { ...this.options.launchOptions } as LaunchOptions & ChromeArgOptions + const launchOptions = { ...this.options.launchOptions } as puppeteer.LaunchOptions & puppeteer.BrowserLaunchArgumentOptions const headless = !(this.options.head) const launchOptionsArgs = launchOptions.args || [] if (this.options.endpoint) { @@ -164,7 +158,7 @@ export class Bridge extends EventEmitter { puppeteerExtra.use(plugin) browser = await puppeteerExtra.launch(options) } else { - browser = await launch(options) + browser = await puppeteer.launch(options) } const version = await browser.version() @@ -173,7 +167,7 @@ export class Bridge extends EventEmitter { return browser } - public async onDialog (dialog: Dialog) { + public async onDialog (dialog: puppeteer.Dialog) { log.warn('PuppetWeChatBridge', 'onDialog() page.on(dialog) type:%s message:%s', dialog.type, dialog.message()) try { @@ -181,12 +175,12 @@ export class Bridge extends EventEmitter { await dialog.accept() // await dialog.dismiss() } catch (e) { - log.error('PuppetWeChatBridge', 'onDialog() dialog.dismiss() reject: %s', e) + log.error('PuppetWeChatBridge', 'onDialog() dialog.dismiss() reject: %s', e as Error) } this.emit('error', new Error(`${dialog.type}(${dialog.message()})`)) } - public async onLoad (page: Page): Promise { + public async onLoad (page: puppeteer.Page): Promise { log.verbose('PuppetWeChatBridge', 'onLoad() page.url=%s', page.url()) if (this.state.off()) { @@ -213,13 +207,13 @@ export class Bridge extends EventEmitter { this.emit('ready') } catch (e) { - log.error('PuppetWeChatBridge', 'onLoad() exception: %s', e) + log.error('PuppetWeChatBridge', 'onLoad() exception: %s', e as Error) await page.close() - this.emit('error', e) + this.emit('error', e as Error) } } - public async initPage (browser: Browser): Promise { + public async initPage (browser: puppeteer.Browser): Promise { log.verbose('PuppetWeChatBridge', 'initPage()') // set this in time because the following callbacks @@ -232,12 +226,12 @@ export class Bridge extends EventEmitter { */ await this.uosPatch(page) - page.on('error', e => this.emit('error', e)) + page.on('error', e => this.emit('error', e as Error)) page.on('dialog', this.onDialog.bind(this)) const cookieList = ( await this.options.memory.get(MEMORY_SLOT) - ) as Protocol.Network.Cookie[] + ) as puppeteer.Protocol.Network.Cookie[] const url = this.entryUrl(cookieList) log.verbose('PuppetWeChatBridge', 'initPage() before page.goto(url)') @@ -260,7 +254,7 @@ export class Bridge extends EventEmitter { return page } - private async uosPatch (page: Page) { + private async uosPatch (page: puppeteer.Page) { /** * Can we support UOS with puppeteer? #127 * https://github.com/wechaty/wechaty-puppet-wechat/issues/127 @@ -285,7 +279,7 @@ export class Bridge extends EventEmitter { url.search += '&' } url.search += 'target=t' - await req.continue({ url }) + await req.continue({ url: url.toString() }) return } if (url.pathname === '/cgi-bin/mmwebwx-bin/webwxnewloginpage') { @@ -302,13 +296,13 @@ export class Bridge extends EventEmitter { }) } - public async readyAngular (page: Page): Promise { + public async readyAngular (page: puppeteer.Page): Promise { log.verbose('PuppetWeChatBridge', 'readyAngular()') try { await page.waitForFunction("typeof window.angular !== 'undefined'") } catch (e) { - log.verbose('PuppetWeChatBridge', 'readyAngular() exception: %s', e) + log.verbose('PuppetWeChatBridge', 'readyAngular() exception: %s', e as Error) const blockedMessage = await this.testBlockedMessage() if (blockedMessage) { // Wechat Account Blocked @@ -325,11 +319,12 @@ export class Bridge extends EventEmitter { } } - public async inject (page: Page): Promise { + public async inject (page: puppeteer.Page): Promise { log.verbose('PuppetWeChatBridge', 'inject()') const WECHATY_BRO_JS_FILE = path.join( - __dirname, + codeRoot, + 'src', 'wechaty-bro.js', ) @@ -366,7 +361,7 @@ export class Bridge extends EventEmitter { log.silly('PuppetWeChatBridge', 'inject() ding success') } catch (e) { - log.verbose('PuppetWeChatBridge', 'inject() exception: %s. stack: %s', e.message, e.stack) + log.verbose('PuppetWeChatBridge', 'inject() exception: %s. stack: %s', (e as Error).message, (e as Error).stack) throw e } } @@ -376,7 +371,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('logout') } catch (e) { - log.error('PuppetWeChatBridge', 'logout() exception: %s', e.message) + log.error('PuppetWeChatBridge', 'logout() exception: %s', (e as Error).message) throw e } } @@ -397,14 +392,14 @@ export class Bridge extends EventEmitter { await this.page.close() log.silly('PuppetWeChatBridge', 'stop() page.close()-ed') } catch (e) { - log.warn('PuppetWeChatBridge', 'stop() page.close() exception: %s', e) + log.warn('PuppetWeChatBridge', 'stop() page.close() exception: %s', e as Error) } try { await this.browser.close() log.silly('PuppetWeChatBridge', 'stop() browser.close()-ed') } catch (e) { - log.warn('PuppetWeChatBridge', 'stop() browser.close() exception: %s', e) + log.warn('PuppetWeChatBridge', 'stop() browser.close() exception: %s', e as Error) } this.state.off(true) @@ -417,7 +412,7 @@ export class Bridge extends EventEmitter { const userName = await this.proxyWechaty('getUserName') return userName } catch (e) { - log.error('PuppetWeChatBridge', 'getUserName() exception: %s', e.message) + log.error('PuppetWeChatBridge', 'getUserName() exception: %s', (e as Error).message) throw e } } @@ -426,7 +421,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('contactRemark', contactId, alias) } catch (e) { - log.verbose('PuppetWeChatBridge', 'contactRemark() exception: %s', e.message) + log.verbose('PuppetWeChatBridge', 'contactRemark() exception: %s', (e as Error).message) // Issue #509 return false instead of throw when contact is not a friend. // throw e log.warn('PuppetWeChatBridge', 'contactRemark() does not work on contact is not a friend') @@ -438,7 +433,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('contactList') } catch (e) { - log.error('PuppetWeChatBridge', 'contactList() exception: %s', e.message) + log.error('PuppetWeChatBridge', 'contactList() exception: %s', (e as Error).message) throw e } } @@ -447,7 +442,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('roomList') } catch (e) { - log.error('PuppetWeChatBridge', 'roomList() exception: %s', e.message) + log.error('PuppetWeChatBridge', 'roomList() exception: %s', (e as Error).message) throw e } } @@ -462,7 +457,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('roomDelMember', roomId, contactId) } catch (e) { - log.error('PuppetWeChatBridge', 'roomDelMember(%s, %s) exception: %s', roomId, contactId, e.message) + log.error('PuppetWeChatBridge', 'roomDelMember(%s, %s) exception: %s', roomId, contactId, (e as Error).message) throw e } } @@ -479,7 +474,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('roomAddMember', roomId, contactId) } catch (e) { - log.error('PuppetWeChatBridge', 'roomAddMember(%s, %s) exception: %s', roomId, contactId, e.message) + log.error('PuppetWeChatBridge', 'roomAddMember(%s, %s) exception: %s', roomId, contactId, (e as Error).message) throw e } } @@ -495,7 +490,7 @@ export class Bridge extends EventEmitter { await this.proxyWechaty('roomModTopic', roomId, topic) return topic } catch (e) { - log.error('PuppetWeChatBridge', 'roomModTopic(%s, %s) exception: %s', roomId, topic, e.message) + log.error('PuppetWeChatBridge', 'roomModTopic(%s, %s) exception: %s', roomId, topic, (e as Error).message) throw e } } @@ -513,7 +508,7 @@ export class Bridge extends EventEmitter { } return roomId } catch (e) { - log.error('PuppetWeChatBridge', 'roomCreate(%s) exception: %s', contactIdList, e.message) + log.error('PuppetWeChatBridge', 'roomCreate(%s) exception: %s', contactIdList, (e as Error).message) throw e } } @@ -530,7 +525,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('verifyUserRequest', contactId, hello) } catch (e) { - log.error('PuppetWeChatBridge', 'verifyUserRequest(%s, %s) exception: %s', contactId, hello, e.message) + log.error('PuppetWeChatBridge', 'verifyUserRequest(%s, %s) exception: %s', contactId, hello, (e as Error).message) throw e } } @@ -547,7 +542,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('verifyUserOk', contactId, ticket) } catch (e) { - log.error('PuppetWeChatBridge', 'verifyUserOk(%s, %s) exception: %s', contactId, ticket, e.message) + log.error('PuppetWeChatBridge', 'verifyUserOk(%s, %s) exception: %s', contactId, ticket, (e as Error).message) throw e } } @@ -571,7 +566,7 @@ export class Bridge extends EventEmitter { throw new Error('send fail') } } catch (e) { - log.error('PuppetWeChatBridge', 'send() exception: %s', e.message) + log.error('PuppetWeChatBridge', 'send() exception: %s', (e as Error).message) throw e } } @@ -582,7 +577,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('getMsgImg', id) } catch (e) { - log.silly('PuppetWeChatBridge', 'proxyWechaty(getMsgImg, %d) exception: %s', id, e.message) + log.silly('PuppetWeChatBridge', 'proxyWechaty(getMsgImg, %d) exception: %s', id, (e as Error).message) throw e } } @@ -593,7 +588,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('getMsgEmoticon', id) } catch (e) { - log.silly('PuppetWeChatBridge', 'proxyWechaty(getMsgEmoticon, %d) exception: %s', id, e.message) + log.silly('PuppetWeChatBridge', 'proxyWechaty(getMsgEmoticon, %d) exception: %s', id, (e as Error).message) throw e } } @@ -604,7 +599,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('getMsgVideo', id) } catch (e) { - log.silly('PuppetWeChatBridge', 'proxyWechaty(getMsgVideo, %d) exception: %s', id, e.message) + log.silly('PuppetWeChatBridge', 'proxyWechaty(getMsgVideo, %d) exception: %s', id, (e as Error).message) throw e } } @@ -615,7 +610,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('getMsgVoice', id) } catch (e) { - log.silly('PuppetWeChatBridge', 'proxyWechaty(getMsgVoice, %d) exception: %s', id, e.message) + log.silly('PuppetWeChatBridge', 'proxyWechaty(getMsgVoice, %d) exception: %s', id, (e as Error).message) throw e } } @@ -626,59 +621,45 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('getMsgPublicLinkImg', id) } catch (e) { - log.silly('PuppetWeChatBridge', 'proxyWechaty(getMsgPublicLinkImg, %d) exception: %s', id, e.message) + log.silly('PuppetWeChatBridge', 'proxyWechaty(getMsgPublicLinkImg, %d) exception: %s', id, (e as Error).message) throw e } } public async getMessage (id: string): Promise { - try { - return await retry(async (retryException, attempt) => { - log.silly('PuppetWeChatBridge', 'getMessage(%s) retry attempt %d', - id, - attempt, - ) - try { - const rawPayload = await this.proxyWechaty('getMessage', id) + const doGet = async () => { + const rawPayload = await this.proxyWechaty('getMessage', id) - if (rawPayload && Object.keys(rawPayload).length > 0) { - return rawPayload - } - throw new Error('got empty return value at attempt: ' + attempt) - } catch (e) { - log.verbose('PuppetWeChatBridge', 'getMessage() proxyWechaty(getMessage, %s) exception: %s', id, e.message) - retryException(e) - } - }) + if (rawPayload && Object.keys(rawPayload).length > 0) { + return rawPayload + } + throw new Error('doGet fail') + } + try { + const rawPayload = await retryPolicy.execute(doGet) + return rawPayload } catch (e) { - log.error('PuppetWeChatBridge', 'promiseRetry() getContact() finally FAIL: %s', e.message) + log.error('PuppetWeChatBridge', 'getMessage() rejection: %s', (e as Error).message) throw e } } public async getContact (id: string): Promise { - try { - return await retry(async (retryException, attempt) => { - log.silly('PuppetWeChatBridge', 'getContact(%s) retry attempt %d', - id, - attempt, - ) - try { - const rawPayload = await this.proxyWechaty('getContact', id) + const doGet = async () => { + const rawPayload = await this.proxyWechaty('getContact', id) - if (rawPayload && Object.keys(rawPayload).length > 0) { - return rawPayload - } - throw new Error('got empty return value at attempt: ' + attempt) - } catch (e) { - log.verbose('PuppetWeChatBridge', 'getContact() proxyWechaty(getContact, %s) exception: %s', id, e.message) - retryException(e) - } - }) + if (rawPayload && Object.keys(rawPayload).length > 0) { + return rawPayload + } + throw new Error('doGet fail') + } + try { + const rawPayload = await retryPolicy.execute(doGet) + return rawPayload } catch (e) { - log.error('PuppetWeChatBridge', 'promiseRetry() getContact() finally FAIL: %s', e.message) + log.error('PuppetWeChatBridge', 'getContact() rejection: %s', (e as Error).message) throw e } } @@ -689,7 +670,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('getBaseRequest') } catch (e) { - log.silly('PuppetWeChatBridge', 'proxyWechaty(getBaseRequest) exception: %s', e.message) + log.silly('PuppetWeChatBridge', 'proxyWechaty(getBaseRequest) exception: %s', (e as Error).message) throw e } } @@ -700,7 +681,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('getPassticket') } catch (e) { - log.silly('PuppetWeChatBridge', 'proxyWechaty(getPassticket) exception: %s', e.message) + log.silly('PuppetWeChatBridge', 'proxyWechaty(getPassticket) exception: %s', (e as Error).message) throw e } } @@ -711,7 +692,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('getCheckUploadUrl') } catch (e) { - log.silly('PuppetWeChatBridge', 'proxyWechaty(getCheckUploadUrl) exception: %s', e.message) + log.silly('PuppetWeChatBridge', 'proxyWechaty(getCheckUploadUrl) exception: %s', (e as Error).message) throw e } } @@ -722,7 +703,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('getUploadMediaUrl') } catch (e) { - log.silly('PuppetWeChatBridge', 'proxyWechaty(getUploadMediaUrl) exception: %s', e.message) + log.silly('PuppetWeChatBridge', 'proxyWechaty(getUploadMediaUrl) exception: %s', (e as Error).message) throw e } } @@ -739,7 +720,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('sendMedia', mediaData) } catch (e) { - log.error('PuppetWeChatBridge', 'sendMedia() exception: %s', e.message) + log.error('PuppetWeChatBridge', 'sendMedia() exception: %s', (e as Error).message) throw e } } @@ -756,7 +737,7 @@ export class Bridge extends EventEmitter { try { return await this.proxyWechaty('forward', baseData, patchData) } catch (e) { - log.error('PuppetWeChatBridge', 'forward() exception: %s', e.message) + log.error('PuppetWeChatBridge', 'forward() exception: %s', (e as Error).message) throw e } } @@ -788,7 +769,7 @@ export class Bridge extends EventEmitter { throw e } } catch (e) { - log.warn('PuppetWeChatBridge', 'proxyWechaty() noWechaty exception: %s', e) + log.warn('PuppetWeChatBridge', 'proxyWechaty() noWechaty exception: %s', e as Error) throw e } @@ -817,7 +798,7 @@ export class Bridge extends EventEmitter { return ret } catch (e) { log.verbose('PuppetWeChatBridge', 'proxyWechaty(%s, %s) ', wechatyFunc, args.join(', ')) - log.warn('PuppetWeChatBridge', 'proxyWechaty() exception: %s', e.message) + log.warn('PuppetWeChatBridge', 'proxyWechaty() exception: %s', (e as Error).message) throw e } } @@ -830,8 +811,8 @@ export class Bridge extends EventEmitter { return this.emit('dong', dongData) }) .catch(e => { - log.error('PuppetWeChatBridge', 'ding(%s) exception: %s', data, e.message) - this.emit('error', e) + log.error('PuppetWeChatBridge', 'ding(%s) exception: %s', data, (e as Error).message) + this.emit('error', e as Error) }) } @@ -889,7 +870,7 @@ export class Bridge extends EventEmitter { }) }) } catch (e) { - log.warn('PuppetWeChatBridge', 'testBlockedMessage() toJson() exception: %s', e) + log.warn('PuppetWeChatBridge', 'testBlockedMessage() toJson() exception: %s', e as Error) return false } @@ -945,7 +926,7 @@ export class Bridge extends EventEmitter { // }) } - public async clickSwitchAccount (page: Page): Promise { + public async clickSwitchAccount (page: puppeteer.Page): Promise { log.verbose('PuppetWeChatBridge', 'clickSwitchAccount()') // https://github.com/GoogleChrome/puppeteer/issues/537#issuecomment-334918553 @@ -976,7 +957,7 @@ export class Bridge extends EventEmitter { // await Promise.all(releasePromises) // return elementHandleList // } catch (e) { - // log.verbose('PuppetWeChatBridge', 'clickSwitchAccount() listXpath() exception: %s', e) + // log.verbose('PuppetWeChatBridge', 'clickSwitchAccount() listXpath() exception: %s', e as Error) // return [] // } // } @@ -1001,7 +982,7 @@ export class Bridge extends EventEmitter { } } catch (e) { - log.silly('PuppetWeChatBridge', 'clickSwitchAccount() exception: %s', e) + log.silly('PuppetWeChatBridge', 'clickSwitchAccount() exception: %s', e as Error) throw e } } @@ -1018,8 +999,8 @@ export class Bridge extends EventEmitter { log.silly('PuppetWeChatBridge', 'hostname() got %s', hostname) return hostname } catch (e) { - log.error('PuppetWeChatBridge', 'hostname() exception: %s', e) - this.emit('error', e) + log.error('PuppetWeChatBridge', 'hostname() exception: %s', e as Error) + this.emit('error', e as Error) return null } } @@ -1027,7 +1008,7 @@ export class Bridge extends EventEmitter { public async cookies (cookieList: Cookie[]): Promise public async cookies (): Promise - public async cookies (cookieList?: Protocol.Network.Cookie[]): Promise { + public async cookies (cookieList?: puppeteer.Protocol.Network.Cookie[]): Promise { if (!this.page) { throw new Error('no page') } @@ -1036,8 +1017,8 @@ export class Bridge extends EventEmitter { try { await this.page.setCookie(...cookieList) } catch (e) { - log.error('PuppetWeChatBridge', 'cookies(%s) reject: %s', cookieList, e) - this.emit('error', e) + log.error('PuppetWeChatBridge', 'cookies(%s) reject: %s', cookieList, e as Error) + this.emit('error', e as Error) } // RETURN } else { @@ -1049,7 +1030,7 @@ export class Bridge extends EventEmitter { /** * name */ - public entryUrl (cookieList?: Protocol.Network.Cookie[]): string { + public entryUrl (cookieList?: puppeteer.Protocol.Network.Cookie[]): string { log.verbose('PuppetWeChatBridge', 'cookieDomain(%s)', cookieList) /** @@ -1067,7 +1048,7 @@ export class Bridge extends EventEmitter { log.silly('PuppetWeChatBridge', 'cookieDomain() no valid cookie, return default hostname') return DEFAULT_URL } - let domain = wxCookieList[0].domain + let domain = wxCookieList[0]!.domain if (!domain) { log.silly('PuppetWeChatBridge', 'cookieDomain() no valid domain in cookies, return default hostname') return DEFAULT_URL @@ -1110,13 +1091,12 @@ export class Bridge extends EventEmitter { try { return await this.page.evaluate(fn, ...args) } catch (e) { - log.error('PuppetWeChatBridge', 'evaluate() exception: %s', e) - this.emit('error', e) + log.error('PuppetWeChatBridge', 'evaluate() exception: %s', e as Error) + this.emit('error', e as Error) return null } } } -export type Cookie = Protocol.Network.Cookie export default Bridge diff --git a/src/cjs.spec.ts b/src/cjs.spec.ts new file mode 100755 index 0000000..6d2f9bb --- /dev/null +++ b/src/cjs.spec.ts @@ -0,0 +1,11 @@ +#!/usr/bin/env -S node --no-warnings --loader ts-node/esm + +import { test } from 'tstest' + +import { + codeRoot, +} from './cjs.js' + +test('ESM: codeRoot', async t => { + t.ok(codeRoot, 'should exists "codeRoot"') +}) diff --git a/src/cjs.ts b/src/cjs.ts new file mode 100644 index 0000000..8b63aeb --- /dev/null +++ b/src/cjs.ts @@ -0,0 +1,6 @@ +import codeRootPkg from '../commonjs/code-root.js' +const codeRoot = codeRootPkg['codeRoot'] + +export { + codeRoot, +} diff --git a/src/config.ts b/src/config.ts index 069d20e..9fce52e 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,46 +1,17 @@ /// -import { OperationOptions } from 'retry' - import { FileBox, log, } from 'wechaty-puppet' import qrImage from 'qr-image' -import { VERSION } from './version' - -import promiseRetry = require('promise-retry') +import { packageJson } from './package-json.js' +import type { Readable } from 'stream' -export async function retry ( - retryableFn: ( - retry: (error: Error) => never, - attempt: number, - ) => Promise, -): Promise { - /** - * 60 seconds: (to be confirmed) - * factor: 3 - * minTimeout: 10 - * maxTimeout: 20 * 1000 - * retries: 9 - */ - const factor = 3 - const minTimeout = 10 - const maxTimeout = 20 * 1000 - const retries = 9 - // const unref = true - - const retryOptions: OperationOptions = { - factor, - maxTimeout, - minTimeout, - retries, - } - return promiseRetry(retryOptions, retryableFn) -} +const VERSION = packageJson.version || '0.0.0' -export function envHead (): boolean { +function envHead (): boolean { const KEY = 'WECHATY_PUPPET_WECHAT_PUPPETEER_HEAD' return KEY in process.env ? !!process.env[KEY] @@ -48,24 +19,28 @@ export function envHead (): boolean { } -export function envStealthless (): boolean { +function envStealthless (): boolean { const KEY = 'WECHATY_PUPPET_WECHAT_PUPPETEER_STEALTHLESS' return !!process.env[KEY] } -export function qrCodeForChatie (): FileBox { +function qrCodeForChatie (): FileBox { const CHATIE_OFFICIAL_ACCOUNT_QRCODE = 'http://weixin.qq.com/r/qymXj7DEO_1ErfTs93y5' const name = 'qrcode-for-chatie.png' const type = 'png' const qrStream = qrImage.image(CHATIE_OFFICIAL_ACCOUNT_QRCODE, { type }) - return FileBox.fromStream(qrStream, name) + return FileBox.fromStream(qrStream as Readable, name) } -export const MEMORY_SLOT = 'PUPPET_WECHAT' +const MEMORY_SLOT = 'PUPPET_WECHAT' export { VERSION, log, + envHead, + envStealthless, + MEMORY_SLOT, + qrCodeForChatie, } diff --git a/src/event.spec.ts b/src/event.spec.ts index adde834..e5a5fea 100755 --- a/src/event.spec.ts +++ b/src/event.spec.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env ts-node +#!/usr/bin/env node --no-warnings --loader ts-node/esm /** * Wechaty - https://github.com/chatie/wechaty @@ -18,14 +18,12 @@ * limitations under the License. * */ -import test from 'blue-tape' -// tslint:disable:no-shadowed-variable -// import sinon from 'sinon' +import { test } from 'tstest' import { // Event, PuppetWeChat, -} from './puppet-wechat' +} from './puppet-wechat.js' test('Puppet Puppeteer Event smoke testing', async (t) => { const puppet = new PuppetWeChat() @@ -36,6 +34,6 @@ test('Puppet Puppeteer Event smoke testing', async (t) => { await puppet.stop() t.pass('should be quited') } catch (e) { - t.fail('exception: ' + e.message) + t.fail('exception: ' + (e as Error).message) } }) diff --git a/src/event.ts b/src/event.ts index 5d8ee88..5190745 100644 --- a/src/event.ts +++ b/src/event.ts @@ -16,33 +16,32 @@ * limitations under the License. * */ -import { +import type { WatchdogFood, } from 'watchdog' import { log, -} from './config' +} from './config.js' // import { // PuppetScanEvent, // } from 'wechaty-puppet' import { Firer, -} from './firer' -import { +} from './firer.js' +import type { PuppetWeChat, -} from './puppet-wechat' +} from './puppet-wechat.js' import { WebMessageRawPayload, WebMessageType, -} from './web-schemas' +} from './web-schemas.js' import { normalizeScanStatus, -} from './pure-function-helpers/normalize-scan-status' +} from './pure-function-helpers/normalize-scan-status.js' -/* tslint:disable:variable-name */ export const Event = { onDing, @@ -168,13 +167,13 @@ async function onLogin ( // fix issue https://github.com/Chatie/wechaty-puppet-wechat/issues/107 // we do not wait `ready` before emit `login` this.waitStable().catch(e => { - log.error('PuppetWeChatEvent', 'onLogin() this.waitStable() rejection: %s', e && e.message) + log.error('PuppetWeChatEvent', 'onLogin() this.waitStable() rejection: %s', e && (e as Error).message) }) await this.login(userId) } catch (e) { - log.error('PuppetWeChatEvent', 'onLogin() exception: %s', e) + log.error('PuppetWeChatEvent', 'onLogin() exception: %s', e as Error) throw e } } @@ -237,8 +236,8 @@ async function onUnload (this: PuppetWeChat): Promise { await this.quit() await this.init() } catch (e) { - log.error('PuppetWeChatEvent', 'onUnload() exception: %s', e) - this.emit('error', e) + log.error('PuppetWeChatEvent', 'onUnload() exception: %s', e as Error) + this.emit('error', e as Error) throw e } */ diff --git a/src/firer.spec.ts b/src/firer.spec.ts index ebd73e0..7f08ff2 100755 --- a/src/firer.spec.ts +++ b/src/firer.spec.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env ts-node +#!/usr/bin/env node --no-warnings --loader ts-node/esm /** * Wechaty - https://github.com/chatie/wechaty * @@ -22,12 +22,10 @@ * Process the Message to find which event to FIRE */ -// tslint:disable:no-shadowed-variable -import test from 'blue-tape' -// import sinon from 'sinon' +import { test } from 'tstest' -import { Firer } from './firer' -import { PuppetWeChat } from './puppet-wechat' +import { Firer } from './firer.js' +import type { PuppetWeChat } from './puppet-wechat.js' const SELF_ID = 'self-id' const mockPuppetWeChat = { @@ -126,8 +124,8 @@ test('parseRoomJoin()', async (t) => { contentList.forEach(([content, inviter, inviteeList]) => { result = (firer as any).parseRoomJoin(content) t.ok(result, 'should check room join message right for ' + content) - t.deepEqual(result[0], inviteeList, 'should get inviteeList right') - t.is(result[1], inviter, 'should get inviter right') + t.same(result[0], inviteeList, 'should get inviteeList right') + t.equal(result[1], inviter, 'should get inviter right') }) t.throws(() => { @@ -163,13 +161,13 @@ test('parseRoomLeave()', async (t) => { contentLeaverList.forEach(([content, leaver]) => { const resultLeaver = (firer as any).parseRoomLeave(content)[0] t.ok(resultLeaver, 'should get leaver for leave message: ' + content) - t.is(resultLeaver, leaver, 'should get leaver name right') + t.equal(resultLeaver, leaver, 'should get leaver name right') }) contentRemoverList.forEach(([content, remover]) => { const resultRemover = (firer as any).parseRoomLeave(content)[1] t.ok(resultRemover, 'should get remover for leave message: ' + content) - t.is(resultRemover, remover, 'should get leaver name right') + t.equal(resultRemover, remover, 'should get leaver name right') }) t.throws(() => { @@ -197,8 +195,8 @@ test('parseRoomTopic()', async (t) => { contentList.forEach(([content, changer, topic]) => { result = (firer as any).parseRoomTopic(content) t.ok(result, 'should check topic right for content: ' + content) - t.is(topic, result[0], 'should get right topic') - t.is(changer, result[1], 'should get right changer') + t.equal(topic, result[0], 'should get right topic') + t.equal(changer, result[1], 'should get right changer') }) t.throws(() => { diff --git a/src/firer.ts b/src/firer.ts index c0c5347..1115791 100644 --- a/src/firer.ts +++ b/src/firer.ts @@ -16,22 +16,19 @@ * limitations under the License. * */ - -// tslint:disable:no-var-requires -// tslint:disable:arrow-parens - -// const retryPromise = require('retry-promise').default -// import cuid from 'cuid' +import { + PayloadType, +} from 'wechaty-puppet' import { log, -} from './config' +} from './config.js' -import { PuppetWeChat } from './puppet-wechat' -import { +import type { PuppetWeChat } from './puppet-wechat.js' +import type { // WebRecomendInfo, WebMessageRawPayload, -} from './web-schemas' +} from './web-schemas.js' // import { // // FriendRequestPayload, @@ -192,7 +189,7 @@ export class Firer { * Convert the display name to Contact ID */ let inviterContactId: undefined | string - const inviteeContactIdList: string[] = [] + const inviteeContactIdList: string[] = [] if (/^You|你$/i.test(inviterName)) { // === 'You' || inviter === '你' || inviter === 'you' inviterContactId = this.puppet.selfId() @@ -217,7 +214,7 @@ export class Firer { * set inviteeContactIdList */ for (let i = 0; i < inviteeNameList.length; i++) { - const inviteeName = inviteeNameList[i] + const inviteeName = inviteeNameList[i]! const inviteeContactId = inviteeContactIdList[i] if (inviteeContactId) { @@ -230,7 +227,7 @@ export class Firer { } catch (e) { log.warn('PuppetWeChatFirer', 'fireRoomJoin() contactPayload(%s) exception: %s', inviteeContactId, - e.message, + (e as Error).message, ) ready = false } @@ -247,12 +244,16 @@ export class Firer { const contactId = memberIdList[0] // XXX: Take out the first one if we have matched many contact. - inviteeContactIdList[i] = contactId + inviteeContactIdList[i] = contactId! - try { - await this.puppet.contactPayload(contactId) - } catch (e) { + if (!contactId) { ready = false + } else { + try { + await this.puppet.contactPayload(contactId) + } catch (e) { + ready = false + } } } @@ -278,9 +279,11 @@ export class Firer { * Resolve All Payload again to make sure the data is ready. */ await Promise.all( - inviteeContactIdList.map( - id => this.puppet.contactPayload(id), - ), + inviteeContactIdList + .filter(id => !!id) + .map( + id => this.puppet.contactPayload(id!), + ), ) if (!inviterContactId) { @@ -322,7 +325,7 @@ export class Firer { try { [leaverName, removerName] = this.parseRoomLeave(rawPayload.Content) } catch (e) { - log.silly('PuppetWeChatFirer', 'fireRoomLeave() %s', e.message) + log.silly('PuppetWeChatFirer', 'fireRoomLeave() %s', (e as Error).message) return false } log.silly('PuppetWeChatFirer', 'fireRoomLeave() got leaverName: %s', leaverName) @@ -371,7 +374,11 @@ export class Firer { }) setTimeout(async () => { - await this.puppet.roomPayloadDirty(roomId) + await this.puppet.dirtyPayload(PayloadType.Room, roomId) + // this.puppet.emit('dirty', { + // payloadType: PayloadType.Room, + // payloadId: roomId, + // }) await this.puppet.roomPayload(roomId) }, 10 * 1000) // reload the room data, especially for memberList @@ -419,7 +426,7 @@ export class Firer { }) return true } catch (e) { - log.error('PuppetWeChatFirer', 'fireRoomTopic() co exception: %s', e.stack) + log.error('PuppetWeChatFirer', 'fireRoomTopic() co exception: %s', (e as Error).stack) return false } } @@ -473,9 +480,9 @@ export class Firer { const [inviter, inviteeStr] = foundInvite ? [foundInvite[1], foundInvite[2]] : [foundQrcode[2], foundQrcode[1]] // FIXME: should also compatible english split - const inviteeList = inviteeStr.split(/、/) + const inviteeList = inviteeStr?.split(/、/) || [] - return [inviteeList, inviter] // put invitee at first place + return [inviteeList, inviter!] // put invitee at first place } private parseRoomLeave ( @@ -508,7 +515,7 @@ export class Firer { throw new Error('no match') } - return [leaverName, removerName] + return [leaverName!, removerName!] } private parseRoomTopic ( @@ -522,7 +529,7 @@ export class Firer { throw new Error('checkRoomTopic() not found') } const [, changer, topic] = found - return [topic, changer] + return [topic!, changer!] } } diff --git a/src/mod.ts b/src/mod.ts index 180eedd..d3cad0e 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -16,12 +16,12 @@ * limitations under the License. * */ -import { PuppetWeChat } from './puppet-wechat' +import { PuppetWeChat } from './puppet-wechat.js' export { VERSION, log, -} from './config' +} from './config.js' export { PuppetWeChat, diff --git a/src/package-json.spec.ts b/src/package-json.spec.ts new file mode 100755 index 0000000..4e65468 --- /dev/null +++ b/src/package-json.spec.ts @@ -0,0 +1,10 @@ +#!/usr/bin/env node --no-warnings --loader ts-node/esm + +import { test } from 'tstest' + +import { packageJson } from './package-json.js' + +test('Make sure the packageJson is fresh in source code', async t => { + const keyNum = Object.keys(packageJson).length + t.equal(keyNum, 0, 'packageJson should be empty in source code, only updated before publish to NPM') +}) diff --git a/src/package-json.ts b/src/package-json.ts new file mode 100644 index 0000000..0a9806f --- /dev/null +++ b/src/package-json.ts @@ -0,0 +1,11 @@ +/** + * This file will be overwrite when we publish NPM module + * by scripts/generate_version.ts + */ +import type { PackageJson } from 'type-fest' + +/** + * Huan(202108): + * The below default values is only for unit testing + */ +export const packageJson: PackageJson = {} diff --git a/src/puppet-wechat.spec.ts b/src/puppet-wechat.spec.ts index 8432695..6c1c423 100755 --- a/src/puppet-wechat.spec.ts +++ b/src/puppet-wechat.spec.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env ts-node +#!/usr/bin/env node --no-warnings --loader ts-node/esm /** * Wechaty - https://github.com/chatie/wechaty * @@ -17,12 +17,10 @@ * limitations under the License. * */ -// tslint:disable:no-shadowed-variable -// tslint:disable:no-var-requires -// tslint:disable:only-arrow-functions -// tslint:disable:arrow-parens - -import { test, sinon } from 'tstest'// const sinonTest = require('sinon-test')(sinon, { +import { + test, + sinon, +} from 'tstest'// const sinonTest = require('sinon-test')(sinon, { // useFakeTimers: { // https://github.com/sinonjs/lolex // advanceTimeDelta : 10, // shouldAdvanceTime : true, @@ -32,21 +30,21 @@ import { test, sinon } from 'tstest'// const sinonTest = require('sinon-test') // import { log } from './config' // log.level('silly') -import { Bridge } from './bridge' -import { Event } from './event' -import { PuppetWeChat } from './puppet-wechat' +import { Bridge } from './bridge.js' +import { Event } from './event.js' +import { PuppetWeChat } from './puppet-wechat.js' class PuppetTest extends PuppetWeChat { - public contactRawPayload (id: string) { + override contactRawPayload (id: string) { return super.contactRawPayload(id) } - public roomRawPayload (id: string) { + override roomRawPayload (id: string) { return super.roomRawPayload(id) } - public messageRawPayload (id: string) { + override messageRawPayload (id: string) { return super.messageRawPayload(id) } @@ -88,14 +86,14 @@ test('login/logout events', async t => { await puppet.start() t.pass('should be inited') - t.is(puppet.logonoff(), false, 'should be not logined') + t.equal(puppet.logonoff(), false, 'should be not logined') const future = new Promise(resolve => puppet.once('login', resolve)) .catch(e => t.fail(e)) puppet.bridge.emit('login', 'TestPuppetWeChat') await future - t.is(puppet.logonoff(), true, 'should be logined') + t.equal(puppet.logonoff(), true, 'should be logined') t.ok((puppet.bridge.getUserName as any).called, 'bridge.getUserName should be called') @@ -115,18 +113,18 @@ test('login/logout events', async t => { * * 3, 4, 5 is PuppetWeChat.waitStable() for `unchangedNum` to reach 3 times. */ - t.is((Bridge.prototype.contactList as any).callCount, 6, 'should call stubContacList 6 times') + t.equal((Bridge.prototype.contactList as any).callCount, 6, 'should call stubContacList 6 times') t.ok(readySpy.called, 'should emit ready event, after login') const logoutPromise = new Promise((resolve) => puppet.once('logout', () => resolve('logoutFired'))) puppet.bridge.emit('logout') - t.is(await logoutPromise, 'logoutFired', 'should fire logout event') - t.is(puppet.logonoff(), false, 'should be logouted') + t.equal(await logoutPromise, 'logoutFired', 'should fire logout event') + t.equal(puppet.logonoff(), false, 'should be logouted') await puppet.stop() } catch (e) { - t.fail(e) + t.fail(e as any) } finally { sandbox.restore() } diff --git a/src/puppet-wechat.ts b/src/puppet-wechat.ts index eb3bb99..a9f3fcc 100644 --- a/src/puppet-wechat.ts +++ b/src/puppet-wechat.ts @@ -16,11 +16,6 @@ * limitations under the License. * */ - -// tslint:disable:member-ordering -// tslint:disable:arrow-parens -// tslint:disable:object-literal-key-quotes - import path from 'path' import nodeUrl from 'url' @@ -28,7 +23,7 @@ import BufferList from 'bl' import md5 from 'md5' import mime from 'mime' import request from 'request' -import { +import type { LaunchOptions, } from 'puppeteer' @@ -68,16 +63,16 @@ import { UrlLinkPayload, ImageType, EventScanPayload, + log, } from 'wechaty-puppet' import { envStealthless, envHead, - log, MEMORY_SLOT, qrCodeForChatie, VERSION, -} from './config' +} from './config.js' import { messageFilename, @@ -85,15 +80,15 @@ import { plainText, unescapeHtml, isRoomId, -} from './pure-function-helpers/mod' +} from './pure-function-helpers/mod.js' import { Bridge, Cookie, -} from './bridge' +} from './bridge.js' import { Event, -} from './event' +} from './event.js' import { WebAppMsgType, @@ -105,13 +100,19 @@ import { WebRecomendInfo, WebRoomRawMember, WebRoomRawPayload, -} from './web-schemas' +} from './web-schemas.js' export type ScanFoodType = 'scan' | 'login' | 'logout' +type PuppetWeChatOptions = PuppetOptions & { + head? : boolean + launchOptions? : LaunchOptions + stealthless? : boolean +} + export class PuppetWeChat extends Puppet { - public static readonly VERSION = VERSION + public static override readonly VERSION = VERSION public bridge: Bridge @@ -121,16 +122,16 @@ export class PuppetWeChat extends Puppet { private fileId: number constructor ( - public options: PuppetOptions = {}, + public override options: PuppetWeChatOptions = {}, ) { super(options) this.fileId = 0 this.bridge = new Bridge({ - endpoint : options.endpoint || process.env.WECHATY_PUPPET_PUPPETEER_ENDPOINT, - extspam : options.token || process.env.WECHATY_PUPPET_WECHAT_TOKEN, - head : typeof options.head === 'boolean' ? options.head : envHead(), - launchOptions : options.launchOptions as LaunchOptions, + endpoint : options.endpoint || process.env['WECHATY_PUPPET_PUPPETEER_ENDPOINT'], + extspam : options.token || process.env['WECHATY_PUPPET_WECHAT_TOKEN'], + head : typeof options.head === 'boolean' ? options['head'] : envHead(), + launchOptions : options.launchOptions, memory : this.memory, stealthless : typeof options.stealthless === 'boolean' ? options.stealthless : envStealthless(), }) @@ -141,7 +142,7 @@ export class PuppetWeChat extends Puppet { this.initWatchdogForScan() } - public async start (): Promise { + public override async start (): Promise { log.verbose('PuppetWeChat', `start() with ${this.memory.name}`) if (this.state.on()) { @@ -153,6 +154,8 @@ export class PuppetWeChat extends Puppet { this.state.on('pending') try { + await super.start() + /** * Overwrite the memory in bridge * because it could be changed between constructor() and start() @@ -196,19 +199,20 @@ export class PuppetWeChat extends Puppet { return } catch (e) { - log.error('PuppetWeChat', 'start() exception: %s', e) + log.error('PuppetWeChat', 'start() exception: %s', e as Error) // this.state.off(true) - this.emit('error', e) + this.emit('error', { + data: (e as Error).message, + }) await this.stop() throw e } } - public async stop (): Promise { + public override async stop (): Promise { log.verbose('PuppetWeChat', 'stop()') - if (this.state.off()) { log.warn('PuppetWeChat', 'stop() is called on a OFF puppet. await ready(off) and return.') await this.state.ready('off') @@ -218,6 +222,8 @@ export class PuppetWeChat extends Puppet { log.verbose('PuppetWeChat', 'stop() make watchdog sleep before do stop') + await super.stop() + /** * Clean listeners for `watchdog` */ @@ -225,14 +231,14 @@ export class PuppetWeChat extends Puppet { this.scanWatchdog.sleep() // this.watchdog.removeAllListeners() this.scanWatchdog.removeAllListeners() - this.removeAllListeners('watchdog') + this.removeAllListeners('watchdog' as any) try { await this.bridge.stop() // register the removeListeners micro task at then end of the task queue setImmediate(() => this.bridge.removeAllListeners()) } catch (e) { - log.error('PuppetWeChat', 'this.bridge.quit() exception: %s', e.message) + log.error('PuppetWeChat', 'this.bridge.quit() exception: %s', (e as Error).message) throw e } finally { this.state.off(true) @@ -284,15 +290,17 @@ export class PuppetWeChat extends Puppet { try { await this.bridge.reload() } catch (e) { - log.error('PuppetWeChat', 'initScanWatchdog() on(reset) exception: %s', e) + log.error('PuppetWeChat', 'initScanWatchdog() on(reset) exception: %s', e as Error) try { - log.error('PuppetWeChat', 'initScanWatchdog() on(reset) try to recover by bridge.{quit,init}()', e) + log.error('PuppetWeChat', 'initScanWatchdog() on(reset) try to recover by bridge.{quit,init}()', e as Error) await this.bridge.stop() await this.bridge.start() log.error('PuppetWeChat', 'initScanWatchdog() on(reset) recover successful') } catch (e) { - log.error('PuppetWeChat', 'initScanWatchdog() on(reset) recover FAIL: %s', e) - this.emit('error', e) + log.error('PuppetWeChat', 'initScanWatchdog() on(reset) recover FAIL: %s', e as Error) + this.emit('error', { + data: (e as Error).message, + }) } } }) @@ -303,7 +311,7 @@ export class PuppetWeChat extends Puppet { if (this.state.off()) { const e = new Error('initBridge() found targetState != live, no init anymore') - log.warn('PuppetWeChat', e.message) + log.warn('PuppetWeChat', (e as Error).message) throw e } @@ -311,7 +319,7 @@ export class PuppetWeChat extends Puppet { // this.bridge.on('ding' , Event.onDing.bind(this)) this.bridge.on('heartbeat', (data: string) => this.emit('heartbeat', { data: data + 'bridge ding' })) - this.bridge.on('error', (e: Error) => this.emit('error', { data: (e && e.message) || String(e) })) + this.bridge.on('error', (e: Error) => this.emit('error', { data: (e && (e as Error).message) || String(e) })) this.bridge.on('log', Event.onLog.bind(this)) this.bridge.on('login', Event.onLogin.bind(this)) this.bridge.on('logout', Event.onLogout.bind(this)) @@ -322,11 +330,13 @@ export class PuppetWeChat extends Puppet { try { await this.bridge.start() } catch (e) { - log.error('PuppetWeChat', 'initBridge() exception: %s', e.message) + log.error('PuppetWeChat', 'initBridge() exception: %s', (e as Error).message) await this.bridge.stop().catch(e => { - log.error('PuppetWeChat', 'initBridge() this.bridge.stop() rejection: %s', e) + log.error('PuppetWeChat', 'initBridge() this.bridge.stop() rejection: %s', e as Error) + }) + this.emit('error', { + data: (e as Error).message, }) - this.emit('error', e) throw e } @@ -340,12 +350,12 @@ export class PuppetWeChat extends Puppet { const obj = JSON.parse(json) return obj.BaseRequest } catch (e) { - log.error('PuppetWeChat', 'send() exception: %s', e.message) + log.error('PuppetWeChat', 'send() exception: %s', (e as Error).message) throw e } } - public unref (): void { + public override unref (): void { log.verbose('PuppetWeChat', 'unref ()') super.unref() @@ -469,7 +479,7 @@ export class PuppetWeChat extends Puppet { * TODO: Test this function if it could work... */ // public async forward(baseData: MsgRawObj, patchData: MsgRawObj): Promise { - public async messageForward ( + public override async messageForward ( conversationId : string, messageId : string, ): Promise { @@ -537,7 +547,7 @@ export class PuppetWeChat extends Puppet { throw new Error('forward failed') } } catch (e) { - log.error('PuppetWeChat', 'forward() exception: %s', e.message) + log.error('PuppetWeChat', 'forward() exception: %s', (e as Error).message) throw e } } @@ -551,19 +561,19 @@ export class PuppetWeChat extends Puppet { try { await this.bridge.send(conversationId, text) } catch (e) { - log.error('PuppetWeChat', 'messageSendText() exception: %s', e.message) + log.error('PuppetWeChat', 'messageSendText() exception: %s', (e as Error).message) throw e } } - public async login (userId: string): Promise { + public override async login (userId: string): Promise { return super.login(userId) } /** * logout from browser, then server will emit `logout` event */ - public async logout (): Promise { + public override async logout (): Promise { log.verbose('PuppetWeChat', 'logout()') const user = this.selfId() @@ -575,7 +585,7 @@ export class PuppetWeChat extends Puppet { try { await this.bridge.logout() } catch (e) { - log.error('PuppetWeChat', 'logout() exception: %s', e.message) + log.error('PuppetWeChat', 'logout() exception: %s', (e as Error).message) throw e } finally { this.id = undefined @@ -612,7 +622,7 @@ export class PuppetWeChat extends Puppet { const rawPayload = await this.bridge.getContact(id) as WebContactRawPayload return rawPayload } catch (e) { - log.error('PuppetWeChat', 'contactRawPayload(%s) exception: %s', id, e.message) + log.error('PuppetWeChat', 'contactRawPayload(%s) exception: %s', id, (e as Error).message) throw e } @@ -652,6 +662,7 @@ export class PuppetWeChat extends Puppet { gender: rawPayload.Sex, id: rawPayload.UserName, name: plainText(rawPayload.NickName || ''), + phone: [], province: rawPayload.Province, signature: rawPayload.Signature, star: !!rawPayload.StarFriend, @@ -663,7 +674,7 @@ export class PuppetWeChat extends Puppet { */ type: (!!rawPayload.UserName && !rawPayload.UserName.startsWith('@@') && !!(rawPayload.VerifyFlag & 8)) ? ContactType.Official - : ContactType.Personal, + : ContactType.Individual, weixin: rawPayload.Alias, // Wechat ID } } @@ -711,7 +722,7 @@ export class PuppetWeChat extends Puppet { ) } catch (err) { - log.warn('PuppeteerContact', 'avatar() exception: %s', err.stack) + log.warn('PuppeteerContact', 'avatar() exception: %s', (err as Error).stack) throw err } } @@ -745,7 +756,7 @@ export class PuppetWeChat extends Puppet { throw new Error('bridge.contactAlias fail') } } catch (e) { - log.warn('PuppetWeChat', 'contactRemark(%s, %s) rejected: %s', contactId, alias, e.message) + log.warn('PuppetWeChat', 'contactRemark(%s, %s) rejected: %s', contactId, alias, (e as Error).message) throw e } } @@ -817,7 +828,7 @@ export class PuppetWeChat extends Puppet { throw new Error('no payload') } catch (e) { - log.error('PuppetWeChat', 'roomRawPayload(%s) exception: %s', id, e.message) + log.error('PuppetWeChat', 'roomRawPayload(%s) exception: %s', id, (e as Error).message) throw e } } @@ -890,7 +901,7 @@ export class PuppetWeChat extends Puppet { try { await this.bridge.roomDelMember(roomId, contactId) } catch (e) { - log.warn('PuppetWeChat', 'roomDelMember(%s, %d) rejected: %s', roomId, contactId, e.message) + log.warn('PuppetWeChat', 'roomDelMember(%s, %d) rejected: %s', roomId, contactId, (e as Error).message) throw e } } @@ -914,7 +925,7 @@ export class PuppetWeChat extends Puppet { try { await this.bridge.roomAddMember(roomId, contactId) } catch (e) { - log.warn('PuppetWeChat', 'roomAddMember(%s) rejected: %s', contactId, e.message) + log.warn('PuppetWeChat', 'roomAddMember(%s) rejected: %s', contactId, (e as Error).message) throw e } } @@ -934,7 +945,7 @@ export class PuppetWeChat extends Puppet { try { await this.bridge.roomModTopic(roomId, topic) } catch (e) { - log.warn('PuppetWeChat', 'roomTopic(%s) rejected: %s', topic, e.message) + log.warn('PuppetWeChat', 'roomTopic(%s) rejected: %s', topic, (e as Error).message) throw e } } @@ -951,7 +962,7 @@ export class PuppetWeChat extends Puppet { return roomId } catch (e) { - log.warn('PuppetWeChat', 'roomCreate(%s, %s) rejected: %s', contactIdList.join(','), topic, e.message) + log.warn('PuppetWeChat', 'roomCreate(%s, %s) rejected: %s', contactIdList.join(','), topic, (e as Error).message) throw e } } @@ -994,7 +1005,7 @@ export class PuppetWeChat extends Puppet { const memberPayloadResult = memberPayloadList.filter(payload => payload.UserName === contactId) if (memberPayloadResult.length > 0) { - return memberPayloadResult[0] + return memberPayloadResult[0]! } else { throw new Error('not found') } @@ -1102,7 +1113,7 @@ export class PuppetWeChat extends Puppet { log.warn('PuppetWeChat', 'friendshipAdd() bridge.verifyUserRequest(%s, %s) rejected: %s', contactId, hello, - e.message, + (e as Error).message, ) throw e } @@ -1119,7 +1130,7 @@ export class PuppetWeChat extends Puppet { log.warn('PuppetWeChat', 'bridge.verifyUserOk(%s, %s) rejected: %s', payload.contactId, payload.ticket, - e.message, + (e as Error).message, ) throw e } @@ -1181,8 +1192,10 @@ export class PuppetWeChat extends Puppet { } return name } catch (e) { - log.error('PuppetWeChat', 'hostname() exception:%s', e) - this.emit('error', e) + log.error('PuppetWeChat', 'hostname() exception:%s', e as Error) + this.emit('error', { + data: (e as Error).message, + }) throw e } } @@ -1266,7 +1279,7 @@ export class PuppetWeChat extends Puppet { default: { const e = new Error('ready() unsupported typeApp(): ' + rawPayload.AppMsgType) - log.warn('PuppeteerMessage', e.message) + log.warn('PuppeteerMessage', (e as Error).message) throw e } } @@ -1302,7 +1315,7 @@ export class PuppetWeChat extends Puppet { } } catch (e) { - log.warn('PuppetWeChat', 'ready() exception: %s', e.message) + log.warn('PuppetWeChat', 'ready() exception: %s', (e as Error).message) throw e } @@ -1341,10 +1354,11 @@ export class PuppetWeChat extends Puppet { } const buffer = await new Promise((resolve, reject) => { - file.pipe(new BufferList((err: Error, data: Buffer) => { + const bl = new BufferList((err: Error, data: Buffer) => { if (err) reject(err) else resolve(data) - })) + }) + file.pipe(bl) }) // Sending video files is not allowed to exceed 20MB @@ -1443,8 +1457,10 @@ export class PuppetWeChat extends Puppet { obj = JSON.parse(body) } catch (e) { log.error('PuppetWeChat', 'updateMedia() body = %s', body) - log.error('PuppetWeChat', 'updateMedia() exception: %s', e) - this.emit('error', e) + log.error('PuppetWeChat', 'updateMedia() exception: %s', e as Error) + this.emit('error', { + data: (e as Error).message, + }) } } if (typeof obj !== 'object' || obj.BaseResponse.Ret !== 0) { @@ -1464,7 +1480,7 @@ export class PuppetWeChat extends Puppet { }) }) } catch (e) { - log.error('PuppetWeChat', 'uploadMedia() checkUpload exception: %s', e.message) + log.error('PuppetWeChat', 'uploadMedia() checkUpload exception: %s', (e as Error).message) throw e } if (!ret.Signature) { @@ -1543,13 +1559,13 @@ export class PuppetWeChat extends Puppet { } }) } catch (e) { - log.error('PuppetWeChat', 'uploadMedia() uploadMedia exception: %s', e.message) - throw new Error('uploadMedia err: ' + e.message) + log.error('PuppetWeChat', 'uploadMedia() uploadMedia exception: %s', (e as Error).message) + throw new Error('uploadMedia err: ' + (e as any).message) } } let mediaId = '' for (let i = 0; i < bufferData.length; i++) { - mediaId = await getMediaId(bufferData[i], i) + mediaId = await getMediaId(bufferData[i]!, i) } if (!mediaId) { log.error('PuppetWeChat', 'uploadMedia(): upload fail') @@ -1576,7 +1592,7 @@ export class PuppetWeChat extends Puppet { rawPayload = Object.assign(rawPayload, mediaData) log.silly('PuppetWeChat', 'Upload completed, new rawObj:%s', JSON.stringify(rawPayload)) } catch (e) { - log.error('PuppetWeChat', 'sendMedia() exception: %s', e.message) + log.error('PuppetWeChat', 'sendMedia() exception: %s', (e as Error).message) throw e } } else { @@ -1607,7 +1623,7 @@ export class PuppetWeChat extends Puppet { try { ret = await this.bridge.sendMedia(mediaData) } catch (e) { - log.error('PuppetWeChat', 'sendMedia() exception: %s', e.message) + log.error('PuppetWeChat', 'sendMedia() exception: %s', (e as Error).message) throw e } if (!ret) { @@ -1654,6 +1670,25 @@ export class PuppetWeChat extends Puppet { return throwUnsupportedError(contactId) } + override async contactCorporationRemark (contactId: string, corporationRemark: string | null) { + return throwUnsupportedError(contactId, corporationRemark) + } + + override async contactDescription (contactId: string, description: string | null) { + return throwUnsupportedError(contactId, description) + } + + override async contactPhone (contactId: string, phoneList: string[]): Promise { + return throwUnsupportedError(contactId, phoneList) + } + + override conversationReadMark ( + conversationId: string, + hasRead = true, + ) : Promise { + return throwUnsupportedError(conversationId, hasRead) + } + } export default PuppetWeChat diff --git a/src/pure-function-helpers/message-extname.ts b/src/pure-function-helpers/message-extname.ts index 3c0bf04..ff9b59f 100644 --- a/src/pure-function-helpers/message-extname.ts +++ b/src/pure-function-helpers/message-extname.ts @@ -2,7 +2,7 @@ import { WebAppMsgType, WebMessageRawPayload, WebMessageType, -} from '../web-schemas' +} from '../web-schemas.js' export function messageExtname ( rawPayload: WebMessageRawPayload, diff --git a/src/pure-function-helpers/message-filename.ts b/src/pure-function-helpers/message-filename.ts index 5d3ed73..75195c4 100644 --- a/src/pure-function-helpers/message-filename.ts +++ b/src/pure-function-helpers/message-filename.ts @@ -1,10 +1,10 @@ -import { +import type { WebMessageRawPayload, -} from '../web-schemas' +} from '../web-schemas.js' import { messageExtname, -} from './message-extname' +} from './message-extname.js' export function messageFilename ( rawPayload: WebMessageRawPayload, diff --git a/src/pure-function-helpers/message-raw-payload-parser.ts b/src/pure-function-helpers/message-raw-payload-parser.ts index 32a61b1..8e4335c 100644 --- a/src/pure-function-helpers/message-raw-payload-parser.ts +++ b/src/pure-function-helpers/message-raw-payload-parser.ts @@ -1,21 +1,22 @@ -import { - WebMessageRawPayload, -} from '../web-schemas' - -import { +import type { MessagePayload, MessageType, } from 'wechaty-puppet' + +import type { + WebMessageRawPayload, +} from '../web-schemas.js' + import { isRoomId, -} from './is-type' +} from './is-type.js' import { messageFilename, -} from './message-filename' +} from './message-filename.js' import { webMessageType, -} from './web-message-type' +} from './web-message-type.js' export function messageRawPayloadParser ( rawPayload: WebMessageRawPayload, diff --git a/src/pure-function-helpers/mod.ts b/src/pure-function-helpers/mod.ts index 575578b..3a7654e 100644 --- a/src/pure-function-helpers/mod.ts +++ b/src/pure-function-helpers/mod.ts @@ -1,6 +1,7 @@ -export * from './is-type' -export * from './message-extname' -export * from './message-filename' -export * from './message-raw-payload-parser' -export * from './web-message-type' -export * from './xml' +export * from './is-type.js' +export * from './message-extname.js' +export * from './message-filename.js' +export * from './message-raw-payload-parser.js' +export * from './web-message-type.js' +export * from './xml.js' +export * from './retry-policy.js' diff --git a/src/pure-function-helpers/normalize-scan-status.spec.ts b/src/pure-function-helpers/normalize-scan-status.spec.ts index 797599b..a0ab4b7 100755 --- a/src/pure-function-helpers/normalize-scan-status.spec.ts +++ b/src/pure-function-helpers/normalize-scan-status.spec.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env ts-node +#!/usr/bin/env node --no-warnings --loader ts-node/esm /** * Wechaty - https://github.com/chatie/wechaty @@ -27,7 +27,7 @@ import { ScanStatus, } from 'wechaty-puppet' -import { normalizeScanStatus } from './normalize-scan-status' +import { normalizeScanStatus } from './normalize-scan-status.js' test('normalizeScanStatus()', async t => { const SCAN_STATUS_LIST = [ @@ -38,7 +38,7 @@ test('normalizeScanStatus()', async t => { ] for (const [puppeteerStatus, EXPECT_PUPPET_STATUS] of SCAN_STATUS_LIST) { - const puppetStatus = normalizeScanStatus(puppeteerStatus) - t.is(puppetStatus, EXPECT_PUPPET_STATUS, `should convert status code from puppeer(${puppeteerStatus}) to puppet(${EXPECT_PUPPET_STATUS})`) + const puppetStatus = normalizeScanStatus(puppeteerStatus!) + t.equal(puppetStatus, EXPECT_PUPPET_STATUS, `should convert status code from puppeer(${puppeteerStatus}) to puppet(${EXPECT_PUPPET_STATUS})`) } }) diff --git a/src/pure-function-helpers/retry-policy.ts b/src/pure-function-helpers/retry-policy.ts new file mode 100644 index 0000000..54b668c --- /dev/null +++ b/src/pure-function-helpers/retry-policy.ts @@ -0,0 +1,45 @@ +import { + Policy, + RetryPolicy, +} from 'cockatiel' +import { + log, +} from 'wechaty-puppet' + +function getRetryPolicy (): RetryPolicy { + const policy = Policy + .handleAll() + .retry() + .attempts(10) + .exponential({ + /** + * ExponentialBackoff + * https://github.com/connor4312/cockatiel#exponentialbackoff + */ + initialDelay : 1000, + maxAttempts : 10, + maxDelay : 10 * 1000, + }) + + policy.onRetry(reason => log.silly('wechaty', + 'retry-policy getRetryPolicy policy.onRetry() reason: "%s"', + JSON.stringify(reason), + )) + policy.onSuccess(({ duration }) => log.silly('wechaty', + 'retry-policy getRetryPolicy policy.onSuccess(): retry call ran in %s ms', + duration, + )) + return policy +} + +/** + * Create a retry policy that'll try whatever function we execute 3 + * times with a randomized exponential backoff. + * + * https://github.com/connor4312/cockatiel#policyretry + */ +const retryPolicy = getRetryPolicy() + +export { + retryPolicy, +} diff --git a/src/pure-function-helpers/web-message-type.ts b/src/pure-function-helpers/web-message-type.ts index 8c224f9..071e9a0 100644 --- a/src/pure-function-helpers/web-message-type.ts +++ b/src/pure-function-helpers/web-message-type.ts @@ -2,7 +2,7 @@ import { WebAppMsgType, WebMessageRawPayload, WebMessageType, -} from '../web-schemas' +} from '../web-schemas.js' import { MessageType, diff --git a/src/version.spec.ts b/src/version.spec.ts deleted file mode 100755 index 127e603..0000000 --- a/src/version.spec.ts +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env ts-node - -// tslint:disable:no-shadowed-variable -import test from 'blue-tape' - -import { VERSION } from './version' - -test('Make sure the VERSION is fresh in source code', async (t) => { - t.equal(VERSION, '0.0.0', 'version should be 0.0.0 in source code, only updated before publish to NPM') -}) diff --git a/src/version.ts b/src/version.ts deleted file mode 100644 index c36f2c5..0000000 --- a/src/version.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * This file will be overwrite when we publish NPM module - * by scripts/generate_version.ts - */ - -export const VERSION = '0.0.0' diff --git a/src/web-schemas.ts b/src/web-schemas.ts index 21f6c95..257a5b9 100644 --- a/src/web-schemas.ts +++ b/src/web-schemas.ts @@ -16,9 +16,6 @@ * limitations under the License. * */ - -// tslint:disable:max-line-length - export interface WebContactRawPayload { Alias: string, City: string, @@ -48,6 +45,118 @@ export interface WebMessageMediaPayload { Signature?: string, } +/** + * from Message + */ +export interface WebRecomendInfo { + UserName : string, + NickName : string, // display_name + Content : string, // request message + HeadImgUrl : string, // message.RecommendInfo.HeadImgUrl + + Ticket : string, // a pass token + VerifyFlag : number, +} + +/** + * + * Enum for MsgType values. + * @enum {number} + * @property {number} TEXT - MsgType.TEXT (1) for TEXT + * @property {number} IMAGE - MsgType.IMAGE (3) for IMAGE + * @property {number} VOICE - MsgType.VOICE (34) for VOICE + * @property {number} VERIFYMSG - MsgType.VERIFYMSG (37) for VERIFYMSG + * @property {number} POSSIBLEFRIEND_MSG - MsgType.POSSIBLEFRIEND_MSG (40) for POSSIBLEFRIEND_MSG + * @property {number} SHARECARD - MsgType.SHARECARD (42) for SHARECARD + * @property {number} VIDEO - MsgType.VIDEO (43) for VIDEO + * @property {number} EMOTICON - MsgType.EMOTICON (47) for EMOTICON + * @property {number} LOCATION - MsgType.LOCATION (48) for LOCATION + * @property {number} APP - MsgType.APP (49) for APP + * @property {number} VOIPMSG - MsgType.VOIPMSG (50) for VOIPMSG + * @property {number} STATUSNOTIFY - MsgType.STATUSNOTIFY (51) for STATUSNOTIFY + * @property {number} VOIPNOTIFY - MsgType.VOIPNOTIFY (52) for VOIPNOTIFY + * @property {number} VOIPINVITE - MsgType.VOIPINVITE (53) for VOIPINVITE + * @property {number} MICROVIDEO - MsgType.MICROVIDEO (62) for MICROVIDEO + * @property {number} SYSNOTICE - MsgType.SYSNOTICE (9999) for SYSNOTICE + * @property {number} SYS - MsgType.SYS (10000) for SYS + * @property {number} RECALLED - MsgType.RECALLED (10002) for RECALLED + */ +export enum WebMessageType { + TEXT = 1, + IMAGE = 3, + VOICE = 34, + VERIFYMSG = 37, + POSSIBLEFRIEND_MSG = 40, + SHARECARD = 42, + VIDEO = 43, + EMOTICON = 47, + LOCATION = 48, + APP = 49, + VOIPMSG = 50, + STATUSNOTIFY = 51, + VOIPNOTIFY = 52, + VOIPINVITE = 53, + MICROVIDEO = 62, + SYSNOTICE = 9999, + SYS = 10000, + RECALLED = 10002, +} + +// export type MessageTypeName = 'TEXT' | 'IMAGE' | 'VOICE' | 'VERIFYMSG' | 'POSSIBLEFRIEND_MSG' +// | 'SHARECARD' | 'VIDEO' | 'EMOTICON' | 'LOCATION' | 'APP' | 'VOIPMSG' | 'STATUSNOTIFY' +// | 'VOIPNOTIFY' | 'VOIPINVITE' | 'MICROVIDEO' | 'SYSNOTICE' | 'SYS' | 'RECALLED' + +// export type MessageTypeValue = 1 | 3 | 34 | 37 | 40 | 42 | 43 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 62 | 9999 | 10000 | 10002 + +// export interface WebMsgTypeDict { +// [index: string]: string|number, +// // MessageTypeName: MessageTypeValue +// // , MessageTypeValue: MessageTypeName +// } + +/** + * + * Enum for AppMsgType values. + * + * @enum {number} + * @property {number} TEXT - AppMsgType.TEXT (1) for TEXT + * @property {number} IMG - AppMsgType.IMG (2) for IMG + * @property {number} AUDIO - AppMsgType.AUDIO (3) for AUDIO + * @property {number} VIDEO - AppMsgType.VIDEO (4) for VIDEO + * @property {number} URL - AppMsgType.URL (5) for URL + * @property {number} ATTACH - AppMsgType.ATTACH (6) for ATTACH + * @property {number} OPEN - AppMsgType.OPEN (7) for OPEN + * @property {number} EMOJI - AppMsgType.EMOJI (8) for EMOJI + * @property {number} VOICE_REMIND - AppMsgType.VOICE_REMIND (9) for VOICE_REMIND + * @property {number} SCAN_GOOD - AppMsgType.SCAN_GOOD (10) for SCAN_GOOD + * @property {number} GOOD - AppMsgType.GOOD (13) for GOOD + * @property {number} EMOTION - AppMsgType.EMOTION (15) for EMOTION + * @property {number} CARD_TICKET - AppMsgType.CARD_TICKET (16) for CARD_TICKET + * @property {number} REALTIME_SHARE_LOCATION - AppMsgType.REALTIME_SHARE_LOCATION (17) for REALTIME_SHARE_LOCATION + * @property {number} TRANSFERS - AppMsgType.TRANSFERS (2e3) for TRANSFERS + * @property {number} RED_ENVELOPES - AppMsgType.RED_ENVELOPES (2001) for RED_ENVELOPES + * @property {number} READER_TYPE - AppMsgType.READER_TYPE (100001) for READER_TYPE + */ +export enum WebAppMsgType { + TEXT = 1, + IMG = 2, + AUDIO = 3, + VIDEO = 4, + URL = 5, + ATTACH = 6, + OPEN = 7, + EMOJI = 8, + VOICE_REMIND = 9, + SCAN_GOOD = 10, + GOOD = 13, + EMOTION = 15, + CARD_TICKET = 16, + REALTIME_SHARE_LOCATION = 17, + TRANSFERS = 2e3, + RED_ENVELOPES = 2001, + READER_TYPE = 100001, +} + export interface WebMessageRawPayload { MsgId: string, @@ -162,118 +271,6 @@ export interface WebMessageRawPayload { } -// export type MessageTypeName = 'TEXT' | 'IMAGE' | 'VOICE' | 'VERIFYMSG' | 'POSSIBLEFRIEND_MSG' -// | 'SHARECARD' | 'VIDEO' | 'EMOTICON' | 'LOCATION' | 'APP' | 'VOIPMSG' | 'STATUSNOTIFY' -// | 'VOIPNOTIFY' | 'VOIPINVITE' | 'MICROVIDEO' | 'SYSNOTICE' | 'SYS' | 'RECALLED' - -// export type MessageTypeValue = 1 | 3 | 34 | 37 | 40 | 42 | 43 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 62 | 9999 | 10000 | 10002 - -// export interface WebMsgTypeDict { -// [index: string]: string|number, -// // MessageTypeName: MessageTypeValue -// // , MessageTypeValue: MessageTypeName -// } - -/** - * - * Enum for AppMsgType values. - * - * @enum {number} - * @property {number} TEXT - AppMsgType.TEXT (1) for TEXT - * @property {number} IMG - AppMsgType.IMG (2) for IMG - * @property {number} AUDIO - AppMsgType.AUDIO (3) for AUDIO - * @property {number} VIDEO - AppMsgType.VIDEO (4) for VIDEO - * @property {number} URL - AppMsgType.URL (5) for URL - * @property {number} ATTACH - AppMsgType.ATTACH (6) for ATTACH - * @property {number} OPEN - AppMsgType.OPEN (7) for OPEN - * @property {number} EMOJI - AppMsgType.EMOJI (8) for EMOJI - * @property {number} VOICE_REMIND - AppMsgType.VOICE_REMIND (9) for VOICE_REMIND - * @property {number} SCAN_GOOD - AppMsgType.SCAN_GOOD (10) for SCAN_GOOD - * @property {number} GOOD - AppMsgType.GOOD (13) for GOOD - * @property {number} EMOTION - AppMsgType.EMOTION (15) for EMOTION - * @property {number} CARD_TICKET - AppMsgType.CARD_TICKET (16) for CARD_TICKET - * @property {number} REALTIME_SHARE_LOCATION - AppMsgType.REALTIME_SHARE_LOCATION (17) for REALTIME_SHARE_LOCATION - * @property {number} TRANSFERS - AppMsgType.TRANSFERS (2e3) for TRANSFERS - * @property {number} RED_ENVELOPES - AppMsgType.RED_ENVELOPES (2001) for RED_ENVELOPES - * @property {number} READER_TYPE - AppMsgType.READER_TYPE (100001) for READER_TYPE - */ -export enum WebAppMsgType { - TEXT = 1, - IMG = 2, - AUDIO = 3, - VIDEO = 4, - URL = 5, - ATTACH = 6, - OPEN = 7, - EMOJI = 8, - VOICE_REMIND = 9, - SCAN_GOOD = 10, - GOOD = 13, - EMOTION = 15, - CARD_TICKET = 16, - REALTIME_SHARE_LOCATION = 17, - TRANSFERS = 2e3, - RED_ENVELOPES = 2001, - READER_TYPE = 100001, -} - -/** - * - * Enum for MsgType values. - * @enum {number} - * @property {number} TEXT - MsgType.TEXT (1) for TEXT - * @property {number} IMAGE - MsgType.IMAGE (3) for IMAGE - * @property {number} VOICE - MsgType.VOICE (34) for VOICE - * @property {number} VERIFYMSG - MsgType.VERIFYMSG (37) for VERIFYMSG - * @property {number} POSSIBLEFRIEND_MSG - MsgType.POSSIBLEFRIEND_MSG (40) for POSSIBLEFRIEND_MSG - * @property {number} SHARECARD - MsgType.SHARECARD (42) for SHARECARD - * @property {number} VIDEO - MsgType.VIDEO (43) for VIDEO - * @property {number} EMOTICON - MsgType.EMOTICON (47) for EMOTICON - * @property {number} LOCATION - MsgType.LOCATION (48) for LOCATION - * @property {number} APP - MsgType.APP (49) for APP - * @property {number} VOIPMSG - MsgType.VOIPMSG (50) for VOIPMSG - * @property {number} STATUSNOTIFY - MsgType.STATUSNOTIFY (51) for STATUSNOTIFY - * @property {number} VOIPNOTIFY - MsgType.VOIPNOTIFY (52) for VOIPNOTIFY - * @property {number} VOIPINVITE - MsgType.VOIPINVITE (53) for VOIPINVITE - * @property {number} MICROVIDEO - MsgType.MICROVIDEO (62) for MICROVIDEO - * @property {number} SYSNOTICE - MsgType.SYSNOTICE (9999) for SYSNOTICE - * @property {number} SYS - MsgType.SYS (10000) for SYS - * @property {number} RECALLED - MsgType.RECALLED (10002) for RECALLED - */ -export enum WebMessageType { - TEXT = 1, - IMAGE = 3, - VOICE = 34, - VERIFYMSG = 37, - POSSIBLEFRIEND_MSG = 40, - SHARECARD = 42, - VIDEO = 43, - EMOTICON = 47, - LOCATION = 48, - APP = 49, - VOIPMSG = 50, - STATUSNOTIFY = 51, - VOIPNOTIFY = 52, - VOIPINVITE = 53, - MICROVIDEO = 62, - SYSNOTICE = 9999, - SYS = 10000, - RECALLED = 10002, -} - -/** - * from Message - */ -export interface WebRecomendInfo { - UserName : string, - NickName : string, // display_name - Content : string, // request message - HeadImgUrl : string, // message.RecommendInfo.HeadImgUrl - - Ticket : string, // a pass token - VerifyFlag : number, -} - export const enum WebMediaType { Image = 'pic', Video = 'video', diff --git a/src/wechaty-bro.js b/src/wechaty-bro.js index 21b5270..be91f50 100644 --- a/src/wechaty-bro.js +++ b/src/wechaty-bro.js @@ -702,12 +702,13 @@ throw new Error('chatroomFactory.create() got empty r.ChatRoomName') } resolve(r.ChatRoomName) + } else { - throw new Error('chatroomFactory.create() error with Ret: ' + reject(new Error('chatroomFactory.create() error with Ret: ' + r && r.BaseResponse.Ret + 'with ErrMsg: ' + r && r.BaseResponse.ErrMsg - ) + )) } }) .catch(function (e) { diff --git a/tests/fixtures/smoke-testing.ts b/tests/fixtures/smoke-testing.ts index 68d3545..10d4541 100644 --- a/tests/fixtures/smoke-testing.ts +++ b/tests/fixtures/smoke-testing.ts @@ -1,12 +1,4 @@ -#!/usr/bin/env ts-node - -// tslint:disable:arrow-parens -// tslint:disable:max-line-length -// tslint:disable:member-ordering -// tslint:disable:no-shadowed-variable -// tslint:disable:unified-signatures -// tslint:disable:no-console - +#!/usr/bin/env node --no-warnings --loader ts-node/esm import { PuppetWeChat, VERSION, @@ -16,10 +8,6 @@ import { log.level('verbose') async function main () { - if (VERSION === '0.0.0') { - throw new Error('VERSION should not be 0.0.0 when publishing') - } - const puppet = new PuppetWeChat() const future = new Promise(resolve => puppet.once('scan', resolve)) @@ -30,6 +18,10 @@ async function main () { await puppet.stop() + if (VERSION === '0.0.0') { + throw new Error('VERSION should not be 0.0.0 when publishing') + } + log.info('SmokeTesting', `Puppet v${puppet.version()} smoke testing passed.`) return 0 } diff --git a/tests/puppeteer-contact.spec.ts b/tests/puppeteer-contact.spec.ts index 21da5ab..19b522a 100755 --- a/tests/puppeteer-contact.spec.ts +++ b/tests/puppeteer-contact.spec.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env ts-node +#!/usr/bin/env node --no-warnings --loader ts-node/esm /** * Wechaty - https://github.com/chatie/wechaty @@ -18,23 +18,21 @@ * limitations under the License. * */ -// tslint:disable:arrow-parens -// tslint:disable:no-shadowed-variable - -import { test, sinon } from 'tstest' +import { + test, + sinon, +} from 'tstest' // import { // cloneClass, // } from 'clone-class' import { log, -} from '../src/config' +} from '../src/config.js' -import PuppetWeChat from '../src/puppet-wechat' +import PuppetWeChat from '../src/puppet-wechat.js' test('Contact smoke testing', async t => { - - /* tslint:disable:variable-name */ const UserName = '@0bb3e4dd746fdbd4a80546aef66f4085' const NickName = 'NickNameTest' const RemarkName = 'AliasTest' @@ -58,15 +56,14 @@ test('Contact smoke testing', async t => { const contactPayload = await puppet.contactPayload(UserName) - // tslint:disable-next-line:variable-name // const MyContact = cloneClass(Contact) // MyContact.puppet = puppet as any // FIXME: any // const c = new MyContact(UserName) - t.is(contactPayload.id, UserName, 'id/UserName right') + t.equal(contactPayload.id, UserName, 'id/UserName right') - t.is(contactPayload.name, NickName, 'NickName set') - t.is(contactPayload.alias, RemarkName, 'should get the right alias from Contact') + t.equal(contactPayload.name, NickName, 'NickName set') + t.equal(contactPayload.alias, RemarkName, 'should get the right alias from Contact') sandbox.restore() diff --git a/tests/puppeteer-friendship.spec.ts b/tests/puppeteer-friendship.spec.ts index f7ef1ea..891561d 100755 --- a/tests/puppeteer-friendship.spec.ts +++ b/tests/puppeteer-friendship.spec.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env ts-node +#!/usr/bin/env node --no-warnings --loader ts-node/esm /** * Wechaty - https://github.com/chatie/wechaty * @@ -30,10 +30,10 @@ import { import { PuppetWeChat, -} from '../src/puppet-wechat' -import { +} from '../src/puppet-wechat.js' +import type { WebMessageRawPayload, -} from '../src/web-schemas' +} from '../src/web-schemas.js' // class WechatyTest extends Wechaty { // public initPuppetAccessory (puppet: PuppetWeChat) { @@ -43,15 +43,15 @@ import { class PuppetTest extends PuppetWeChat { - public contactRawPayload (id: string) { + public override contactRawPayload (id: string) { return super.contactRawPayload(id) } - public roomRawPayload (id: string) { + public override roomRawPayload (id: string) { return super.roomRawPayload(id) } - public messageRawPayload (id: string) { + public override messageRawPayload (id: string) { return super.messageRawPayload(id) } @@ -62,7 +62,6 @@ test('PuppetWeChatFriendship.receive smoke testing', async (t) => { // const wechaty = new WechatyTest({ puppet }) // wechaty.initPuppetAccessory(puppet) - /* tslint:disable:max-line-length */ const rawMessagePayload: WebMessageRawPayload = JSON.parse(` {"MsgId":"3225371967511173931","FromUserName":"fmessage","ToUserName":"@f7321198e0349f1b38c9f2ef158f70eb","MsgType":37,"Content":"<msg fromusername=\\"wxid_a8d806dzznm822\\" encryptusername=\\"v1_c1e03a32c60dd9a9e14f1092132808a2de0ad363f79b303693654282954fbe4d3e12481166f4b841f28de3dd58b0bd54@stranger\\" fromnickname=\\"李卓桓.PreAngel\\" content=\\"我是群聊&quot;Wechaty&quot;的李卓桓.PreAngel\\" shortpy=\\"LZHPREANGEL\\" imagestatus=\\"3\\" scene=\\"14\\" country=\\"CN\\" province=\\"Beijing\\" city=\\"Haidian\\" sign=\\"投资人中最会飞的程序员。好友请加 918999 ,因为本号好友已满。\\" percard=\\"1\\" sex=\\"1\\" alias=\\"zixia008\\" weibo=\\"\\" weibonickname=\\"\\" albumflag=\\"0\\" albumstyle=\\"0\\" albumbgimgid=\\"911623988445184_911623988445184\\" snsflag=\\"49\\" snsbgimgid=\\"http://mmsns.qpic.cn/mmsns/zZSYtpeVianSQYekFNbuiajROicLficBzzeGuvQjnWdGDZ4budZovamibQnoKWba7D2LeuQRPffS8aeE/0\\" snsbgobjectid=\\"12183966160653848744\\" mhash=\\"\\" mfullhash=\\"\\" bigheadimgurl=\\"http://wx.qlogo.cn/mmhead/ver_1/xct7OPTbuU6iaS8gTaK2VibhRs3rATwnU1rCUwWu8ic89EGOynaic2Y4MUdKr66khhAplcfFlm7xbXhum5reania3fXDXH6CI9c3Bb4BODmYAh04/0\\" smallheadimgurl=\\"http://wx.qlogo.cn/mmhead/ver_1/xct7OPTbuU6iaS8gTaK2VibhRs3rATwnU1rCUwWu8ic89EGOynaic2Y4MUdKr66khhAplcfFlm7xbXhum5reania3fXDXH6CI9c3Bb4BODmYAh04/132\\" ticket=\\"v2_ba70dfbdb1b10168d61c1ab491be19e219db11ed5c28701f605efb4dccbf132f664d8a4c9ef6e852b2a4e8d8638be81d125c2e641f01903669539c53f1e582b2@stranger\\" opcode=\\"2\\" googlecontact=\\"\\" qrticket=\\"\\" chatroomusername=\\"2332413729@chatroom\\" sourceusername=\\"\\" sourcenickname=\\"\\"><brandlist count=\\"0\\" ver=\\"670564024\\"></brandlist></msg>","Status":3,"ImgStatus":1,"CreateTime":1475567560,"VoiceLength":0,"PlayLength":0,"FileName":"","FileSize":"","MediaId":"","Url":"","AppMsgType":0,"StatusNotifyCode":0,"StatusNotifyUserName":"","RecommendInfo":{"UserName":"@04a0fa314d0d8d50dc54e2ec908744ebf46b87404d143fd9a6692182dd90bd49","NickName":"李卓桓.PreAngel","Province":"北京","City":"海淀","Content":"我是群聊\\"Wechaty\\"的李卓桓.PreAngel","Signature":"投资人中最会飞的程序员。好友请加 918999 ,因为本号好友已满。","Alias":"zixia008","Scene":14,"AttrStatus":233251,"Sex":1,"Ticket":"v2_ba70dfbdb1b10168d61c1ab491be19e219db11ed5c28701f605efb4dccbf132f664d8a4c9ef6e852b2a4e8d8638be81d125c2e641f01903669539c53f1e582b2@stranger","OpCode":2,"HeadImgUrl":"/cgi-bin/mmwebwx-bin/webwxgeticon?seq=0&username=@04a0fa314d0d8d50dc54e2ec908744ebf46b87404d143fd9a6692182dd90bd49&skey=@crypt_f9cec94b_5b073dca472bd5e41771d309bb8c37bd&msgid=3225371967511173931","MMFromVerifyMsg":true},"ForwardFlag":0,"AppInfo":{"AppID":"","Type":0},"HasProductId":0,"Ticket":"","ImgHeight":0,"ImgWidth":0,"SubMsgType":0,"NewMsgId":3225371967511174000,"MMPeerUserName":"fmessage","MMDigest":"李卓桓.PreAngel想要将你加为朋友","MMIsSend":false,"MMIsChatRoom":false,"MMUnread":true,"LocalID":"3225371967511173931","ClientMsgId":"3225371967511173931","MMActualContent":"<msg fromusername=\\"wxid_a8d806dzznm822\\" encryptusername=\\"v1_c1e03a32c60dd9a9e14f1092132808a2de0ad363f79b303693654282954fbe4d3e12481166f4b841f28de3dd58b0bd54@stranger\\" fromnickname=\\"李卓桓.PreAngel\\" content=\\"我是群聊&quot;Wechaty&quot;的李卓桓.PreAngel\\" shortpy=\\"LZHPREANGEL\\" imagestatus=\\"3\\" scene=\\"14\\" country=\\"CN\\" province=\\"Beijing\\" city=\\"Haidian\\" sign=\\"投资人中最会飞的程序员。好友请加 918999 ,因为本号好友已满。\\" percard=\\"1\\" sex=\\"1\\" alias=\\"zixia008\\" weibo=\\"\\" weibonickname=\\"\\" albumflag=\\"0\\" albumstyle=\\"0\\" albumbgimgid=\\"911623988445184_911623988445184\\" snsflag=\\"49\\" snsbgimgid=\\"http://mmsns.qpic.cn/mmsns/zZSYtpeVianSQYekFNbuiajROicLficBzzeGuvQjnWdGDZ4budZovamibQnoKWba7D2LeuQRPffS8aeE/0\\" snsbgobjectid=\\"12183966160653848744\\" mhash=\\"\\" mfullhash=\\"\\" bigheadimgurl=\\"http://wx.qlogo.cn/mmhead/ver_1/xct7OPTbuU6iaS8gTaK2VibhRs3rATwnU1rCUwWu8ic89EGOynaic2Y4MUdKr66khhAplcfFlm7xbXhum5reania3fXDXH6CI9c3Bb4BODmYAh04/0\\" smallheadimgurl=\\"http://wx.qlogo.cn/mmhead/ver_1/xct7OPTbuU6iaS8gTaK2VibhRs3rATwnU1rCUwWu8ic89EGOynaic2Y4MUdKr66khhAplcfFlm7xbXhum5reania3fXDXH6CI9c3Bb4BODmYAh04/132\\" ticket=\\"v2_ba70dfbdb1b10168d61c1ab491be19e219db11ed5c28701f605efb4dccbf132f664d8a4c9ef6e852b2a4e8d8638be81d125c2e641f01903669539c53f1e582b2@stranger\\" opcode=\\"2\\" googlecontact=\\"\\" qrticket=\\"\\" chatroomusername=\\"2332413729@chatroom\\" sourceusername=\\"\\" sourcenickname=\\"\\"><brandlist count=\\"0\\" ver=\\"670564024\\"></brandlist></msg>","MMActualSender":"fmessage","MMDigestTime":"15:52","MMDisplayTime":1475567560,"MMTime":"15:52"} `) @@ -113,7 +112,6 @@ test('PuppetWeChatFriendship.confirm smoke testing', async (t) => { // const wechaty = new WechatyTest({ puppet }) // wechaty.initPuppetAccessory(puppet) - /* tslint:disable:max-line-length */ const rawMessagePayload: WebMessageRawPayload = JSON.parse(` {"MsgId":"3382012679535022763","FromUserName":"@04a0fa314d0d8d50dc54e2ec908744ebf46b87404d143fd9a6692182dd90bd49","ToUserName":"@f7321198e0349f1b38c9f2ef158f70eb","MsgType":10000,"Content":"You have added 李卓桓.PreAngel as your WeChat contact. Start chatting!","Status":4,"ImgStatus":1,"CreateTime":1475569920,"VoiceLength":0,"PlayLength":0,"FileName":"","FileSize":"","MediaId":"","Url":"","AppMsgType":0,"StatusNotifyCode":0,"StatusNotifyUserName":"","RecommendInfo":{"UserName":"","NickName":"","QQNum":0,"Province":"","City":"","Content":"","Signature":"","Alias":"","Scene":0,"VerifyFlag":0,"AttrStatus":0,"Sex":0,"Ticket":"","OpCode":0},"ForwardFlag":0,"AppInfo":{"AppID":"","Type":0},"HasProductId":0,"Ticket":"","ImgHeight":0,"ImgWidth":0,"SubMsgType":0,"NewMsgId":3382012679535022600,"MMPeerUserName":"@04a0fa314d0d8d50dc54e2ec908744ebf46b87404d143fd9a6692182dd90bd49","MMDigest":"You have added 李卓桓.PreAngel as your WeChat contact. Start chatting!","MMIsSend":false,"MMIsChatRoom":false,"LocalID":"3382012679535022763","ClientMsgId":"3382012679535022763","MMActualContent":"You have added 李卓桓.PreAngel as your WeChat contact. Start chatting!","MMActualSender":"@04a0fa314d0d8d50dc54e2ec908744ebf46b87404d143fd9a6692182dd90bd49","MMDigestTime":"16:32","MMDisplayTime":1475569920,"MMTime":"16:32"} `) @@ -144,14 +142,14 @@ test('PuppetWeChatFriendship.confirm smoke testing', async (t) => { // await msg.ready() const msgPayload = await puppet.messagePayload(rawMessagePayload.MsgId) - t.true(/^You have added (.+) as your WeChat contact. Start chatting!$/.test(msgPayload.text || ''), 'should match confirm message') + t.ok(/^You have added (.+) as your WeChat contact. Start chatting!$/.test(msgPayload.text || ''), 'should match confirm message') // const fr = wechaty.Friendship.load('xx') // await fr.ready() const friendshipPayload2 = await puppet.friendshipPayload('xx') - t.is(friendshipPayload2.contactId, CONTACT_ID, 'should have a Contact id') - t.is(friendshipPayload2.type, FriendshipType.Confirm, 'should be confirm type') + t.equal(friendshipPayload2.contactId, CONTACT_ID, 'should have a Contact id') + t.equal(friendshipPayload2.type, FriendshipType.Confirm, 'should be confirm type') sandbox.restore() }) diff --git a/tests/puppeteer-message.spec.ts b/tests/puppeteer-message.spec.ts index 5eb6678..4366e6b 100755 --- a/tests/puppeteer-message.spec.ts +++ b/tests/puppeteer-message.spec.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env ts-node +#!/usr/bin/env node --no-warnings --loader ts-node/esm /** * Wechaty - https://github.com/chatie/wechaty * @@ -17,12 +17,6 @@ * limitations under the License. * */ - -// tslint:disable:no-shadowed-variable -// tslint:disable:max-classes-per-file -// tslint:disable:arrow-parens -// tslint:disable:ter-no-irregular-whitespace - import { test, sinon, @@ -31,7 +25,7 @@ import { import { // config, log, -} from '../src/config' +} from '../src/config.js' import { MessagePayload, @@ -40,11 +34,11 @@ import { import { PuppetWeChat, -} from '../src/puppet-wechat' -import { +} from '../src/puppet-wechat.js' +import type { WebMessageRawPayload, // WebRoomRawPayload, -} from '../src/web-schemas' +} from '../src/web-schemas.js' // class WechatyTest extends Wechaty { // public initPuppetAccessory (puppet: PuppetWeChat) { @@ -54,15 +48,15 @@ import { class PuppetTest extends PuppetWeChat { - public contactRawPayload (id: string) { + public override contactRawPayload (id: string) { return super.contactRawPayload(id) } - public roomRawPayload (id: string) { + public override roomRawPayload (id: string) { return super.roomRawPayload(id) } - public messageRawPayload (id: string) { + public override messageRawPayload (id: string) { return super.messageRawPayload(id) } @@ -79,7 +73,6 @@ test('constructor()', async t => { const MOCK_USER_ID = 'TEST-USER-ID' - /* tslint:disable:max-line-length */ const rawPayload: WebMessageRawPayload = JSON.parse('{"MsgId":"179242112323992762","FromUserName":"@0bb3e4dd746fdbd4a80546aef66f4085","ToUserName":"@16d20edf23a3bf3bc71bb4140e91619f3ff33b4e33f7fcd25e65c1b02c7861ab","MsgType":1,"Content":"test123","Status":3,"ImgStatus":1,"CreateTime":1461652670,"VoiceLength":0,"PlayLength":0,"FileName":"","FileSize":"","MediaId":"","Url":"","AppMsgType":0,"StatusNotifyCode":0,"StatusNotifyUserName":"","RecommendInfo":{"UserName":"","NickName":"","QQNum":0,"Province":"","City":"","Content":"","Signature":"","Alias":"","Scene":0,"VerifyFlag":0,"AttrStatus":0,"Sex":0,"Ticket":"","OpCode":0},"ForwardFlag":0,"AppInfo":{"AppID":"","Type":0},"HasProductId":0,"Ticket":"","ImgHeight":0,"ImgWidth":0,"SubMsgType":0,"NewMsgId":179242112323992770,"MMPeerUserName":"@0bb3e4dd746fdbd4a80546aef66f4085","MMDigest":"test123","MMIsSend":false,"MMIsChatRoom":false,"MMUnread":true,"LocalID":"179242112323992762","ClientMsgId":"179242112323992762","MMActualContent":"test123","MMActualSender":"@0bb3e4dd746fdbd4a80546aef66f4085","MMDigestTime":"14:37","MMDisplayTime":1461652670,"MMTime":"14:37"}') const EXPECTED = { @@ -112,8 +105,8 @@ test('constructor()', async t => { const msgPayload = await puppet.messagePayload(rawPayload.MsgId) - t.is(msgPayload.id, EXPECTED.id, 'id right') - t.is(msgPayload.fromId, EXPECTED.from, 'from right') + t.equal(msgPayload.id, EXPECTED.id, 'id right') + t.equal(msgPayload.fromId, EXPECTED.from, 'from right') sandbox.restore() }) @@ -123,7 +116,6 @@ test('constructor()', async t => { test('ready()', async t => { // must different with other rawData, because Contact class with load() will cache the result. or use Contact.resetPool() - /* tslint:disable:max-line-length */ const rawPayload: WebMessageRawPayload = JSON.parse('{"MsgId":"3009511950433684462","FromUserName":"@0748ee480711bf20af91c298a0d7dcc77c30a680c1004157386b81cf13474823","ToUserName":"@b58f91e0c5c9e841e290d862ddb63c14","MsgType":1,"Content":"哈哈","Status":3,"ImgStatus":1,"CreateTime":1462887888,"VoiceLength":0,"PlayLength":0,"FileName":"","FileSize":"","MediaId":"","Url":"","AppMsgType":0,"StatusNotifyCode":0,"StatusNotifyUserName":"","RecommendInfo":{"UserName":"","NickName":"","QQNum":0,"Province":"","City":"","Content":"","Signature":"","Alias":"","Scene":0,"VerifyFlag":0,"AttrStatus":0,"Sex":0,"Ticket":"","OpCode":0},"ForwardFlag":0,"AppInfo":{"AppID":"","Type":0},"HasProductId":0,"Ticket":"","ImgHeight":0,"ImgWidth":0,"SubMsgType":0,"NewMsgId":3009511950433684500,"MMPeerUserName":"@0748ee480711bf20af91c298a0d7dcc77c30a680c1004157386b81cf13474823","MMDigest":"哈哈","MMIsSend":false,"MMIsChatRoom":false,"MMUnread":false,"LocalID":"3009511950433684462","ClientMsgId":"3009511950433684462","MMActualContent":"哈哈","MMActualSender":"@0748ee480711bf20af91c298a0d7dcc77c30a680c1004157386b81cf13474823","MMDigestTime":"21:44","MMDisplayTime":1462887888,"MMTime":"21:44","_h":104,"_index":0,"_offsetTop":0,"$$hashKey":"098"}') const expectedFromUserName = '@0748ee480711bf20af91c298a0d7dcc77c30a680c1004157386b81cf13474823' @@ -183,7 +175,7 @@ test('ready()', async t => { // const m = wechaty.Message.create(rawPayload.MsgId) const msgPayload = await puppet.messagePayload(rawPayload.MsgId) - t.is(msgPayload.id, expectedMsgId, 'id/MsgId right') + t.equal(msgPayload.id, expectedMsgId, 'id/MsgId right') const fromId = msgPayload.fromId const toId = msgPayload.toId @@ -195,10 +187,10 @@ test('ready()', async t => { const fromContactPayload = await puppet.contactPayload(fromId) const toContactPayload = await puppet.contactPayload(toId) - t.is(fromId, expectedFromUserName, 'contact ready for FromUserName') - t.is(fromContactPayload.name, expectedFromNickName, 'contact ready for FromNickName') - t.is(toId, expectedToUserName, 'contact ready for ToUserName') - t.is(toContactPayload.name, expectedToNickName, 'contact ready for ToNickName') + t.equal(fromId, expectedFromUserName, 'contact ready for FromUserName') + t.equal(fromContactPayload.name, expectedFromNickName, 'contact ready for FromNickName') + t.equal(toId, expectedToUserName, 'contact ready for ToUserName') + t.equal(toContactPayload.name, expectedToNickName, 'contact ready for ToNickName') sandbox.restore() }) @@ -243,7 +235,7 @@ test('ready()', async t => { // from: 'yyy', // }) -// t.is(msgList.length, 2, 'Message.findAll with limit 2') +// t.equal(msgList.length, 2, 'Message.findAll with limit 2') // sandbox.restore() // }) diff --git a/tests/puppeteer-room.spec.ts b/tests/puppeteer-room.spec.ts index 6119511..42b6e7e 100755 --- a/tests/puppeteer-room.spec.ts +++ b/tests/puppeteer-room.spec.ts @@ -1,5 +1,4 @@ -#!/usr/bin/env ts-node - +#!/usr/bin/env node --no-warnings --loader ts-node/esm /** * Wechaty - https://github.com/chatie/wechaty * @@ -19,29 +18,27 @@ * */ -// tslint:disable:no-shadowed-variable -// tslint:disable:max-classes-per-file -// tslint:disable:arrow-parens - -import { test, sinon } from 'tstest' +import { + test, + sinon, +} from 'tstest' import { log, -} from '../src/config' +} from '../src/config.js' import { PuppetWeChat, -} from '../src/puppet-wechat' -import { +} from '../src/puppet-wechat.js' +import type { WebRoomRawPayload, -} from '../src/web-schemas' +} from '../src/web-schemas.js' class PuppetWeChatTest extends PuppetWeChat { - public id?: string = undefined + public override id?: string = undefined } -// tslint:disable:max-line-length const ROOM_RAW_PAYLOAD: WebRoomRawPayload = JSON.parse('{"RemarkPYQuanPin":"","RemarkPYInitial":"","PYInitial":"TZZGQNTSHGFJ","PYQuanPin":"tongzhizhongguoqingniantianshihuiguanfangjia","Uin":0,"UserName":"@@e2355db381dc46a77c0b95516d05e7486135cb6370d8a6af66925d89d50ec278","NickName":"(通知)中国青年天使会官方家","HeadImgUrl":"/cgi-bin/mmwebwx-bin/webwxgetheadimg?seq=670397504&username=@@e2355db381dc46a77c0b95516d05e7486135cb6370d8a6af66925d89d50ec278&skey=","ContactFlag":2,"MemberCount":146,"MemberList":[{"Uin":0,"UserName":"@ecff4a7a86f23455dc42317269aa36ab","NickName":"童玮亮","AttrStatus":103423,"PYInitial":"","PYQuanPin":"","RemarkPYInitial":"","RemarkPYQuanPin":"","MemberStatus":0,"DisplayName":"","KeyWord":"dap","HeadImgUrl":"/cgi-bin/mmwebwx-bin/webwxgeticon?seq=0&username=@ecff4a7a86f23455dc42317269aa36ab&skey=@crypt_f9cec94b_f23a307a23231cfb5098faf91ff759ca&chatroomid=@4b8baa99bdfc354443711412126d2aaf"},{"Uin":0,"UserName":"@eac4377ecfd59e4321262f892177169f","NickName":"麦刚","AttrStatus":33674247,"PYInitial":"","PYQuanPin":"","RemarkPYInitial":"","RemarkPYQuanPin":"","MemberStatus":0,"DisplayName":"","KeyWord":"mai","HeadImgUrl":"/cgi-bin/mmwebwx-bin/webwxgeticon?seq=0&username=@eac4377ecfd59e4321262f892177169f&skey=@crypt_f9cec94b_f23a307a23231cfb5098faf91ff759ca&chatroomid=@4b8baa99bdfc354443711412126d2aaf"},{"Uin":0,"UserName":"@ad85207730aa94e006ddce28f74e6878","NickName":"田美坤Maggie","AttrStatus":112679,"PYInitial":"","PYQuanPin":"","RemarkPYInitial":"","RemarkPYQuanPin":"","MemberStatus":0,"DisplayName":"田美坤","KeyWord":"tia","HeadImgUrl":"/cgi-bin/mmwebwx-bin/webwxgeticon?seq=0&username=@ad85207730aa94e006ddce28f74e6878&skey=@crypt_f9cec94b_f23a307a23231cfb5098faf91ff759ca&chatroomid=@4b8baa99bdfc354443711412126d2aaf"},{"Uin":2351423900,"UserName":"@33cc239d22b20d56395bbbd0967b28b9","NickName":"周宏光","AttrStatus":327869,"PYInitial":"","PYQuanPin":"","RemarkPYInitial":"","RemarkPYQuanPin":"","MemberStatus":0,"DisplayName":"周宏光","KeyWord":"acc","HeadImgUrl":"/cgi-bin/mmwebwx-bin/webwxgeticon?seq=0&username=@33cc239d22b20d56395bbbd0967b28b9&skey=@crypt_f9cec94b_f23a307a23231cfb5098faf91ff759ca&chatroomid=@4b8baa99bdfc354443711412126d2aaf"},{"Uin":0,"UserName":"@5e77381e1e3b5641ddcee44670b6e83a","NickName":"牛文文","AttrStatus":100349,"PYInitial":"","PYQuanPin":"","RemarkPYInitial":"","RemarkPYQuanPin":"","MemberStatus":0,"DisplayName":"","KeyWord":"niu","HeadImgUrl":"/cgi-bin/mmwebwx-bin/webwxgeticon?seq=0&username=@5e77381e1e3b5641ddcee44670b6e83a&skey=@crypt_f9cec94b_f23a307a23231cfb5098faf91ff759ca&chatroomid=@4b8baa99bdfc354443711412126d2aaf"},{"Uin":0,"UserName":"@56941ef97f3e9c70af88667fdd613b44","NickName":"羊东 东方红酒窖","AttrStatus":33675367,"PYInitial":"","PYQuanPin":"","RemarkPYInitial":"","RemarkPYQuanPin":"","MemberStatus":0,"DisplayName":"","KeyWord":"Yan","HeadImgUrl":"/cgi-bin/mmwebwx-bin/webwxgeticon?seq=0&username=@56941ef97f3e9c70af88667fdd613b44&skey=@crypt_f9cec94b_f23a307a23231cfb5098faf91ff759ca&chatroomid=@4b8baa99bdfc354443711412126d2aaf"},{"Uin":0,"UserName":"@72c4767ce32db488871fdd1c27173b81","NickName":"李竹~英诺天使(此号已满)","AttrStatus":235261,"PYInitial":"","PYQuanPin":"","RemarkPYInitial":"","RemarkPYQuanPin":"","MemberStatus":0,"DisplayName":"","KeyWord":"liz","HeadImgUrl":"/cgi-bin/mmwebwx-bin/webwxgeticon?seq=0&username=@72c4767ce32db488871fdd1c27173b81&skey=@crypt_f9cec94b_f23a307a23231cfb5098faf91ff759ca&chatroomid=@4b8baa99bdfc354443711412126d2aaf"},{"Uin":0,"UserName":"@0b0e2eb9501ab2d84f9f800f6a0b4216","NickName":"周静彤 杨宁助理","AttrStatus":230885,"PYInitial":"","PYQuanPin":"","RemarkPYInitial":"","RemarkPYQuanPin":"","MemberStatus":0,"DisplayName":"","KeyWord":"zlo","HeadImgUrl":"/cgi-bin/mmwebwx-bin/webwxgeticon?seq=0&username=@0b0e2eb9501ab2d84f9f800f6a0b4216&skey=@crypt_f9cec94b_f23a307a23231cfb5098faf91ff759ca&chatroomid=@4b8baa99bdfc354443711412126d2aaf"},{"Uin":0,"UserName":"@4bfa767be0cd3fb78409b9735d1dcc57","NickName":"周哲 Jeremy","AttrStatus":33791995,"PYInitial":"","PYQuanPin":"","RemarkPYInitial":"","RemarkPYQuanPin":"","MemberStatus":0,"DisplayName":"","KeyWord":"zho","HeadImgUrl":"/cgi-bin/mmwebwx-bin/webwxgeticon?seq=0&username=@4bfa767be0cd3fb78409b9735d1dcc57&skey=@crypt_f9cec94b_f23a307a23231cfb5098faf91ff759ca&chatroomid=@4b8baa99bdfc354443711412126d2aaf"},{"Uin":0,"UserName":"@ad954bf2159a572b7743a5bc134739f4","NickName":"vicky张","AttrStatus":100477,"PYInitial":"","PYQuanPin":"","RemarkPYInitial":"","RemarkPYQuanPin":"","MemberStatus":0,"DisplayName":"","KeyWord":"hua","HeadImgUrl":"/cgi-bin/mmwebwx-bin/webwxgeticon?seq=0&username=@ad954bf2159a572b7743a5bc134739f4&skey=@crypt_f9cec94b_f23a307a23231cfb5098faf91ff759ca&chatroomid=@4b8baa99bdfc354443711412126d2aaf"}],"RemarkName":"","HideInputBarFlag":0,"Sex":0,"Signature":"","VerifyFlag":0,"OwnerUin":2351423900,"StarFriend":0,"AppAccountFlag":0,"Statues":0,"AttrStatus":0,"Province":"","City":"","Alias":"","SnsFlag":0,"UniFriend":0,"DisplayName":"","ChatRoomId":0,"KeyWord":"","EncryChatRoomId":"@4b8baa99bdfc354443711412126d2aaf","MMFromBatchGet":true,"MMOrderSymbol":"TONGZHIZHONGGUOQINGNIANTIANSHIHUIGUANFANGJIA","MMFromBatchget":true,"MMInChatroom":true}') const CONTACT_RAW_PAYLOAD_DICT = JSON.parse('{"@ad85207730aa94e006ddce28f74e6878":{ "UserName": "@ad85207730aa94e006ddce28f74e6878","NickName": "田美坤Maggie","RemarkName": "" },"@72c4767ce32db488871fdd1c27173b81":{ "UserName": "@72c4767ce32db488871fdd1c27173b81","NickName": "李竹~英诺天使(此号已满)","RemarkName": "" },"@ecff4a7a86f23455dc42317269aa36ab":{ "UserName": "@ecff4a7a86f23455dc42317269aa36ab","NickName": "童玮亮","RemarkName": "童玮亮备注" }}') @@ -86,18 +83,18 @@ test('Room smoke testing', async t => { const roomPayload = await puppet.roomPayload(ROOM_EXPECTED.id) - t.is(roomPayload.id, ROOM_EXPECTED.id, 'should set id/UserName right') + t.equal(roomPayload.id, ROOM_EXPECTED.id, 'should set id/UserName right') // t.is((r as any).payload[.('encryId') , EXPECTED.encryId, 'should set EncryChatRoomId') - t.is(roomPayload.topic, ROOM_EXPECTED.topic, 'should set topic/NickName') + t.equal(roomPayload.topic, ROOM_EXPECTED.topic, 'should set topic/NickName') // const contact1 = new wechaty.Contact(ROOM_EXPECTED.memberId1) // const alias1 = await room.alias(contact1) // const contactPayload1 = await puppet.contactPayload(ROOM_EXPECTED.memberId1) const roomMemberPayload1 = await puppet.roomMemberPayload(ROOM_EXPECTED.id, ROOM_EXPECTED.memberId1) - t.is(roomMemberPayload1.roomAlias, ROOM_EXPECTED.memberNick1, 'should get roomAlias') + t.equal(roomMemberPayload1.roomAlias, ROOM_EXPECTED.memberNick1, 'should get roomAlias') // const name1 = r.alias(contact1) // t.is(name1, EXPECTED.memberNick1, 'should get roomAlias') @@ -107,7 +104,7 @@ test('Room smoke testing', async t => { // const contactPayload2 = await puppet.contactPayload(ROOM_EXPECTED.memberId2) const memberPayload2 = await puppet.roomMemberPayload(ROOM_EXPECTED.id, ROOM_EXPECTED.memberId2) - t.is(memberPayload2.roomAlias, '', 'should return null if not set roomAlias') + t.equal(memberPayload2.roomAlias, '', 'should return null if not set roomAlias') // const name2 = r.alias(contact2) // t.is(name2, null, 'should return null if not set roomAlias') @@ -137,10 +134,10 @@ test('Room smoke testing', async t => { // const contactC = await room.member(ROOM_EXPECTED.memberNick3) // const contactD = await room.member({ roomAlias: ROOM_EXPECTED.memberNick1 }) - t.is(resultA[0], ROOM_EXPECTED.memberId1, `should get the right id from ${ROOM_EXPECTED.memberId1}, find member by default`) - t.is(resultB[0], ROOM_EXPECTED.memberId2, `should get the right id from ${ROOM_EXPECTED.memberId2}, find member by default`) - t.is(resultC[0], ROOM_EXPECTED.memberId3, `should get the right id from ${ROOM_EXPECTED.memberId3}, find member by default`) - t.is(resultD[0], ROOM_EXPECTED.memberId1, `should get the right id from ${ROOM_EXPECTED.memberId1}, find member by roomAlias`) + t.equal(resultA[0], ROOM_EXPECTED.memberId1, `should get the right id from ${ROOM_EXPECTED.memberId1}, find member by default`) + t.equal(resultB[0], ROOM_EXPECTED.memberId2, `should get the right id from ${ROOM_EXPECTED.memberId2}, find member by default`) + t.equal(resultC[0], ROOM_EXPECTED.memberId3, `should get the right id from ${ROOM_EXPECTED.memberId3}, find member by default`) + t.equal(resultD[0], ROOM_EXPECTED.memberId1, `should get the right id from ${ROOM_EXPECTED.memberId1}, find member by roomAlias`) sandbox.restore() }) @@ -153,7 +150,7 @@ test('Room smoke testing', async t => { // try { // const result = await wechaty.Room.find({ topic: 'xxx' }) -// t.is(result, null, `should return null if cannot find the room`) +// t.equal(result, null, `should return null if cannot find the room`) // } catch (e) { // t.pass('should throw before login or not found') // } @@ -162,5 +159,5 @@ test('Room smoke testing', async t => { // topic: 'yyy', // }) -// t.is(roomList.length, 0, 'should return empty array before login') +// t.equal(roomList.length, 0, 'should return empty array before login') // }) diff --git a/tests/puppeteer.spec.ts b/tests/puppeteer.spec.ts index 8484250..5351dee 100755 --- a/tests/puppeteer.spec.ts +++ b/tests/puppeteer.spec.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env ts-node +#!/usr/bin/env node --no-warnings --loader ts-node/esm /** * Wechaty - https://github.com/chatie/wechaty * @@ -17,22 +17,18 @@ * limitations under the License. * */ - -// tslint:disable:arrow-parens -// tslint:disable:no-console - import fs from 'fs' import path from 'path' -// tslint:disable:no-shadowed-variable import { test, sinon, -} from 'tstest' +} from 'tstest' +import puppeteer from 'puppeteer' + import { - Protocol, - launch, -} from 'puppeteer' + codeRoot, +} from '../src/cjs.js' const PUPPETEER_LAUNCH_OPTIONS = { args: [ @@ -48,7 +44,7 @@ test('Puppeteer smoke testing', async t => { let page try { - browser = await launch(PUPPETEER_LAUNCH_OPTIONS) + browser = await puppeteer.launch(PUPPETEER_LAUNCH_OPTIONS) t.ok(browser, 'Browser instnace') const version = await browser.version() @@ -60,10 +56,10 @@ test('Puppeteer smoke testing', async t => { t.pass('should open wx.qq.com') const result = await page.evaluate(() => 42) - t.is(result, 42, 'should get 42') + t.equal(result, 42, 'should get 42') } catch (e) { - t.fail((e && e.message) || e) + t.fail(e as any) } finally { if (page) { await page.close() @@ -76,7 +72,7 @@ test('Puppeteer smoke testing', async t => { test('evaluate() a function that returns a Promise', async t => { try { - const browser = await launch(PUPPETEER_LAUNCH_OPTIONS) + const browser = await puppeteer.launch(PUPPETEER_LAUNCH_OPTIONS) const page = await browser.newPage() const result = await page.evaluate(() => Promise.resolve(42)) @@ -85,7 +81,7 @@ test('evaluate() a function that returns a Promise', async t => { await page.close() await browser.close() } catch (e) { - t.fail((e && e.message) || e) + t.fail(e as any) } }) @@ -96,17 +92,18 @@ test('evaluate() a file and get the returns value', async t => { } try { - const browser = await launch(PUPPETEER_LAUNCH_OPTIONS) + const browser = await puppeteer.launch(PUPPETEER_LAUNCH_OPTIONS) const page = await browser.newPage() const file = path.join( - __dirname, + codeRoot, + 'tests', 'fixtures/inject-file.js', ) const source = fs.readFileSync(file).toString() const result = await page.evaluate(source) - t.deepEqual(result, EXPECTED_OBJ, 'should inject file inside browser and return the value') + t.same(result, EXPECTED_OBJ, 'should inject file inside browser and return the value') const noWechaty = await page.evaluate('typeof WechatyBro === "undefined"') t.equal(noWechaty, true, 'should no wechaty by default') @@ -118,7 +115,7 @@ test('evaluate() a file and get the returns value', async t => { await browser.close() } catch (e) { - t.fail((e && e.message) || e) + t.fail(e as any) } }) @@ -127,7 +124,7 @@ test('page.on(console)', async t => { const EXPECTED_ARG2 = 2 // const EXPECTED_ARG3 = { arg3: 3 } - const browser = await launch(PUPPETEER_LAUNCH_OPTIONS) + const browser = await puppeteer.launch(PUPPETEER_LAUNCH_OPTIONS) const page = await browser.newPage() const spy = sinon.spy() @@ -151,7 +148,7 @@ test('page.on(console)', async t => { }) test('page.exposeFunction()', async t => { - const browser = await launch(PUPPETEER_LAUNCH_OPTIONS) + const browser = await puppeteer.launch(PUPPETEER_LAUNCH_OPTIONS) const page = await browser.newPage() const spy = sinon.spy() @@ -169,7 +166,7 @@ test('other demos', async t => { const EXPECTED_URL = 'https://github.com/' try { - const browser = await launch(PUPPETEER_LAUNCH_OPTIONS) + const browser = await puppeteer.launch(PUPPETEER_LAUNCH_OPTIONS) const version = await browser.version() t.ok(version, 'should get version') @@ -189,16 +186,16 @@ test('other demos', async t => { }) page.on('error', (e, ...args) => { - console.error('error', e) + console.error('error', e as Error) console.error('error:args:', args) }) page.on('pageerror', (e, ...args) => { - console.error('pageerror', e) + console.error('pageerror', e as Error) console.error('pageerror:args:', args) }) page.on('load', (e, ...args) => { - console.info('load:e:', e) + console.info('load:e:', e as Error) console.info('load:args:', args) }) @@ -227,23 +224,25 @@ test('other demos', async t => { // await page.injectFile(path.join(__dirname, 'wechaty-bro.js')) const cookieList = await page.cookies() t.ok(cookieList.length, 'should get cookies') - t.ok(cookieList[0].name, 'should get cookies with name') - - const cookie: Protocol.Network.Cookie = { - domain : 'qq.com', - expires : 1234324132, - httpOnly : false, - name : 'test-name', - path : '/', - priority: 'Medium', - sameParty: true, - sameSite : 'Strict', - secure : false, - session : true, - size : 42, - value : 'test-value', - } - await page.setCookie(cookie) + t.ok(cookieList[0]?.name, 'should get cookies with name') + + /** + * Huan(202109): skip the below Error + * message: "Protocol error (Network.setCookies): Invalid cookie fields" + */ + // const cookie: puppeteer.Protocol.Network.CookieParam = { + // domain : 'qq.com', + // expires : 1234324132, + // httpOnly : false, + // name : 'test-name', + // path : '/', + // priority: 'Medium', + // sameParty: true, + // sameSite : 'Strict', + // secure : false, + // value : 'test-value', + // } + // await page.setCookie(cookie) const result = await page.evaluate(() => 8 * 7) t.equal(result, 56, 'should evaluated function for () => 8 * 7 = 56') @@ -257,6 +256,6 @@ test('other demos', async t => { await page.close() await browser.close() } catch (e) { - t.fail(e) + t.fail(e as any) } }) diff --git a/tsconfig.cjs.json b/tsconfig.cjs.json new file mode 100644 index 0000000..8693cd0 --- /dev/null +++ b/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "dist/cjs", + }, +} diff --git a/tsconfig.json b/tsconfig.json index dfcc6ba..03befc4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "@chatie/tsconfig", "compilerOptions": { - "outDir": "dist", + "outDir": "dist/esm", "lib": [ "dom", "esnext",