@ardennguyen/zalo-agent-cli 1.0.7 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DISCLAIMER.md +2 -2
- package/LICENSE +456 -21
- package/NOTICE +127 -0
- package/README.md +169 -70
- package/node_modules/asynckit/LICENSE +21 -0
- package/node_modules/asynckit/README.md +233 -0
- package/node_modules/asynckit/bench.js +76 -0
- package/node_modules/asynckit/index.js +6 -0
- package/node_modules/asynckit/lib/abort.js +29 -0
- package/node_modules/asynckit/lib/async.js +34 -0
- package/node_modules/asynckit/lib/defer.js +26 -0
- package/node_modules/asynckit/lib/iterate.js +75 -0
- package/node_modules/asynckit/lib/readable_asynckit.js +91 -0
- package/node_modules/asynckit/lib/readable_parallel.js +25 -0
- package/node_modules/asynckit/lib/readable_serial.js +25 -0
- package/node_modules/asynckit/lib/readable_serial_ordered.js +29 -0
- package/node_modules/asynckit/lib/state.js +37 -0
- package/node_modules/asynckit/lib/streamify.js +141 -0
- package/node_modules/asynckit/lib/terminator.js +29 -0
- package/node_modules/asynckit/package.json +63 -0
- package/node_modules/asynckit/parallel.js +43 -0
- package/node_modules/asynckit/serial.js +17 -0
- package/node_modules/asynckit/serialOrdered.js +75 -0
- package/node_modules/asynckit/stream.js +21 -0
- package/node_modules/bignumber.js/CHANGELOG.md +381 -0
- package/node_modules/bignumber.js/LICENCE.md +26 -0
- package/node_modules/bignumber.js/README.md +289 -0
- package/node_modules/bignumber.js/bignumber.d.mts +6 -0
- package/node_modules/bignumber.js/bignumber.d.ts +5 -0
- package/node_modules/bignumber.js/bignumber.js +2922 -0
- package/node_modules/bignumber.js/bignumber.mjs +2907 -0
- package/node_modules/bignumber.js/doc/API.html +2249 -0
- package/node_modules/bignumber.js/package.json +60 -0
- package/node_modules/bignumber.js/types.d.ts +1821 -0
- package/node_modules/call-bind-apply-helpers/.eslintrc +17 -0
- package/node_modules/call-bind-apply-helpers/.github/FUNDING.yml +12 -0
- package/node_modules/call-bind-apply-helpers/.nycrc +9 -0
- package/node_modules/call-bind-apply-helpers/CHANGELOG.md +30 -0
- package/node_modules/call-bind-apply-helpers/LICENSE +21 -0
- package/node_modules/call-bind-apply-helpers/README.md +62 -0
- package/node_modules/call-bind-apply-helpers/actualApply.d.ts +1 -0
- package/node_modules/call-bind-apply-helpers/actualApply.js +10 -0
- package/node_modules/call-bind-apply-helpers/applyBind.d.ts +19 -0
- package/node_modules/call-bind-apply-helpers/applyBind.js +10 -0
- package/node_modules/call-bind-apply-helpers/functionApply.d.ts +1 -0
- package/node_modules/call-bind-apply-helpers/functionApply.js +4 -0
- package/node_modules/call-bind-apply-helpers/functionCall.d.ts +1 -0
- package/node_modules/call-bind-apply-helpers/functionCall.js +4 -0
- package/node_modules/call-bind-apply-helpers/index.d.ts +64 -0
- package/node_modules/call-bind-apply-helpers/index.js +15 -0
- package/node_modules/call-bind-apply-helpers/package.json +85 -0
- package/node_modules/call-bind-apply-helpers/reflectApply.d.ts +3 -0
- package/node_modules/call-bind-apply-helpers/reflectApply.js +4 -0
- package/node_modules/call-bind-apply-helpers/test/index.js +63 -0
- package/node_modules/call-bind-apply-helpers/tsconfig.json +9 -0
- package/node_modules/combined-stream/License +19 -0
- package/node_modules/combined-stream/Readme.md +138 -0
- package/node_modules/combined-stream/lib/combined_stream.js +208 -0
- package/node_modules/combined-stream/package.json +25 -0
- package/node_modules/crypto-js/CONTRIBUTING.md +28 -0
- package/node_modules/crypto-js/LICENSE +24 -0
- package/node_modules/crypto-js/README.md +275 -0
- package/node_modules/crypto-js/aes.js +234 -0
- package/node_modules/crypto-js/blowfish.js +471 -0
- package/node_modules/crypto-js/bower.json +39 -0
- package/node_modules/crypto-js/cipher-core.js +895 -0
- package/node_modules/crypto-js/core.js +807 -0
- package/node_modules/crypto-js/crypto-js.js +6657 -0
- package/node_modules/crypto-js/docs/QuickStartGuide.wiki +470 -0
- package/node_modules/crypto-js/enc-base64.js +136 -0
- package/node_modules/crypto-js/enc-base64url.js +148 -0
- package/node_modules/crypto-js/enc-hex.js +18 -0
- package/node_modules/crypto-js/enc-latin1.js +18 -0
- package/node_modules/crypto-js/enc-utf16.js +149 -0
- package/node_modules/crypto-js/enc-utf8.js +18 -0
- package/node_modules/crypto-js/evpkdf.js +134 -0
- package/node_modules/crypto-js/format-hex.js +66 -0
- package/node_modules/crypto-js/format-openssl.js +18 -0
- package/node_modules/crypto-js/hmac-md5.js +18 -0
- package/node_modules/crypto-js/hmac-ripemd160.js +18 -0
- package/node_modules/crypto-js/hmac-sha1.js +18 -0
- package/node_modules/crypto-js/hmac-sha224.js +18 -0
- package/node_modules/crypto-js/hmac-sha256.js +18 -0
- package/node_modules/crypto-js/hmac-sha3.js +18 -0
- package/node_modules/crypto-js/hmac-sha384.js +18 -0
- package/node_modules/crypto-js/hmac-sha512.js +18 -0
- package/node_modules/crypto-js/hmac.js +143 -0
- package/node_modules/crypto-js/index.js +18 -0
- package/node_modules/crypto-js/lib-typedarrays.js +76 -0
- package/node_modules/crypto-js/md5.js +268 -0
- package/node_modules/crypto-js/mode-cfb.js +80 -0
- package/node_modules/crypto-js/mode-ctr-gladman.js +116 -0
- package/node_modules/crypto-js/mode-ctr.js +58 -0
- package/node_modules/crypto-js/mode-ecb.js +40 -0
- package/node_modules/crypto-js/mode-ofb.js +54 -0
- package/node_modules/crypto-js/package.json +42 -0
- package/node_modules/crypto-js/pad-ansix923.js +49 -0
- package/node_modules/crypto-js/pad-iso10126.js +44 -0
- package/node_modules/crypto-js/pad-iso97971.js +40 -0
- package/node_modules/crypto-js/pad-nopadding.js +30 -0
- package/node_modules/crypto-js/pad-pkcs7.js +18 -0
- package/node_modules/crypto-js/pad-zeropadding.js +47 -0
- package/node_modules/crypto-js/pbkdf2.js +145 -0
- package/node_modules/crypto-js/rabbit-legacy.js +190 -0
- package/node_modules/crypto-js/rabbit.js +192 -0
- package/node_modules/crypto-js/rc4.js +139 -0
- package/node_modules/crypto-js/ripemd160.js +267 -0
- package/node_modules/crypto-js/sha1.js +150 -0
- package/node_modules/crypto-js/sha224.js +80 -0
- package/node_modules/crypto-js/sha256.js +199 -0
- package/node_modules/crypto-js/sha3.js +326 -0
- package/node_modules/crypto-js/sha384.js +83 -0
- package/node_modules/crypto-js/sha512.js +326 -0
- package/node_modules/crypto-js/tripledes.js +779 -0
- package/node_modules/crypto-js/x64-core.js +304 -0
- package/node_modules/delayed-stream/.npmignore +1 -0
- package/node_modules/delayed-stream/License +19 -0
- package/node_modules/delayed-stream/Makefile +7 -0
- package/node_modules/delayed-stream/Readme.md +141 -0
- package/node_modules/delayed-stream/lib/delayed_stream.js +107 -0
- package/node_modules/delayed-stream/package.json +27 -0
- package/node_modules/dunder-proto/.eslintrc +5 -0
- package/node_modules/dunder-proto/.github/FUNDING.yml +12 -0
- package/node_modules/dunder-proto/.nycrc +13 -0
- package/node_modules/dunder-proto/CHANGELOG.md +24 -0
- package/node_modules/dunder-proto/LICENSE +21 -0
- package/node_modules/dunder-proto/README.md +54 -0
- package/node_modules/dunder-proto/get.d.ts +5 -0
- package/node_modules/dunder-proto/get.js +30 -0
- package/node_modules/dunder-proto/package.json +76 -0
- package/node_modules/dunder-proto/set.d.ts +5 -0
- package/node_modules/dunder-proto/set.js +35 -0
- package/node_modules/dunder-proto/test/get.js +34 -0
- package/node_modules/dunder-proto/test/index.js +4 -0
- package/node_modules/dunder-proto/test/set.js +50 -0
- package/node_modules/dunder-proto/tsconfig.json +9 -0
- package/node_modules/es-define-property/.eslintrc +13 -0
- package/node_modules/es-define-property/.github/FUNDING.yml +12 -0
- package/node_modules/es-define-property/.nycrc +9 -0
- package/node_modules/es-define-property/CHANGELOG.md +29 -0
- package/node_modules/es-define-property/LICENSE +21 -0
- package/node_modules/es-define-property/README.md +49 -0
- package/node_modules/es-define-property/index.d.ts +3 -0
- package/node_modules/es-define-property/index.js +14 -0
- package/node_modules/es-define-property/package.json +81 -0
- package/node_modules/es-define-property/test/index.js +56 -0
- package/node_modules/es-define-property/tsconfig.json +10 -0
- package/node_modules/es-errors/.eslintrc +5 -0
- package/node_modules/es-errors/.github/FUNDING.yml +12 -0
- package/node_modules/es-errors/CHANGELOG.md +40 -0
- package/node_modules/es-errors/LICENSE +21 -0
- package/node_modules/es-errors/README.md +55 -0
- package/node_modules/es-errors/eval.d.ts +3 -0
- package/node_modules/es-errors/eval.js +4 -0
- package/node_modules/es-errors/index.d.ts +3 -0
- package/node_modules/es-errors/index.js +4 -0
- package/node_modules/es-errors/package.json +80 -0
- package/node_modules/es-errors/range.d.ts +3 -0
- package/node_modules/es-errors/range.js +4 -0
- package/node_modules/es-errors/ref.d.ts +3 -0
- package/node_modules/es-errors/ref.js +4 -0
- package/node_modules/es-errors/syntax.d.ts +3 -0
- package/node_modules/es-errors/syntax.js +4 -0
- package/node_modules/es-errors/test/index.js +19 -0
- package/node_modules/es-errors/tsconfig.json +49 -0
- package/node_modules/es-errors/type.d.ts +3 -0
- package/node_modules/es-errors/type.js +4 -0
- package/node_modules/es-errors/uri.d.ts +3 -0
- package/node_modules/es-errors/uri.js +4 -0
- package/node_modules/es-object-atoms/.eslintrc +16 -0
- package/node_modules/es-object-atoms/.github/FUNDING.yml +12 -0
- package/node_modules/es-object-atoms/CHANGELOG.md +44 -0
- package/node_modules/es-object-atoms/LICENSE +21 -0
- package/node_modules/es-object-atoms/README.md +63 -0
- package/node_modules/es-object-atoms/RequireObjectCoercible.d.ts +3 -0
- package/node_modules/es-object-atoms/RequireObjectCoercible.js +11 -0
- package/node_modules/es-object-atoms/ToObject.d.ts +7 -0
- package/node_modules/es-object-atoms/ToObject.js +10 -0
- package/node_modules/es-object-atoms/index.d.ts +3 -0
- package/node_modules/es-object-atoms/index.js +4 -0
- package/node_modules/es-object-atoms/isObject.d.ts +3 -0
- package/node_modules/es-object-atoms/isObject.js +6 -0
- package/node_modules/es-object-atoms/package.json +79 -0
- package/node_modules/es-object-atoms/test/index.js +38 -0
- package/node_modules/es-object-atoms/tsconfig.json +7 -0
- package/node_modules/es-set-tostringtag/.eslintrc +13 -0
- package/node_modules/es-set-tostringtag/.nycrc +9 -0
- package/node_modules/es-set-tostringtag/CHANGELOG.md +67 -0
- package/node_modules/es-set-tostringtag/LICENSE +21 -0
- package/node_modules/es-set-tostringtag/README.md +53 -0
- package/node_modules/es-set-tostringtag/index.d.ts +10 -0
- package/node_modules/es-set-tostringtag/index.js +35 -0
- package/node_modules/es-set-tostringtag/package.json +78 -0
- package/node_modules/es-set-tostringtag/test/index.js +85 -0
- package/node_modules/es-set-tostringtag/tsconfig.json +9 -0
- package/node_modules/form-data/CHANGELOG.md +686 -0
- package/node_modules/form-data/License +19 -0
- package/node_modules/form-data/README.md +355 -0
- package/node_modules/form-data/index.d.ts +62 -0
- package/node_modules/form-data/lib/browser.js +4 -0
- package/node_modules/form-data/lib/form_data.js +506 -0
- package/node_modules/form-data/lib/populate.js +10 -0
- package/node_modules/form-data/node_modules/mime-db/HISTORY.md +507 -0
- package/node_modules/form-data/node_modules/mime-db/LICENSE +23 -0
- package/node_modules/form-data/node_modules/mime-db/README.md +100 -0
- package/node_modules/form-data/node_modules/mime-db/db.json +8519 -0
- package/node_modules/form-data/node_modules/mime-db/index.js +12 -0
- package/node_modules/form-data/node_modules/mime-db/package.json +60 -0
- package/node_modules/form-data/node_modules/mime-types/HISTORY.md +397 -0
- package/node_modules/form-data/node_modules/mime-types/LICENSE +23 -0
- package/node_modules/form-data/node_modules/mime-types/README.md +113 -0
- package/node_modules/form-data/node_modules/mime-types/index.js +188 -0
- package/node_modules/form-data/node_modules/mime-types/package.json +44 -0
- package/node_modules/form-data/package.json +82 -0
- package/node_modules/function-bind/.eslintrc +21 -0
- package/node_modules/function-bind/.github/FUNDING.yml +12 -0
- package/node_modules/function-bind/.github/SECURITY.md +3 -0
- package/node_modules/function-bind/.nycrc +13 -0
- package/node_modules/function-bind/CHANGELOG.md +136 -0
- package/node_modules/function-bind/LICENSE +20 -0
- package/node_modules/function-bind/README.md +46 -0
- package/node_modules/function-bind/implementation.js +84 -0
- package/node_modules/function-bind/index.js +5 -0
- package/node_modules/function-bind/package.json +87 -0
- package/node_modules/function-bind/test/.eslintrc +9 -0
- package/node_modules/function-bind/test/index.js +252 -0
- package/node_modules/get-intrinsic/.eslintrc +42 -0
- package/node_modules/get-intrinsic/.github/FUNDING.yml +12 -0
- package/node_modules/get-intrinsic/.nycrc +9 -0
- package/node_modules/get-intrinsic/CHANGELOG.md +186 -0
- package/node_modules/get-intrinsic/LICENSE +21 -0
- package/node_modules/get-intrinsic/README.md +71 -0
- package/node_modules/get-intrinsic/index.js +378 -0
- package/node_modules/get-intrinsic/package.json +97 -0
- package/node_modules/get-intrinsic/test/GetIntrinsic.js +274 -0
- package/node_modules/get-proto/.eslintrc +10 -0
- package/node_modules/get-proto/.github/FUNDING.yml +12 -0
- package/node_modules/get-proto/.nycrc +9 -0
- package/node_modules/get-proto/CHANGELOG.md +21 -0
- package/node_modules/get-proto/LICENSE +21 -0
- package/node_modules/get-proto/Object.getPrototypeOf.d.ts +5 -0
- package/node_modules/get-proto/Object.getPrototypeOf.js +6 -0
- package/node_modules/get-proto/README.md +50 -0
- package/node_modules/get-proto/Reflect.getPrototypeOf.d.ts +3 -0
- package/node_modules/get-proto/Reflect.getPrototypeOf.js +4 -0
- package/node_modules/get-proto/index.d.ts +5 -0
- package/node_modules/get-proto/index.js +27 -0
- package/node_modules/get-proto/package.json +81 -0
- package/node_modules/get-proto/test/index.js +68 -0
- package/node_modules/get-proto/tsconfig.json +9 -0
- package/node_modules/gopd/.eslintrc +16 -0
- package/node_modules/gopd/.github/FUNDING.yml +12 -0
- package/node_modules/gopd/CHANGELOG.md +45 -0
- package/node_modules/gopd/LICENSE +21 -0
- package/node_modules/gopd/README.md +40 -0
- package/node_modules/gopd/gOPD.d.ts +1 -0
- package/node_modules/gopd/gOPD.js +4 -0
- package/node_modules/gopd/index.d.ts +5 -0
- package/node_modules/gopd/index.js +15 -0
- package/node_modules/gopd/package.json +77 -0
- package/node_modules/gopd/test/index.js +36 -0
- package/node_modules/gopd/tsconfig.json +9 -0
- package/node_modules/has-symbols/.eslintrc +11 -0
- package/node_modules/has-symbols/.github/FUNDING.yml +12 -0
- package/node_modules/has-symbols/.nycrc +9 -0
- package/node_modules/has-symbols/CHANGELOG.md +91 -0
- package/node_modules/has-symbols/LICENSE +21 -0
- package/node_modules/has-symbols/README.md +46 -0
- package/node_modules/has-symbols/index.d.ts +3 -0
- package/node_modules/has-symbols/index.js +14 -0
- package/node_modules/has-symbols/package.json +111 -0
- package/node_modules/has-symbols/shams.d.ts +3 -0
- package/node_modules/has-symbols/shams.js +45 -0
- package/node_modules/has-symbols/test/index.js +22 -0
- package/node_modules/has-symbols/test/shams/core-js.js +29 -0
- package/node_modules/has-symbols/test/shams/get-own-property-symbols.js +29 -0
- package/node_modules/has-symbols/test/tests.js +58 -0
- package/node_modules/has-symbols/tsconfig.json +10 -0
- package/node_modules/has-tostringtag/.eslintrc +5 -0
- package/node_modules/has-tostringtag/.github/FUNDING.yml +12 -0
- package/node_modules/has-tostringtag/.nycrc +13 -0
- package/node_modules/has-tostringtag/CHANGELOG.md +42 -0
- package/node_modules/has-tostringtag/LICENSE +21 -0
- package/node_modules/has-tostringtag/README.md +46 -0
- package/node_modules/has-tostringtag/index.d.ts +3 -0
- package/node_modules/has-tostringtag/index.js +8 -0
- package/node_modules/has-tostringtag/package.json +108 -0
- package/node_modules/has-tostringtag/shams.d.ts +3 -0
- package/node_modules/has-tostringtag/shams.js +8 -0
- package/node_modules/has-tostringtag/test/index.js +21 -0
- package/node_modules/has-tostringtag/test/shams/core-js.js +31 -0
- package/node_modules/has-tostringtag/test/shams/get-own-property-symbols.js +30 -0
- package/node_modules/has-tostringtag/test/tests.js +15 -0
- package/node_modules/has-tostringtag/tsconfig.json +49 -0
- package/node_modules/hasown/.github/FUNDING.yml +12 -0
- package/node_modules/hasown/.nycrc +13 -0
- package/node_modules/hasown/CHANGELOG.md +58 -0
- package/node_modules/hasown/LICENSE +21 -0
- package/node_modules/hasown/README.md +40 -0
- package/node_modules/hasown/eslint.config.mjs +6 -0
- package/node_modules/hasown/index.d.ts +3 -0
- package/node_modules/hasown/index.js +8 -0
- package/node_modules/hasown/package.json +91 -0
- package/node_modules/hasown/tsconfig.json +6 -0
- package/node_modules/json-bigint/LICENSE +20 -0
- package/node_modules/json-bigint/README.md +240 -0
- package/node_modules/json-bigint/index.js +12 -0
- package/node_modules/json-bigint/lib/parse.js +443 -0
- package/node_modules/json-bigint/lib/stringify.js +384 -0
- package/node_modules/json-bigint/package.json +34 -0
- package/node_modules/math-intrinsics/.eslintrc +16 -0
- package/node_modules/math-intrinsics/.github/FUNDING.yml +12 -0
- package/node_modules/math-intrinsics/CHANGELOG.md +24 -0
- package/node_modules/math-intrinsics/LICENSE +21 -0
- package/node_modules/math-intrinsics/README.md +50 -0
- package/node_modules/math-intrinsics/abs.d.ts +1 -0
- package/node_modules/math-intrinsics/abs.js +4 -0
- package/node_modules/math-intrinsics/constants/maxArrayLength.d.ts +3 -0
- package/node_modules/math-intrinsics/constants/maxArrayLength.js +4 -0
- package/node_modules/math-intrinsics/constants/maxSafeInteger.d.ts +3 -0
- package/node_modules/math-intrinsics/constants/maxSafeInteger.js +5 -0
- package/node_modules/math-intrinsics/constants/maxValue.d.ts +3 -0
- package/node_modules/math-intrinsics/constants/maxValue.js +5 -0
- package/node_modules/math-intrinsics/floor.d.ts +1 -0
- package/node_modules/math-intrinsics/floor.js +4 -0
- package/node_modules/math-intrinsics/isFinite.d.ts +3 -0
- package/node_modules/math-intrinsics/isFinite.js +12 -0
- package/node_modules/math-intrinsics/isInteger.d.ts +3 -0
- package/node_modules/math-intrinsics/isInteger.js +16 -0
- package/node_modules/math-intrinsics/isNaN.d.ts +1 -0
- package/node_modules/math-intrinsics/isNaN.js +6 -0
- package/node_modules/math-intrinsics/isNegativeZero.d.ts +3 -0
- package/node_modules/math-intrinsics/isNegativeZero.js +6 -0
- package/node_modules/math-intrinsics/max.d.ts +1 -0
- package/node_modules/math-intrinsics/max.js +4 -0
- package/node_modules/math-intrinsics/min.d.ts +1 -0
- package/node_modules/math-intrinsics/min.js +4 -0
- package/node_modules/math-intrinsics/mod.d.ts +3 -0
- package/node_modules/math-intrinsics/mod.js +9 -0
- package/node_modules/math-intrinsics/package.json +86 -0
- package/node_modules/math-intrinsics/pow.d.ts +1 -0
- package/node_modules/math-intrinsics/pow.js +4 -0
- package/node_modules/math-intrinsics/round.d.ts +1 -0
- package/node_modules/math-intrinsics/round.js +4 -0
- package/node_modules/math-intrinsics/sign.d.ts +3 -0
- package/node_modules/math-intrinsics/sign.js +11 -0
- package/node_modules/math-intrinsics/test/index.js +192 -0
- package/node_modules/math-intrinsics/tsconfig.json +3 -0
- package/node_modules/pako/LICENSE +21 -0
- package/node_modules/pako/README.md +155 -0
- package/node_modules/pako/dist/pako.es5.js +6760 -0
- package/node_modules/pako/dist/pako.es5.min.js +2 -0
- package/node_modules/pako/dist/pako.esm.mjs +6945 -0
- package/node_modules/pako/dist/pako.js +6964 -0
- package/node_modules/pako/dist/pako.min.js +2 -0
- package/node_modules/pako/dist/pako_deflate.es5.js +3953 -0
- package/node_modules/pako/dist/pako_deflate.es5.min.js +2 -0
- package/node_modules/pako/dist/pako_deflate.js +4154 -0
- package/node_modules/pako/dist/pako_deflate.min.js +2 -0
- package/node_modules/pako/dist/pako_inflate.es5.js +3272 -0
- package/node_modules/pako/dist/pako_inflate.es5.min.js +2 -0
- package/node_modules/pako/dist/pako_inflate.js +3279 -0
- package/node_modules/pako/dist/pako_inflate.min.js +2 -0
- package/node_modules/pako/index.js +18 -0
- package/node_modules/pako/lib/deflate.js +387 -0
- package/node_modules/pako/lib/inflate.js +456 -0
- package/node_modules/pako/lib/utils/common.js +48 -0
- package/node_modules/pako/lib/utils/strings.js +174 -0
- package/node_modules/pako/lib/zlib/README +59 -0
- package/node_modules/pako/lib/zlib/adler32.js +51 -0
- package/node_modules/pako/lib/zlib/constants.js +68 -0
- package/node_modules/pako/lib/zlib/crc32.js +59 -0
- package/node_modules/pako/lib/zlib/deflate.js +2069 -0
- package/node_modules/pako/lib/zlib/gzheader.js +58 -0
- package/node_modules/pako/lib/zlib/inffast.js +344 -0
- package/node_modules/pako/lib/zlib/inflate.js +1575 -0
- package/node_modules/pako/lib/zlib/inftrees.js +340 -0
- package/node_modules/pako/lib/zlib/messages.js +32 -0
- package/node_modules/pako/lib/zlib/trees.js +1179 -0
- package/node_modules/pako/lib/zlib/zstream.js +47 -0
- package/node_modules/pako/package.json +73 -0
- package/node_modules/semver/LICENSE +15 -0
- package/node_modules/semver/README.md +680 -0
- package/node_modules/semver/bin/semver.js +195 -0
- package/node_modules/semver/classes/comparator.js +143 -0
- package/node_modules/semver/classes/index.js +7 -0
- package/node_modules/semver/classes/range.js +577 -0
- package/node_modules/semver/classes/semver.js +350 -0
- package/node_modules/semver/functions/clean.js +8 -0
- package/node_modules/semver/functions/cmp.js +54 -0
- package/node_modules/semver/functions/coerce.js +62 -0
- package/node_modules/semver/functions/compare-build.js +9 -0
- package/node_modules/semver/functions/compare-loose.js +5 -0
- package/node_modules/semver/functions/compare.js +7 -0
- package/node_modules/semver/functions/diff.js +60 -0
- package/node_modules/semver/functions/eq.js +5 -0
- package/node_modules/semver/functions/gt.js +5 -0
- package/node_modules/semver/functions/gte.js +5 -0
- package/node_modules/semver/functions/inc.js +21 -0
- package/node_modules/semver/functions/lt.js +5 -0
- package/node_modules/semver/functions/lte.js +5 -0
- package/node_modules/semver/functions/major.js +5 -0
- package/node_modules/semver/functions/minor.js +5 -0
- package/node_modules/semver/functions/neq.js +5 -0
- package/node_modules/semver/functions/parse.js +18 -0
- package/node_modules/semver/functions/patch.js +5 -0
- package/node_modules/semver/functions/prerelease.js +8 -0
- package/node_modules/semver/functions/rcompare.js +5 -0
- package/node_modules/semver/functions/rsort.js +5 -0
- package/node_modules/semver/functions/satisfies.js +12 -0
- package/node_modules/semver/functions/sort.js +5 -0
- package/node_modules/semver/functions/truncate.js +48 -0
- package/node_modules/semver/functions/valid.js +8 -0
- package/node_modules/semver/index.js +93 -0
- package/node_modules/semver/internal/constants.js +37 -0
- package/node_modules/semver/internal/debug.js +11 -0
- package/node_modules/semver/internal/identifiers.js +29 -0
- package/node_modules/semver/internal/lrucache.js +42 -0
- package/node_modules/semver/internal/parse-options.js +17 -0
- package/node_modules/semver/internal/re.js +223 -0
- package/node_modules/semver/package.json +78 -0
- package/node_modules/semver/preload.js +4 -0
- package/node_modules/semver/range.bnf +17 -0
- package/node_modules/semver/ranges/gtr.js +6 -0
- package/node_modules/semver/ranges/intersects.js +9 -0
- package/node_modules/semver/ranges/ltr.js +6 -0
- package/node_modules/semver/ranges/max-satisfying.js +27 -0
- package/node_modules/semver/ranges/min-satisfying.js +26 -0
- package/node_modules/semver/ranges/min-version.js +63 -0
- package/node_modules/semver/ranges/outside.js +82 -0
- package/node_modules/semver/ranges/simplify.js +49 -0
- package/node_modules/semver/ranges/subset.js +249 -0
- package/node_modules/semver/ranges/to-comparators.js +10 -0
- package/node_modules/semver/ranges/valid.js +13 -0
- package/node_modules/spark-md5/LICENSE +13 -0
- package/node_modules/spark-md5/README.md +191 -0
- package/node_modules/spark-md5/package.json +35 -0
- package/node_modules/spark-md5/spark-md5.js +751 -0
- package/node_modules/spark-md5/spark-md5.min.js +1 -0
- package/node_modules/tldts/LICENSE +13 -0
- package/node_modules/tldts/README.md +327 -0
- package/node_modules/tldts/bin/cli.js +21 -0
- package/node_modules/tldts/dist/cjs/index.js +666 -0
- package/node_modules/tldts/dist/cjs/index.js.map +1 -0
- package/node_modules/tldts/dist/cjs/src/data/trie.js +14 -0
- package/node_modules/tldts/dist/cjs/src/data/trie.js.map +1 -0
- package/node_modules/tldts/dist/cjs/src/suffix-trie.js +67 -0
- package/node_modules/tldts/dist/cjs/src/suffix-trie.js.map +1 -0
- package/node_modules/tldts/dist/cjs/tsconfig.tsbuildinfo +1 -0
- package/node_modules/tldts/dist/es6/index.js +33 -0
- package/node_modules/tldts/dist/es6/index.js.map +1 -0
- package/node_modules/tldts/dist/es6/src/data/trie.js +11 -0
- package/node_modules/tldts/dist/es6/src/data/trie.js.map +1 -0
- package/node_modules/tldts/dist/es6/src/suffix-trie.js +64 -0
- package/node_modules/tldts/dist/es6/src/suffix-trie.js.map +1 -0
- package/node_modules/tldts/dist/es6/tsconfig.bundle.tsbuildinfo +1 -0
- package/node_modules/tldts/dist/index.cjs.min.js +2 -0
- package/node_modules/tldts/dist/index.cjs.min.js.map +1 -0
- package/node_modules/tldts/dist/index.esm.min.js +2 -0
- package/node_modules/tldts/dist/index.esm.min.js.map +1 -0
- package/node_modules/tldts/dist/index.umd.min.js +2 -0
- package/node_modules/tldts/dist/index.umd.min.js.map +1 -0
- package/node_modules/tldts/dist/types/index.d.ts +7 -0
- package/node_modules/tldts/dist/types/src/data/trie.d.ts +5 -0
- package/node_modules/tldts/dist/types/src/suffix-trie.d.ts +5 -0
- package/node_modules/tldts/index.ts +62 -0
- package/node_modules/tldts/package.json +91 -0
- package/node_modules/tldts/src/data/trie.ts +14 -0
- package/node_modules/tldts/src/suffix-trie.ts +110 -0
- package/node_modules/tldts-core/LICENSE +13 -0
- package/node_modules/tldts-core/README.md +3 -0
- package/node_modules/tldts-core/dist/cjs/index.js +561 -0
- package/node_modules/tldts-core/dist/cjs/index.js.map +1 -0
- package/node_modules/tldts-core/dist/cjs/src/domain-without-suffix.js +15 -0
- package/node_modules/tldts-core/dist/cjs/src/domain-without-suffix.js.map +1 -0
- package/node_modules/tldts-core/dist/cjs/src/domain.js +83 -0
- package/node_modules/tldts-core/dist/cjs/src/domain.js.map +1 -0
- package/node_modules/tldts-core/dist/cjs/src/extract-hostname.js +149 -0
- package/node_modules/tldts-core/dist/cjs/src/extract-hostname.js.map +1 -0
- package/node_modules/tldts-core/dist/cjs/src/factory.js +104 -0
- package/node_modules/tldts-core/dist/cjs/src/factory.js.map +1 -0
- package/node_modules/tldts-core/dist/cjs/src/is-ip.js +72 -0
- package/node_modules/tldts-core/dist/cjs/src/is-ip.js.map +1 -0
- package/node_modules/tldts-core/dist/cjs/src/is-valid.js +69 -0
- package/node_modules/tldts-core/dist/cjs/src/is-valid.js.map +1 -0
- package/node_modules/tldts-core/dist/cjs/src/lookup/fast-path.js +69 -0
- package/node_modules/tldts-core/dist/cjs/src/lookup/fast-path.js.map +1 -0
- package/node_modules/tldts-core/dist/cjs/src/lookup/interface.js +3 -0
- package/node_modules/tldts-core/dist/cjs/src/lookup/interface.js.map +1 -0
- package/node_modules/tldts-core/dist/cjs/src/options.js +22 -0
- package/node_modules/tldts-core/dist/cjs/src/options.js.map +1 -0
- package/node_modules/tldts-core/dist/cjs/src/subdomain.js +14 -0
- package/node_modules/tldts-core/dist/cjs/src/subdomain.js.map +1 -0
- package/node_modules/tldts-core/dist/cjs/tsconfig.tsbuildinfo +1 -0
- package/node_modules/tldts-core/dist/es6/index.js +4 -0
- package/node_modules/tldts-core/dist/es6/index.js.map +1 -0
- package/node_modules/tldts-core/dist/es6/src/domain-without-suffix.js +12 -0
- package/node_modules/tldts-core/dist/es6/src/domain-without-suffix.js.map +1 -0
- package/node_modules/tldts-core/dist/es6/src/domain.js +80 -0
- package/node_modules/tldts-core/dist/es6/src/domain.js.map +1 -0
- package/node_modules/tldts-core/dist/es6/src/extract-hostname.js +146 -0
- package/node_modules/tldts-core/dist/es6/src/extract-hostname.js.map +1 -0
- package/node_modules/tldts-core/dist/es6/src/factory.js +99 -0
- package/node_modules/tldts-core/dist/es6/src/factory.js.map +1 -0
- package/node_modules/tldts-core/dist/es6/src/is-ip.js +69 -0
- package/node_modules/tldts-core/dist/es6/src/is-ip.js.map +1 -0
- package/node_modules/tldts-core/dist/es6/src/is-valid.js +66 -0
- package/node_modules/tldts-core/dist/es6/src/is-valid.js.map +1 -0
- package/node_modules/tldts-core/dist/es6/src/lookup/fast-path.js +66 -0
- package/node_modules/tldts-core/dist/es6/src/lookup/fast-path.js.map +1 -0
- package/node_modules/tldts-core/dist/es6/src/lookup/interface.js +2 -0
- package/node_modules/tldts-core/dist/es6/src/lookup/interface.js.map +1 -0
- package/node_modules/tldts-core/dist/es6/src/options.js +19 -0
- package/node_modules/tldts-core/dist/es6/src/options.js.map +1 -0
- package/node_modules/tldts-core/dist/es6/src/subdomain.js +11 -0
- package/node_modules/tldts-core/dist/es6/src/subdomain.js.map +1 -0
- package/node_modules/tldts-core/dist/es6/tsconfig.bundle.tsbuildinfo +1 -0
- package/node_modules/tldts-core/dist/types/index.d.ts +4 -0
- package/node_modules/tldts-core/dist/types/src/domain-without-suffix.d.ts +6 -0
- package/node_modules/tldts-core/dist/types/src/domain.d.ts +5 -0
- package/node_modules/tldts-core/dist/types/src/extract-hostname.d.ts +5 -0
- package/node_modules/tldts-core/dist/types/src/factory.d.ts +28 -0
- package/node_modules/tldts-core/dist/types/src/is-ip.d.ts +6 -0
- package/node_modules/tldts-core/dist/types/src/is-valid.d.ts +15 -0
- package/node_modules/tldts-core/dist/types/src/lookup/fast-path.d.ts +2 -0
- package/node_modules/tldts-core/dist/types/src/lookup/interface.d.ts +9 -0
- package/node_modules/tldts-core/dist/types/src/options.d.ts +10 -0
- package/node_modules/tldts-core/dist/types/src/subdomain.d.ts +4 -0
- package/node_modules/tldts-core/index.ts +10 -0
- package/node_modules/tldts-core/package.json +68 -0
- package/node_modules/tldts-core/src/domain-without-suffix.ts +14 -0
- package/node_modules/tldts-core/src/domain.ts +100 -0
- package/node_modules/tldts-core/src/extract-hostname.ts +170 -0
- package/node_modules/tldts-core/src/factory.ts +160 -0
- package/node_modules/tldts-core/src/is-ip.ts +87 -0
- package/node_modules/tldts-core/src/is-valid.ts +79 -0
- package/node_modules/tldts-core/src/lookup/fast-path.ts +80 -0
- package/node_modules/tldts-core/src/lookup/interface.ts +10 -0
- package/node_modules/tldts-core/src/options.ts +39 -0
- package/node_modules/tldts-core/src/subdomain.ts +11 -0
- package/node_modules/tough-cookie/LICENSE +12 -0
- package/node_modules/tough-cookie/README.md +154 -0
- package/node_modules/tough-cookie/dist/cookie/canonicalDomain.d.ts +33 -0
- package/node_modules/tough-cookie/dist/cookie/canonicalDomain.js +65 -0
- package/node_modules/tough-cookie/dist/cookie/constants.d.ts +54 -0
- package/node_modules/tough-cookie/dist/cookie/constants.js +38 -0
- package/node_modules/tough-cookie/dist/cookie/cookie.d.ts +326 -0
- package/node_modules/tough-cookie/dist/cookie/cookie.js +831 -0
- package/node_modules/tough-cookie/dist/cookie/cookieCompare.d.ts +58 -0
- package/node_modules/tough-cookie/dist/cookie/cookieCompare.js +84 -0
- package/node_modules/tough-cookie/dist/cookie/cookieJar.d.ts +621 -0
- package/node_modules/tough-cookie/dist/cookie/cookieJar.js +1000 -0
- package/node_modules/tough-cookie/dist/cookie/defaultPath.d.ts +40 -0
- package/node_modules/tough-cookie/dist/cookie/defaultPath.js +60 -0
- package/node_modules/tough-cookie/dist/cookie/domainMatch.d.ts +38 -0
- package/node_modules/tough-cookie/dist/cookie/domainMatch.js +94 -0
- package/node_modules/tough-cookie/dist/cookie/formatDate.d.ts +15 -0
- package/node_modules/tough-cookie/dist/cookie/formatDate.js +20 -0
- package/node_modules/tough-cookie/dist/cookie/index.d.ts +29 -0
- package/node_modules/tough-cookie/dist/cookie/index.js +54 -0
- package/node_modules/tough-cookie/dist/cookie/parseDate.d.ts +103 -0
- package/node_modules/tough-cookie/dist/cookie/parseDate.js +323 -0
- package/node_modules/tough-cookie/dist/cookie/permutePath.d.ts +14 -0
- package/node_modules/tough-cookie/dist/cookie/permutePath.js +32 -0
- package/node_modules/tough-cookie/dist/getPublicSuffix.d.ts +55 -0
- package/node_modules/tough-cookie/dist/getPublicSuffix.js +71 -0
- package/node_modules/tough-cookie/dist/memstore.d.ts +220 -0
- package/node_modules/tough-cookie/dist/memstore.js +187 -0
- package/node_modules/tough-cookie/dist/pathMatch.d.ts +17 -0
- package/node_modules/tough-cookie/dist/pathMatch.js +40 -0
- package/node_modules/tough-cookie/dist/permuteDomain.d.ts +15 -0
- package/node_modules/tough-cookie/dist/permuteDomain.js +44 -0
- package/node_modules/tough-cookie/dist/store.d.ts +211 -0
- package/node_modules/tough-cookie/dist/store.js +76 -0
- package/node_modules/tough-cookie/dist/utils.d.ts +34 -0
- package/node_modules/tough-cookie/dist/utils.js +99 -0
- package/node_modules/tough-cookie/dist/validators.d.ts +24 -0
- package/node_modules/tough-cookie/dist/validators.js +90 -0
- package/node_modules/tough-cookie/dist/version.d.ts +5 -0
- package/node_modules/tough-cookie/dist/version.js +8 -0
- package/node_modules/tough-cookie/package.json +144 -0
- package/node_modules/ws/LICENSE +20 -0
- package/node_modules/ws/README.md +548 -0
- package/node_modules/ws/browser.js +8 -0
- package/node_modules/ws/index.js +22 -0
- package/node_modules/ws/lib/buffer-util.js +131 -0
- package/node_modules/ws/lib/constants.js +19 -0
- package/node_modules/ws/lib/event-target.js +292 -0
- package/node_modules/ws/lib/extension.js +203 -0
- package/node_modules/ws/lib/limiter.js +55 -0
- package/node_modules/ws/lib/permessage-deflate.js +530 -0
- package/node_modules/ws/lib/receiver.js +743 -0
- package/node_modules/ws/lib/sender.js +607 -0
- package/node_modules/ws/lib/stream.js +161 -0
- package/node_modules/ws/lib/subprotocol.js +62 -0
- package/node_modules/ws/lib/validation.js +152 -0
- package/node_modules/ws/lib/websocket-server.js +562 -0
- package/node_modules/ws/lib/websocket.js +1407 -0
- package/node_modules/ws/package.json +74 -0
- package/node_modules/ws/wrapper.mjs +21 -0
- package/node_modules/zca-js/.prettierrc +5 -0
- package/node_modules/zca-js/CODE_OF_CONDUCT.md +163 -0
- package/node_modules/zca-js/CONTRIBUTING.md +470 -0
- package/node_modules/zca-js/LICENSE +21 -0
- package/node_modules/zca-js/README.md +225 -0
- package/node_modules/zca-js/SECURITY.md +83 -0
- package/node_modules/zca-js/dist/Errors/ZaloApiError.d.ts +4 -0
- package/node_modules/zca-js/dist/Errors/ZaloApiError.js +7 -0
- package/node_modules/zca-js/dist/Errors/ZaloApiLoginQRAborted.d.ts +4 -0
- package/node_modules/zca-js/dist/Errors/ZaloApiLoginQRAborted.js +7 -0
- package/node_modules/zca-js/dist/Errors/ZaloApiLoginQRDeclined.d.ts +4 -0
- package/node_modules/zca-js/dist/Errors/ZaloApiLoginQRDeclined.js +7 -0
- package/node_modules/zca-js/dist/Errors/ZaloApiMissingImageMetadataGetter.d.ts +4 -0
- package/node_modules/zca-js/dist/Errors/ZaloApiMissingImageMetadataGetter.js +7 -0
- package/node_modules/zca-js/dist/Errors/index.d.ts +4 -0
- package/node_modules/zca-js/dist/Errors/index.js +4 -0
- package/node_modules/zca-js/dist/apis/acceptFriendRequest.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/acceptFriendRequest.js +28 -0
- package/node_modules/zca-js/dist/apis/addGroupBlockedMember.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/addGroupBlockedMember.js +28 -0
- package/node_modules/zca-js/dist/apis/addGroupDeputy.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/addGroupDeputy.js +30 -0
- package/node_modules/zca-js/dist/apis/addPollOptions.d.ts +14 -0
- package/node_modules/zca-js/dist/apis/addPollOptions.js +26 -0
- package/node_modules/zca-js/dist/apis/addQuickMessage.d.ts +11 -0
- package/node_modules/zca-js/dist/apis/addQuickMessage.js +60 -0
- package/node_modules/zca-js/dist/apis/addReaction.d.ts +18 -0
- package/node_modules/zca-js/dist/apis/addReaction.js +294 -0
- package/node_modules/zca-js/dist/apis/addUnreadMark.d.ts +8 -0
- package/node_modules/zca-js/dist/apis/addUnreadMark.js +52 -0
- package/node_modules/zca-js/dist/apis/addUserToGroup.d.ts +5 -0
- package/node_modules/zca-js/dist/apis/addUserToGroup.js +34 -0
- package/node_modules/zca-js/dist/apis/blockUser.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/blockUser.js +28 -0
- package/node_modules/zca-js/dist/apis/blockViewFeed.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/blockViewFeed.js +30 -0
- package/node_modules/zca-js/dist/apis/changeAccountAvatar.d.ts +3 -0
- package/node_modules/zca-js/dist/apis/changeAccountAvatar.js +52 -0
- package/node_modules/zca-js/dist/apis/changeFriendAlias.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/changeFriendAlias.js +27 -0
- package/node_modules/zca-js/dist/apis/changeGroupAvatar.d.ts +3 -0
- package/node_modules/zca-js/dist/apis/changeGroupAvatar.js +44 -0
- package/node_modules/zca-js/dist/apis/changeGroupName.d.ts +4 -0
- package/node_modules/zca-js/dist/apis/changeGroupName.js +32 -0
- package/node_modules/zca-js/dist/apis/changeGroupOwner.d.ts +4 -0
- package/node_modules/zca-js/dist/apis/changeGroupOwner.js +30 -0
- package/node_modules/zca-js/dist/apis/createAutoReply.d.ts +14 -0
- package/node_modules/zca-js/dist/apis/createAutoReply.js +37 -0
- package/node_modules/zca-js/dist/apis/createBankAccount.d.ts +8 -0
- package/node_modules/zca-js/dist/apis/createBankAccount.js +32 -0
- package/node_modules/zca-js/dist/apis/createCatalog.d.ts +7 -0
- package/node_modules/zca-js/dist/apis/createCatalog.js +29 -0
- package/node_modules/zca-js/dist/apis/createGroup.d.ts +28 -0
- package/node_modules/zca-js/dist/apis/createGroup.js +43 -0
- package/node_modules/zca-js/dist/apis/createNote.d.ts +7 -0
- package/node_modules/zca-js/dist/apis/createNote.js +47 -0
- package/node_modules/zca-js/dist/apis/createPoll.d.ts +36 -0
- package/node_modules/zca-js/dist/apis/createPoll.js +40 -0
- package/node_modules/zca-js/dist/apis/createProductCatalog.d.ts +23 -0
- package/node_modules/zca-js/dist/apis/createProductCatalog.js +50 -0
- package/node_modules/zca-js/dist/apis/createReminder.d.ts +12 -0
- package/node_modules/zca-js/dist/apis/createReminder.js +63 -0
- package/node_modules/zca-js/dist/apis/custom.d.ts +9 -0
- package/node_modules/zca-js/dist/apis/custom.js +15 -0
- package/node_modules/zca-js/dist/apis/deleteAutoReply.d.ts +5 -0
- package/node_modules/zca-js/dist/apis/deleteAutoReply.js +29 -0
- package/node_modules/zca-js/dist/apis/deleteAvatar.d.ts +9 -0
- package/node_modules/zca-js/dist/apis/deleteAvatar.js +27 -0
- package/node_modules/zca-js/dist/apis/deleteBankAccount.d.ts +11 -0
- package/node_modules/zca-js/dist/apis/deleteBankAccount.js +32 -0
- package/node_modules/zca-js/dist/apis/deleteCatalog.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/deleteCatalog.js +28 -0
- package/node_modules/zca-js/dist/apis/deleteChat.d.ts +19 -0
- package/node_modules/zca-js/dist/apis/deleteChat.js +50 -0
- package/node_modules/zca-js/dist/apis/deleteGroupInviteBox.d.ts +9 -0
- package/node_modules/zca-js/dist/apis/deleteGroupInviteBox.js +27 -0
- package/node_modules/zca-js/dist/apis/deleteMessage.d.ts +14 -0
- package/node_modules/zca-js/dist/apis/deleteMessage.js +52 -0
- package/node_modules/zca-js/dist/apis/deleteProductCatalog.d.ts +10 -0
- package/node_modules/zca-js/dist/apis/deleteProductCatalog.js +31 -0
- package/node_modules/zca-js/dist/apis/deleteSnapshotMobileMsg.js +18 -0
- package/node_modules/zca-js/dist/apis/disableGroupLink.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/disableGroupLink.js +24 -0
- package/node_modules/zca-js/dist/apis/disperseGroup.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/disperseGroup.js +28 -0
- package/node_modules/zca-js/dist/apis/editNote.d.ts +17 -0
- package/node_modules/zca-js/dist/apis/editNote.js +46 -0
- package/node_modules/zca-js/dist/apis/editReminder.d.ts +13 -0
- package/node_modules/zca-js/dist/apis/editReminder.js +61 -0
- package/node_modules/zca-js/dist/apis/enableGroupLink.d.ts +6 -0
- package/node_modules/zca-js/dist/apis/enableGroupLink.js +25 -0
- package/node_modules/zca-js/dist/apis/fetchAccountInfo.d.ts +5 -0
- package/node_modules/zca-js/dist/apis/fetchAccountInfo.js +10 -0
- package/node_modules/zca-js/dist/apis/findUser.d.ts +3 -0
- package/node_modules/zca-js/dist/apis/findUser.js +42 -0
- package/node_modules/zca-js/dist/apis/findUserByUsername.d.ts +3 -0
- package/node_modules/zca-js/dist/apis/findUserByUsername.js +27 -0
- package/node_modules/zca-js/dist/apis/forwardMessage.d.ts +24 -0
- package/node_modules/zca-js/dist/apis/forwardMessage.js +114 -0
- package/node_modules/zca-js/dist/apis/getAliasList.d.ts +8 -0
- package/node_modules/zca-js/dist/apis/getAliasList.js +27 -0
- package/node_modules/zca-js/dist/apis/getAllFriends.d.ts +3 -0
- package/node_modules/zca-js/dist/apis/getAllFriends.js +34 -0
- package/node_modules/zca-js/dist/apis/getAllGroups.d.ts +7 -0
- package/node_modules/zca-js/dist/apis/getAllGroups.js +10 -0
- package/node_modules/zca-js/dist/apis/getArchivedChatList.d.ts +5 -0
- package/node_modules/zca-js/dist/apis/getArchivedChatList.js +23 -0
- package/node_modules/zca-js/dist/apis/getAutoDeleteChat.d.ts +9 -0
- package/node_modules/zca-js/dist/apis/getAutoDeleteChat.js +21 -0
- package/node_modules/zca-js/dist/apis/getAutoReplyList.d.ts +6 -0
- package/node_modules/zca-js/dist/apis/getAutoReplyList.js +24 -0
- package/node_modules/zca-js/dist/apis/getAvatarList.d.ts +12 -0
- package/node_modules/zca-js/dist/apis/getAvatarList.js +28 -0
- package/node_modules/zca-js/dist/apis/getAvatarUrlProfile.d.ts +7 -0
- package/node_modules/zca-js/dist/apis/getAvatarUrlProfile.js +30 -0
- package/node_modules/zca-js/dist/apis/getBizAccount.d.ts +24 -0
- package/node_modules/zca-js/dist/apis/getBizAccount.js +27 -0
- package/node_modules/zca-js/dist/apis/getCatalogList.d.ts +18 -0
- package/node_modules/zca-js/dist/apis/getCatalogList.js +32 -0
- package/node_modules/zca-js/dist/apis/getCloseFriends.d.ts +3 -0
- package/node_modules/zca-js/dist/apis/getCloseFriends.js +20 -0
- package/node_modules/zca-js/dist/apis/getContext.d.ts +1 -0
- package/node_modules/zca-js/dist/apis/getContext.js +4 -0
- package/node_modules/zca-js/dist/apis/getCookie.d.ts +1 -0
- package/node_modules/zca-js/dist/apis/getCookie.js +9 -0
- package/node_modules/zca-js/dist/apis/getCrossDB.js +18 -0
- package/node_modules/zca-js/dist/apis/getFriendBoardList.d.ts +5 -0
- package/node_modules/zca-js/dist/apis/getFriendBoardList.js +26 -0
- package/node_modules/zca-js/dist/apis/getFriendOnlines.d.ts +10 -0
- package/node_modules/zca-js/dist/apis/getFriendOnlines.js +35 -0
- package/node_modules/zca-js/dist/apis/getFriendRecommendations.d.ts +42 -0
- package/node_modules/zca-js/dist/apis/getFriendRecommendations.js +27 -0
- package/node_modules/zca-js/dist/apis/getFriendRequestStatus.d.ts +8 -0
- package/node_modules/zca-js/dist/apis/getFriendRequestStatus.js +25 -0
- package/node_modules/zca-js/dist/apis/getFullAvatar.d.ts +5 -0
- package/node_modules/zca-js/dist/apis/getFullAvatar.js +25 -0
- package/node_modules/zca-js/dist/apis/getGroupBlockedMember.d.ts +23 -0
- package/node_modules/zca-js/dist/apis/getGroupBlockedMember.js +29 -0
- package/node_modules/zca-js/dist/apis/getGroupChatHistory.d.ts +8 -0
- package/node_modules/zca-js/dist/apis/getGroupChatHistory.js +36 -0
- package/node_modules/zca-js/dist/apis/getGroupInfo.d.ts +16 -0
- package/node_modules/zca-js/dist/apis/getGroupInfo.js +32 -0
- package/node_modules/zca-js/dist/apis/getGroupInviteBoxInfo.d.ts +32 -0
- package/node_modules/zca-js/dist/apis/getGroupInviteBoxInfo.js +38 -0
- package/node_modules/zca-js/dist/apis/getGroupInviteBoxList.d.ts +38 -0
- package/node_modules/zca-js/dist/apis/getGroupInviteBoxList.js +31 -0
- package/node_modules/zca-js/dist/apis/getGroupLinkDetail.d.ts +9 -0
- package/node_modules/zca-js/dist/apis/getGroupLinkDetail.js +25 -0
- package/node_modules/zca-js/dist/apis/getGroupLinkInfo.d.ts +34 -0
- package/node_modules/zca-js/dist/apis/getGroupLinkInfo.js +28 -0
- package/node_modules/zca-js/dist/apis/getGroupMembersInfo.d.ts +17 -0
- package/node_modules/zca-js/dist/apis/getGroupMembersInfo.js +24 -0
- package/node_modules/zca-js/dist/apis/getHiddenConversations.d.ts +11 -0
- package/node_modules/zca-js/dist/apis/getHiddenConversations.js +23 -0
- package/node_modules/zca-js/dist/apis/getLabels.d.ts +7 -0
- package/node_modules/zca-js/dist/apis/getLabels.js +28 -0
- package/node_modules/zca-js/dist/apis/getListBank.d.ts +5 -0
- package/node_modules/zca-js/dist/apis/getListBank.js +20 -0
- package/node_modules/zca-js/dist/apis/getListBankAccount.d.ts +7 -0
- package/node_modules/zca-js/dist/apis/getListBankAccount.js +27 -0
- package/node_modules/zca-js/dist/apis/getListBoard.d.ts +21 -0
- package/node_modules/zca-js/dist/apis/getListBoard.js +45 -0
- package/node_modules/zca-js/dist/apis/getListDevice.d.ts +10 -0
- package/node_modules/zca-js/dist/apis/getListDevice.js +22 -0
- package/node_modules/zca-js/dist/apis/getListReminder.d.ts +28 -0
- package/node_modules/zca-js/dist/apis/getListReminder.js +47 -0
- package/node_modules/zca-js/dist/apis/getMultiUsersByPhones.d.ts +5 -0
- package/node_modules/zca-js/dist/apis/getMultiUsersByPhones.js +39 -0
- package/node_modules/zca-js/dist/apis/getMute.d.ts +13 -0
- package/node_modules/zca-js/dist/apis/getMute.js +22 -0
- package/node_modules/zca-js/dist/apis/getOwnId.d.ts +1 -0
- package/node_modules/zca-js/dist/apis/getOwnId.js +4 -0
- package/node_modules/zca-js/dist/apis/getPendingGroupMembers.d.ts +11 -0
- package/node_modules/zca-js/dist/apis/getPendingGroupMembers.js +27 -0
- package/node_modules/zca-js/dist/apis/getPinConversations.d.ts +5 -0
- package/node_modules/zca-js/dist/apis/getPinConversations.js +22 -0
- package/node_modules/zca-js/dist/apis/getPollDetail.d.ts +3 -0
- package/node_modules/zca-js/dist/apis/getPollDetail.js +30 -0
- package/node_modules/zca-js/dist/apis/getProductCatalogList.d.ts +20 -0
- package/node_modules/zca-js/dist/apis/getProductCatalogList.js +33 -0
- package/node_modules/zca-js/dist/apis/getQR.d.ts +4 -0
- package/node_modules/zca-js/dist/apis/getQR.js +29 -0
- package/node_modules/zca-js/dist/apis/getQuickMessageList.d.ts +7 -0
- package/node_modules/zca-js/dist/apis/getQuickMessageList.js +24 -0
- package/node_modules/zca-js/dist/apis/getRelatedFriendGroup.d.ts +6 -0
- package/node_modules/zca-js/dist/apis/getRelatedFriendGroup.js +30 -0
- package/node_modules/zca-js/dist/apis/getReminder.d.ts +3 -0
- package/node_modules/zca-js/dist/apis/getReminder.js +25 -0
- package/node_modules/zca-js/dist/apis/getReminderResponses.d.ts +5 -0
- package/node_modules/zca-js/dist/apis/getReminderResponses.js +24 -0
- package/node_modules/zca-js/dist/apis/getSentFriendRequest.d.ts +18 -0
- package/node_modules/zca-js/dist/apis/getSentFriendRequest.js +24 -0
- package/node_modules/zca-js/dist/apis/getSettings.d.ts +3 -0
- package/node_modules/zca-js/dist/apis/getSettings.js +20 -0
- package/node_modules/zca-js/dist/apis/getStickerCategoryDetail.d.ts +3 -0
- package/node_modules/zca-js/dist/apis/getStickerCategoryDetail.js +24 -0
- package/node_modules/zca-js/dist/apis/getStickers.d.ts +1 -0
- package/node_modules/zca-js/dist/apis/getStickers.js +40 -0
- package/node_modules/zca-js/dist/apis/getStickersDetail.d.ts +3 -0
- package/node_modules/zca-js/dist/apis/getStickersDetail.js +40 -0
- package/node_modules/zca-js/dist/apis/getUnreadMark.d.ts +14 -0
- package/node_modules/zca-js/dist/apis/getUnreadMark.js +30 -0
- package/node_modules/zca-js/dist/apis/getUserInfo.d.ts +8 -0
- package/node_modules/zca-js/dist/apis/getUserInfo.js +44 -0
- package/node_modules/zca-js/dist/apis/inviteUserToGroups.d.ts +10 -0
- package/node_modules/zca-js/dist/apis/inviteUserToGroups.js +30 -0
- package/node_modules/zca-js/dist/apis/joinGroupInviteBox.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/joinGroupInviteBox.js +25 -0
- package/node_modules/zca-js/dist/apis/joinGroupLink.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/joinGroupLink.js +27 -0
- package/node_modules/zca-js/dist/apis/keepAlive.d.ts +4 -0
- package/node_modules/zca-js/dist/apis/keepAlive.js +22 -0
- package/node_modules/zca-js/dist/apis/lastOnline.d.ts +7 -0
- package/node_modules/zca-js/dist/apis/lastOnline.js +26 -0
- package/node_modules/zca-js/dist/apis/leaveGroup.d.ts +4 -0
- package/node_modules/zca-js/dist/apis/leaveGroup.js +33 -0
- package/node_modules/zca-js/dist/apis/listen.d.ts +102 -0
- package/node_modules/zca-js/dist/apis/listen.js +425 -0
- package/node_modules/zca-js/dist/apis/lockPoll.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/lockPoll.js +28 -0
- package/node_modules/zca-js/dist/apis/login.d.ts +3 -0
- package/node_modules/zca-js/dist/apis/login.js +96 -0
- package/node_modules/zca-js/dist/apis/loginQR.d.ts +71 -0
- package/node_modules/zca-js/dist/apis/loginQR.js +376 -0
- package/node_modules/zca-js/dist/apis/logoutV2.js +79 -0
- package/node_modules/zca-js/dist/apis/lostFocus.d.ts +4 -0
- package/node_modules/zca-js/dist/apis/lostFocus.js +23 -0
- package/node_modules/zca-js/dist/apis/parseLink.d.ts +21 -0
- package/node_modules/zca-js/dist/apis/parseLink.js +27 -0
- package/node_modules/zca-js/dist/apis/pullMobileMsg.js +22 -0
- package/node_modules/zca-js/dist/apis/registerCatalog.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/registerCatalog.js +27 -0
- package/node_modules/zca-js/dist/apis/rejectFriendRequest.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/rejectFriendRequest.js +27 -0
- package/node_modules/zca-js/dist/apis/removeFriend.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/removeFriend.js +28 -0
- package/node_modules/zca-js/dist/apis/removeFriendAlias.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/removeFriendAlias.js +24 -0
- package/node_modules/zca-js/dist/apis/removeGroupBlockedMember.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/removeGroupBlockedMember.js +28 -0
- package/node_modules/zca-js/dist/apis/removeGroupDeputy.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/removeGroupDeputy.js +30 -0
- package/node_modules/zca-js/dist/apis/removeQuickMessage.d.ts +5 -0
- package/node_modules/zca-js/dist/apis/removeQuickMessage.js +27 -0
- package/node_modules/zca-js/dist/apis/removeReminder.d.ts +3 -0
- package/node_modules/zca-js/dist/apis/removeReminder.js +40 -0
- package/node_modules/zca-js/dist/apis/removeUnreadMark.d.ts +8 -0
- package/node_modules/zca-js/dist/apis/removeUnreadMark.js +50 -0
- package/node_modules/zca-js/dist/apis/removeUserFromGroup.d.ts +4 -0
- package/node_modules/zca-js/dist/apis/removeUserFromGroup.js +34 -0
- package/node_modules/zca-js/dist/apis/requestSyncMessage.js +18 -0
- package/node_modules/zca-js/dist/apis/resetHiddenConversPin.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/resetHiddenConversPin.js +20 -0
- package/node_modules/zca-js/dist/apis/reuseAvatar.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/reuseAvatar.js +26 -0
- package/node_modules/zca-js/dist/apis/reviewPendingMemberRequest.d.ts +14 -0
- package/node_modules/zca-js/dist/apis/reviewPendingMemberRequest.js +38 -0
- package/node_modules/zca-js/dist/apis/scanURL.d.ts +4 -0
- package/node_modules/zca-js/dist/apis/scanURL.js +28 -0
- package/node_modules/zca-js/dist/apis/searchSticker.d.ts +3 -0
- package/node_modules/zca-js/dist/apis/searchSticker.js +28 -0
- package/node_modules/zca-js/dist/apis/sendBankCard.d.ts +9 -0
- package/node_modules/zca-js/dist/apis/sendBankCard.js +53 -0
- package/node_modules/zca-js/dist/apis/sendCard.d.ts +10 -0
- package/node_modules/zca-js/dist/apis/sendCard.js +75 -0
- package/node_modules/zca-js/dist/apis/sendDeliveredEvent.d.ts +16 -0
- package/node_modules/zca-js/dist/apis/sendDeliveredEvent.js +55 -0
- package/node_modules/zca-js/dist/apis/sendFriendRequest.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/sendFriendRequest.js +39 -0
- package/node_modules/zca-js/dist/apis/sendLink.d.ts +10 -0
- package/node_modules/zca-js/dist/apis/sendLink.js +78 -0
- package/node_modules/zca-js/dist/apis/sendMessage.d.ts +105 -0
- package/node_modules/zca-js/dist/apis/sendMessage.js +486 -0
- package/node_modules/zca-js/dist/apis/sendReport.d.ts +17 -0
- package/node_modules/zca-js/dist/apis/sendReport.js +53 -0
- package/node_modules/zca-js/dist/apis/sendSeenEvent.d.ts +16 -0
- package/node_modules/zca-js/dist/apis/sendSeenEvent.js +63 -0
- package/node_modules/zca-js/dist/apis/sendSticker.d.ts +10 -0
- package/node_modules/zca-js/dist/apis/sendSticker.js +74 -0
- package/node_modules/zca-js/dist/apis/sendTypingEvent.d.ts +5 -0
- package/node_modules/zca-js/dist/apis/sendTypingEvent.js +33 -0
- package/node_modules/zca-js/dist/apis/sendVideo.d.ts +35 -0
- package/node_modules/zca-js/dist/apis/sendVideo.js +134 -0
- package/node_modules/zca-js/dist/apis/sendVoice.d.ts +12 -0
- package/node_modules/zca-js/dist/apis/sendVoice.js +89 -0
- package/node_modules/zca-js/dist/apis/setHiddenConversations.d.ts +3 -0
- package/node_modules/zca-js/dist/apis/setHiddenConversations.js +39 -0
- package/node_modules/zca-js/dist/apis/setMute.d.ts +20 -0
- package/node_modules/zca-js/dist/apis/setMute.js +67 -0
- package/node_modules/zca-js/dist/apis/setPinnedConversations.d.ts +3 -0
- package/node_modules/zca-js/dist/apis/setPinnedConversations.js +34 -0
- package/node_modules/zca-js/dist/apis/sharePoll.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/sharePoll.js +28 -0
- package/node_modules/zca-js/dist/apis/unblockUser.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/unblockUser.js +28 -0
- package/node_modules/zca-js/dist/apis/undo.d.ts +9 -0
- package/node_modules/zca-js/dist/apis/undo.js +42 -0
- package/node_modules/zca-js/dist/apis/undoFriendRequest.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/undoFriendRequest.js +27 -0
- package/node_modules/zca-js/dist/apis/updateActiveStatus.d.ts +4 -0
- package/node_modules/zca-js/dist/apis/updateActiveStatus.js +27 -0
- package/node_modules/zca-js/dist/apis/updateArchivedChatList.d.ts +10 -0
- package/node_modules/zca-js/dist/apis/updateArchivedChatList.js +34 -0
- package/node_modules/zca-js/dist/apis/updateAutoDeleteChat.d.ts +9 -0
- package/node_modules/zca-js/dist/apis/updateAutoDeleteChat.js +40 -0
- package/node_modules/zca-js/dist/apis/updateAutoReply.d.ts +15 -0
- package/node_modules/zca-js/dist/apis/updateAutoReply.js +38 -0
- package/node_modules/zca-js/dist/apis/updateBankAccount.d.ts +9 -0
- package/node_modules/zca-js/dist/apis/updateBankAccount.js +31 -0
- package/node_modules/zca-js/dist/apis/updateCatalog.d.ts +11 -0
- package/node_modules/zca-js/dist/apis/updateCatalog.js +30 -0
- package/node_modules/zca-js/dist/apis/updateGroupSettings.d.ts +40 -0
- package/node_modules/zca-js/dist/apis/updateGroupSettings.js +43 -0
- package/node_modules/zca-js/dist/apis/updateHiddenConversPin.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/updateHiddenConversPin.js +30 -0
- package/node_modules/zca-js/dist/apis/updateLabels.d.ts +15 -0
- package/node_modules/zca-js/dist/apis/updateLabels.js +34 -0
- package/node_modules/zca-js/dist/apis/updateLang.d.ts +6 -0
- package/node_modules/zca-js/dist/apis/updateLang.js +30 -0
- package/node_modules/zca-js/dist/apis/updateProductCatalog.d.ts +25 -0
- package/node_modules/zca-js/dist/apis/updateProductCatalog.js +51 -0
- package/node_modules/zca-js/dist/apis/updateProfile.d.ts +20 -0
- package/node_modules/zca-js/dist/apis/updateProfile.js +43 -0
- package/node_modules/zca-js/dist/apis/updateProfileBio.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/updateProfileBio.js +27 -0
- package/node_modules/zca-js/dist/apis/updateQuickMessage.d.ts +11 -0
- package/node_modules/zca-js/dist/apis/updateQuickMessage.js +61 -0
- package/node_modules/zca-js/dist/apis/updateSettings.d.ts +16 -0
- package/node_modules/zca-js/dist/apis/updateSettings.js +90 -0
- package/node_modules/zca-js/dist/apis/upgradeGroupToCommunity.d.ts +2 -0
- package/node_modules/zca-js/dist/apis/upgradeGroupToCommunity.js +27 -0
- package/node_modules/zca-js/dist/apis/uploadAttachment.d.ts +50 -0
- package/node_modules/zca-js/dist/apis/uploadAttachment.js +197 -0
- package/node_modules/zca-js/dist/apis/uploadProductPhoto.d.ts +14 -0
- package/node_modules/zca-js/dist/apis/uploadProductPhoto.js +50 -0
- package/node_modules/zca-js/dist/apis/votePoll.d.ts +5 -0
- package/node_modules/zca-js/dist/apis/votePoll.js +29 -0
- package/node_modules/zca-js/dist/apis.d.ts +317 -0
- package/node_modules/zca-js/dist/apis.js +327 -0
- package/node_modules/zca-js/dist/cjs/Errors/ZaloApiError.cjs +11 -0
- package/node_modules/zca-js/dist/cjs/Errors/ZaloApiLoginQRAborted.cjs +12 -0
- package/node_modules/zca-js/dist/cjs/Errors/ZaloApiLoginQRDeclined.cjs +12 -0
- package/node_modules/zca-js/dist/cjs/Errors/ZaloApiMissingImageMetadataGetter.cjs +12 -0
- package/node_modules/zca-js/dist/cjs/apis/acceptFriendRequest.cjs +33 -0
- package/node_modules/zca-js/dist/cjs/apis/addGroupBlockedMember.cjs +33 -0
- package/node_modules/zca-js/dist/cjs/apis/addGroupDeputy.cjs +35 -0
- package/node_modules/zca-js/dist/cjs/apis/addPollOptions.cjs +31 -0
- package/node_modules/zca-js/dist/cjs/apis/addQuickMessage.cjs +65 -0
- package/node_modules/zca-js/dist/cjs/apis/addReaction.cjs +308 -0
- package/node_modules/zca-js/dist/cjs/apis/addUnreadMark.cjs +66 -0
- package/node_modules/zca-js/dist/cjs/apis/addUserToGroup.cjs +39 -0
- package/node_modules/zca-js/dist/cjs/apis/blockUser.cjs +33 -0
- package/node_modules/zca-js/dist/cjs/apis/blockViewFeed.cjs +35 -0
- package/node_modules/zca-js/dist/cjs/apis/changeAccountAvatar.cjs +57 -0
- package/node_modules/zca-js/dist/cjs/apis/changeFriendAlias.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/changeGroupAvatar.cjs +49 -0
- package/node_modules/zca-js/dist/cjs/apis/changeGroupName.cjs +37 -0
- package/node_modules/zca-js/dist/cjs/apis/changeGroupOwner.cjs +35 -0
- package/node_modules/zca-js/dist/cjs/apis/createAutoReply.cjs +42 -0
- package/node_modules/zca-js/dist/cjs/apis/createBankAccount.cjs +37 -0
- package/node_modules/zca-js/dist/cjs/apis/createCatalog.cjs +34 -0
- package/node_modules/zca-js/dist/cjs/apis/createGroup.cjs +48 -0
- package/node_modules/zca-js/dist/cjs/apis/createNote.cjs +52 -0
- package/node_modules/zca-js/dist/cjs/apis/createPoll.cjs +45 -0
- package/node_modules/zca-js/dist/cjs/apis/createProductCatalog.cjs +55 -0
- package/node_modules/zca-js/dist/cjs/apis/createReminder.cjs +77 -0
- package/node_modules/zca-js/dist/cjs/apis/custom.cjs +19 -0
- package/node_modules/zca-js/dist/cjs/apis/deleteAutoReply.cjs +34 -0
- package/node_modules/zca-js/dist/cjs/apis/deleteAvatar.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/deleteBankAccount.cjs +37 -0
- package/node_modules/zca-js/dist/cjs/apis/deleteCatalog.cjs +33 -0
- package/node_modules/zca-js/dist/cjs/apis/deleteChat.cjs +64 -0
- package/node_modules/zca-js/dist/cjs/apis/deleteGroupInviteBox.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/deleteMessage.cjs +66 -0
- package/node_modules/zca-js/dist/cjs/apis/deleteProductCatalog.cjs +36 -0
- package/node_modules/zca-js/dist/cjs/apis/deleteSnapshotMobileMsg.cjs +22 -0
- package/node_modules/zca-js/dist/cjs/apis/disableGroupLink.cjs +29 -0
- package/node_modules/zca-js/dist/cjs/apis/disperseGroup.cjs +33 -0
- package/node_modules/zca-js/dist/cjs/apis/editNote.cjs +51 -0
- package/node_modules/zca-js/dist/cjs/apis/editReminder.cjs +75 -0
- package/node_modules/zca-js/dist/cjs/apis/enableGroupLink.cjs +30 -0
- package/node_modules/zca-js/dist/cjs/apis/fetchAccountInfo.cjs +15 -0
- package/node_modules/zca-js/dist/cjs/apis/findUser.cjs +56 -0
- package/node_modules/zca-js/dist/cjs/apis/findUserByUsername.cjs +41 -0
- package/node_modules/zca-js/dist/cjs/apis/forwardMessage.cjs +129 -0
- package/node_modules/zca-js/dist/cjs/apis/getAliasList.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/getAllFriends.cjs +48 -0
- package/node_modules/zca-js/dist/cjs/apis/getAllGroups.cjs +15 -0
- package/node_modules/zca-js/dist/cjs/apis/getArchivedChatList.cjs +28 -0
- package/node_modules/zca-js/dist/cjs/apis/getAutoDeleteChat.cjs +26 -0
- package/node_modules/zca-js/dist/cjs/apis/getAutoReplyList.cjs +29 -0
- package/node_modules/zca-js/dist/cjs/apis/getAvatarList.cjs +33 -0
- package/node_modules/zca-js/dist/cjs/apis/getAvatarUrlProfile.cjs +44 -0
- package/node_modules/zca-js/dist/cjs/apis/getBizAccount.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/getCatalogList.cjs +37 -0
- package/node_modules/zca-js/dist/cjs/apis/getCloseFriends.cjs +25 -0
- package/node_modules/zca-js/dist/cjs/apis/getContext.cjs +9 -0
- package/node_modules/zca-js/dist/cjs/apis/getCookie.cjs +14 -0
- package/node_modules/zca-js/dist/cjs/apis/getCrossDB.cjs +22 -0
- package/node_modules/zca-js/dist/cjs/apis/getFriendBoardList.cjs +31 -0
- package/node_modules/zca-js/dist/cjs/apis/getFriendOnlines.cjs +40 -0
- package/node_modules/zca-js/dist/cjs/apis/getFriendRecommendations.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/getFriendRequestStatus.cjs +30 -0
- package/node_modules/zca-js/dist/cjs/apis/getFullAvatar.cjs +30 -0
- package/node_modules/zca-js/dist/cjs/apis/getGroupBlockedMember.cjs +34 -0
- package/node_modules/zca-js/dist/cjs/apis/getGroupChatHistory.cjs +51 -0
- package/node_modules/zca-js/dist/cjs/apis/getGroupInfo.cjs +37 -0
- package/node_modules/zca-js/dist/cjs/apis/getGroupInviteBoxInfo.cjs +43 -0
- package/node_modules/zca-js/dist/cjs/apis/getGroupInviteBoxList.cjs +36 -0
- package/node_modules/zca-js/dist/cjs/apis/getGroupLinkDetail.cjs +30 -0
- package/node_modules/zca-js/dist/cjs/apis/getGroupLinkInfo.cjs +33 -0
- package/node_modules/zca-js/dist/cjs/apis/getGroupMembersInfo.cjs +29 -0
- package/node_modules/zca-js/dist/cjs/apis/getHiddenConversations.cjs +28 -0
- package/node_modules/zca-js/dist/cjs/apis/getLabels.cjs +33 -0
- package/node_modules/zca-js/dist/cjs/apis/getListBank.cjs +25 -0
- package/node_modules/zca-js/dist/cjs/apis/getListBankAccount.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/getListBoard.cjs +59 -0
- package/node_modules/zca-js/dist/cjs/apis/getListDevice.cjs +27 -0
- package/node_modules/zca-js/dist/cjs/apis/getListReminder.cjs +61 -0
- package/node_modules/zca-js/dist/cjs/apis/getMultiUsersByPhones.cjs +53 -0
- package/node_modules/zca-js/dist/cjs/apis/getMute.cjs +27 -0
- package/node_modules/zca-js/dist/cjs/apis/getOwnId.cjs +9 -0
- package/node_modules/zca-js/dist/cjs/apis/getPendingGroupMembers.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/getPinConversations.cjs +27 -0
- package/node_modules/zca-js/dist/cjs/apis/getPollDetail.cjs +35 -0
- package/node_modules/zca-js/dist/cjs/apis/getProductCatalogList.cjs +38 -0
- package/node_modules/zca-js/dist/cjs/apis/getQR.cjs +34 -0
- package/node_modules/zca-js/dist/cjs/apis/getQuickMessageList.cjs +29 -0
- package/node_modules/zca-js/dist/cjs/apis/getRelatedFriendGroup.cjs +35 -0
- package/node_modules/zca-js/dist/cjs/apis/getReminder.cjs +30 -0
- package/node_modules/zca-js/dist/cjs/apis/getReminderResponses.cjs +29 -0
- package/node_modules/zca-js/dist/cjs/apis/getSentFriendRequest.cjs +29 -0
- package/node_modules/zca-js/dist/cjs/apis/getSettings.cjs +25 -0
- package/node_modules/zca-js/dist/cjs/apis/getStickerCategoryDetail.cjs +29 -0
- package/node_modules/zca-js/dist/cjs/apis/getStickers.cjs +45 -0
- package/node_modules/zca-js/dist/cjs/apis/getStickersDetail.cjs +45 -0
- package/node_modules/zca-js/dist/cjs/apis/getUnreadMark.cjs +35 -0
- package/node_modules/zca-js/dist/cjs/apis/getUserInfo.cjs +58 -0
- package/node_modules/zca-js/dist/cjs/apis/inviteUserToGroups.cjs +35 -0
- package/node_modules/zca-js/dist/cjs/apis/joinGroupInviteBox.cjs +30 -0
- package/node_modules/zca-js/dist/cjs/apis/joinGroupLink.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/keepAlive.cjs +27 -0
- package/node_modules/zca-js/dist/cjs/apis/lastOnline.cjs +31 -0
- package/node_modules/zca-js/dist/cjs/apis/leaveGroup.cjs +38 -0
- package/node_modules/zca-js/dist/cjs/apis/listen.cjs +440 -0
- package/node_modules/zca-js/dist/cjs/apis/lockPoll.cjs +33 -0
- package/node_modules/zca-js/dist/cjs/apis/login.cjs +102 -0
- package/node_modules/zca-js/dist/cjs/apis/loginQR.cjs +368 -0
- package/node_modules/zca-js/dist/cjs/apis/logoutV2.cjs +83 -0
- package/node_modules/zca-js/dist/cjs/apis/lostFocus.cjs +28 -0
- package/node_modules/zca-js/dist/cjs/apis/parseLink.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/pullMobileMsg.cjs +26 -0
- package/node_modules/zca-js/dist/cjs/apis/registerCatalog.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/rejectFriendRequest.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/removeFriend.cjs +33 -0
- package/node_modules/zca-js/dist/cjs/apis/removeFriendAlias.cjs +29 -0
- package/node_modules/zca-js/dist/cjs/apis/removeGroupBlockedMember.cjs +33 -0
- package/node_modules/zca-js/dist/cjs/apis/removeGroupDeputy.cjs +35 -0
- package/node_modules/zca-js/dist/cjs/apis/removeQuickMessage.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/removeReminder.cjs +54 -0
- package/node_modules/zca-js/dist/cjs/apis/removeUnreadMark.cjs +64 -0
- package/node_modules/zca-js/dist/cjs/apis/removeUserFromGroup.cjs +39 -0
- package/node_modules/zca-js/dist/cjs/apis/requestSyncMessage.cjs +22 -0
- package/node_modules/zca-js/dist/cjs/apis/resetHiddenConversPin.cjs +25 -0
- package/node_modules/zca-js/dist/cjs/apis/reuseAvatar.cjs +31 -0
- package/node_modules/zca-js/dist/cjs/apis/reviewPendingMemberRequest.cjs +43 -0
- package/node_modules/zca-js/dist/cjs/apis/scanURL.cjs +33 -0
- package/node_modules/zca-js/dist/cjs/apis/searchSticker.cjs +33 -0
- package/node_modules/zca-js/dist/cjs/apis/sendBankCard.cjs +68 -0
- package/node_modules/zca-js/dist/cjs/apis/sendCard.cjs +89 -0
- package/node_modules/zca-js/dist/cjs/apis/sendDeliveredEvent.cjs +69 -0
- package/node_modules/zca-js/dist/cjs/apis/sendFriendRequest.cjs +44 -0
- package/node_modules/zca-js/dist/cjs/apis/sendLink.cjs +92 -0
- package/node_modules/zca-js/dist/cjs/apis/sendMessage.cjs +500 -0
- package/node_modules/zca-js/dist/cjs/apis/sendReport.cjs +67 -0
- package/node_modules/zca-js/dist/cjs/apis/sendSeenEvent.cjs +77 -0
- package/node_modules/zca-js/dist/cjs/apis/sendSticker.cjs +88 -0
- package/node_modules/zca-js/dist/cjs/apis/sendTypingEvent.cjs +47 -0
- package/node_modules/zca-js/dist/cjs/apis/sendVideo.cjs +148 -0
- package/node_modules/zca-js/dist/cjs/apis/sendVoice.cjs +103 -0
- package/node_modules/zca-js/dist/cjs/apis/setHiddenConversations.cjs +53 -0
- package/node_modules/zca-js/dist/cjs/apis/setMute.cjs +81 -0
- package/node_modules/zca-js/dist/cjs/apis/setPinnedConversations.cjs +48 -0
- package/node_modules/zca-js/dist/cjs/apis/sharePoll.cjs +33 -0
- package/node_modules/zca-js/dist/cjs/apis/unblockUser.cjs +33 -0
- package/node_modules/zca-js/dist/cjs/apis/undo.cjs +56 -0
- package/node_modules/zca-js/dist/cjs/apis/undoFriendRequest.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/updateActiveStatus.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/updateArchivedChatList.cjs +39 -0
- package/node_modules/zca-js/dist/cjs/apis/updateAutoDeleteChat.cjs +54 -0
- package/node_modules/zca-js/dist/cjs/apis/updateAutoReply.cjs +43 -0
- package/node_modules/zca-js/dist/cjs/apis/updateBankAccount.cjs +36 -0
- package/node_modules/zca-js/dist/cjs/apis/updateCatalog.cjs +35 -0
- package/node_modules/zca-js/dist/cjs/apis/updateGroupSettings.cjs +48 -0
- package/node_modules/zca-js/dist/cjs/apis/updateHiddenConversPin.cjs +35 -0
- package/node_modules/zca-js/dist/cjs/apis/updateLabels.cjs +39 -0
- package/node_modules/zca-js/dist/cjs/apis/updateLang.cjs +35 -0
- package/node_modules/zca-js/dist/cjs/apis/updateProductCatalog.cjs +56 -0
- package/node_modules/zca-js/dist/cjs/apis/updateProfile.cjs +48 -0
- package/node_modules/zca-js/dist/cjs/apis/updateProfileBio.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/updateQuickMessage.cjs +66 -0
- package/node_modules/zca-js/dist/cjs/apis/updateSettings.cjs +95 -0
- package/node_modules/zca-js/dist/cjs/apis/upgradeGroupToCommunity.cjs +32 -0
- package/node_modules/zca-js/dist/cjs/apis/uploadAttachment.cjs +211 -0
- package/node_modules/zca-js/dist/cjs/apis/uploadProductPhoto.cjs +55 -0
- package/node_modules/zca-js/dist/cjs/apis/votePoll.cjs +34 -0
- package/node_modules/zca-js/dist/cjs/apis.cjs +332 -0
- package/node_modules/zca-js/dist/cjs/context.cjs +34 -0
- package/node_modules/zca-js/dist/cjs/index.cjs +159 -0
- package/node_modules/zca-js/dist/cjs/models/AutoReply.cjs +9 -0
- package/node_modules/zca-js/dist/cjs/models/Bank.cjs +263 -0
- package/node_modules/zca-js/dist/cjs/models/Board.cjs +8 -0
- package/node_modules/zca-js/dist/cjs/models/DeliveredMessage.cjs +23 -0
- package/node_modules/zca-js/dist/cjs/models/Enum.cjs +27 -0
- package/node_modules/zca-js/dist/cjs/models/FriendEvent.cjs +87 -0
- package/node_modules/zca-js/dist/cjs/models/Group.cjs +13 -0
- package/node_modules/zca-js/dist/cjs/models/GroupEvent.cjs +94 -0
- package/node_modules/zca-js/dist/cjs/models/Message.cjs +35 -0
- package/node_modules/zca-js/dist/cjs/models/Reaction.cjs +74 -0
- package/node_modules/zca-js/dist/cjs/models/Reminder.cjs +9 -0
- package/node_modules/zca-js/dist/cjs/models/SeenMessage.cjs +23 -0
- package/node_modules/zca-js/dist/cjs/models/Typing.cjs +23 -0
- package/node_modules/zca-js/dist/cjs/models/Undo.cjs +16 -0
- package/node_modules/zca-js/dist/cjs/models/ZBusiness.cjs +39 -0
- package/node_modules/zca-js/dist/cjs/update.cjs +34 -0
- package/node_modules/zca-js/dist/cjs/utils.cjs +703 -0
- package/node_modules/zca-js/dist/cjs/zalo.cjs +112 -0
- package/node_modules/zca-js/dist/context.d.ts +179 -0
- package/node_modules/zca-js/dist/context.js +28 -0
- package/node_modules/zca-js/dist/index.d.ts +166 -0
- package/node_modules/zca-js/dist/index.js +14 -0
- package/node_modules/zca-js/dist/models/Attachment.d.ts +9 -0
- package/node_modules/zca-js/dist/models/Attachment.js +1 -0
- package/node_modules/zca-js/dist/models/AutoReply.d.ts +20 -0
- package/node_modules/zca-js/dist/models/AutoReply.js +7 -0
- package/node_modules/zca-js/dist/models/Bank.d.ts +291 -0
- package/node_modules/zca-js/dist/models/Bank.js +262 -0
- package/node_modules/zca-js/dist/models/Board.d.ts +60 -0
- package/node_modules/zca-js/dist/models/Board.js +6 -0
- package/node_modules/zca-js/dist/models/Catalog.d.ts +16 -0
- package/node_modules/zca-js/dist/models/Catalog.js +1 -0
- package/node_modules/zca-js/dist/models/DeliveredMessage.d.ts +27 -0
- package/node_modules/zca-js/dist/models/DeliveredMessage.js +17 -0
- package/node_modules/zca-js/dist/models/Enum.d.ts +21 -0
- package/node_modules/zca-js/dist/models/Enum.js +25 -0
- package/node_modules/zca-js/dist/models/FriendEvent.d.ts +107 -0
- package/node_modules/zca-js/dist/models/FriendEvent.js +83 -0
- package/node_modules/zca-js/dist/models/Group.d.ts +125 -0
- package/node_modules/zca-js/dist/models/Group.js +11 -0
- package/node_modules/zca-js/dist/models/GroupEvent.d.ts +178 -0
- package/node_modules/zca-js/dist/models/GroupEvent.js +90 -0
- package/node_modules/zca-js/dist/models/Label.d.ts +10 -0
- package/node_modules/zca-js/dist/models/Label.js +1 -0
- package/node_modules/zca-js/dist/models/Message.d.ts +91 -0
- package/node_modules/zca-js/dist/models/Message.js +29 -0
- package/node_modules/zca-js/dist/models/ProductCatalog.d.ts +17 -0
- package/node_modules/zca-js/dist/models/ProductCatalog.js +1 -0
- package/node_modules/zca-js/dist/models/QuickMessage.d.ts +25 -0
- package/node_modules/zca-js/dist/models/QuickMessage.js +1 -0
- package/node_modules/zca-js/dist/models/Reaction.d.ts +85 -0
- package/node_modules/zca-js/dist/models/Reaction.js +70 -0
- package/node_modules/zca-js/dist/models/Reminder.d.ts +51 -0
- package/node_modules/zca-js/dist/models/Reminder.js +7 -0
- package/node_modules/zca-js/dist/models/SeenMessage.d.ts +26 -0
- package/node_modules/zca-js/dist/models/SeenMessage.js +17 -0
- package/node_modules/zca-js/dist/models/Sticker.d.ts +27 -0
- package/node_modules/zca-js/dist/models/Sticker.js +1 -0
- package/node_modules/zca-js/dist/models/Typing.d.ts +24 -0
- package/node_modules/zca-js/dist/models/Typing.js +17 -0
- package/node_modules/zca-js/dist/models/Undo.d.ts +34 -0
- package/node_modules/zca-js/dist/models/Undo.js +12 -0
- package/node_modules/zca-js/dist/models/User.d.ts +70 -0
- package/node_modules/zca-js/dist/models/User.js +1 -0
- package/node_modules/zca-js/dist/models/ZBusiness.d.ts +22 -0
- package/node_modules/zca-js/dist/models/ZBusiness.js +35 -0
- package/node_modules/zca-js/dist/models/index.d.ts +22 -0
- package/node_modules/zca-js/dist/models/index.js +22 -0
- package/node_modules/zca-js/dist/update.d.ts +2 -0
- package/node_modules/zca-js/dist/update.js +29 -0
- package/node_modules/zca-js/dist/utils.d.ts +167 -0
- package/node_modules/zca-js/dist/utils.js +719 -0
- package/node_modules/zca-js/dist/zalo.d.ts +41 -0
- package/node_modules/zca-js/dist/zalo.js +107 -0
- package/node_modules/zca-js/eslint.config.js +21 -0
- package/node_modules/zca-js/index.d.ts +2 -0
- package/node_modules/zca-js/package.json +78 -0
- package/node_modules/zca-js/rollup.config.js +28 -0
- package/package.json +80 -62
- package/patches/zca-js+2.2.0.patch +1677 -0
- package/src/cli.test.js +34 -5
- package/src/commands/account.js +98 -5
- package/src/commands/auto-reply.js +30 -10
- package/src/commands/catalog.js +5 -4
- package/src/commands/conv.js +551 -14
- package/src/commands/friend.js +19 -7
- package/src/commands/group.js +82 -31
- package/src/commands/listen.js +624 -68
- package/src/commands/login.js +179 -20
- package/src/commands/mcp.js +422 -44
- package/src/commands/msg.js +1288 -140
- package/src/commands/oa-listen.js +130 -20
- package/src/commands/oa.js +26 -5
- package/src/commands/poll.js +7 -2
- package/src/commands/profile.js +3 -2
- package/src/commands/sync.js +2052 -0
- package/src/core/accounts.js +69 -6
- package/src/core/cached-message.js +200 -0
- package/src/core/coverage.js +90 -0
- package/src/core/current-user-names.js +104 -0
- package/src/core/daemon-account.js +61 -0
- package/src/core/daemon-channel.js +638 -0
- package/src/core/daemon-sync.js +181 -0
- package/src/core/db.js +1625 -0
- package/src/core/forward.js +112 -0
- package/src/core/group-history.js +248 -0
- package/src/core/group-members.js +68 -0
- package/src/core/group-settings.js +250 -0
- package/src/core/history-fetch.js +208 -0
- package/src/core/listener-lifecycle.js +90 -0
- package/src/core/live-api.js +47 -0
- package/src/core/live-store.js +733 -0
- package/src/core/lock.js +129 -0
- package/src/core/logout.js +289 -0
- package/src/core/offline-queue.js +194 -0
- package/src/core/pin.js +222 -0
- package/src/core/read-state.js +318 -0
- package/src/core/receipts.js +679 -0
- package/src/core/recent-conversations.js +30 -0
- package/src/core/self-heal.js +522 -0
- package/src/core/socket-tap.js +154 -0
- package/src/core/sync-v2/assets.js +181 -0
- package/src/core/sync-v2/board.js +338 -0
- package/src/core/sync-v2/conv-state.js +152 -0
- package/src/core/sync-v2/gap-advice.js +110 -0
- package/src/core/sync-v2/gid.js +106 -0
- package/src/core/sync-v2/index.js +1259 -0
- package/src/core/sync-v2/keepalive.js +177 -0
- package/src/core/sync-v2/media.js +704 -0
- package/src/core/sync-v2/message-types.js +522 -0
- package/src/core/sync-v2/plan.js +95 -0
- package/src/core/sync-v2/reactions.js +236 -0
- package/src/core/sync-v2/renewlink.js +124 -0
- package/src/core/sync-v2/senders.js +61 -0
- package/src/core/sync-v2/zcloud-cipher.js +88 -0
- package/src/core/sync-v2/zcloud.js +675 -0
- package/src/core/sync.js +540 -0
- package/src/core/zalo-client.js +179 -61
- package/src/core/zca-custom.js +78 -0
- package/src/index.js +15 -3
- package/src/mcp/mcp-config.js +40 -3
- package/src/mcp/mcp-http-transport.js +12 -2
- package/src/mcp/mcp-server.js +5 -2
- package/src/mcp/mcp-tools.js +969 -99
- package/src/mcp/message-buffer.js +38 -8
- package/src/mcp/message-buffer.test.js +67 -15
- package/src/mcp/notifier.js +17 -1
- package/src/mcp/notifier.test.js +6 -0
- package/src/mcp/sync-jobs.js +82 -0
- package/src/mcp/thread-name-cache.js +14 -1
- package/src/mcp/thread-name-cache.test.js +4 -1
- package/src/utils/bank-helpers.js +8 -3
- package/src/utils/device-fingerprint.js +72 -0
- package/src/utils/mentions.js +140 -0
- package/src/utils/my-documents.js +95 -0
- package/src/utils/open-file.js +49 -0
- package/src/utils/output.js +75 -4
- package/src/utils/parse-options.js +56 -0
- package/src/utils/qr-http-server.js +44 -7
- package/src/utils/quote.js +203 -0
- package/src/utils/thread-type.js +72 -0
- package/src/utils/update-check.js +42 -1
- package/src/utils/urgency.js +29 -0
- package/src/mcp/media-downloader.js +0 -191
- package/src/mcp/media-downloader.test.js +0 -45
- package/src/utils/qr-display.test.js +0 -97
package/src/core/db.js
ADDED
|
@@ -0,0 +1,1625 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import Database from "better-sqlite3";
|
|
4
|
+
|
|
5
|
+
let db;
|
|
6
|
+
|
|
7
|
+
export function initDb(dbPath) {
|
|
8
|
+
// A fresh login has no accounts/<ownId>/ yet: only a daemon taking its
|
|
9
|
+
// lock (lock.js) used to create it, so every command that opened the
|
|
10
|
+
// cache before a listener had ever run crashed (sync-cloud, live
|
|
11
|
+
// 2026-09-30: "Cannot open database because the directory does not exist").
|
|
12
|
+
mkdirSync(dirname(dbPath), { recursive: true });
|
|
13
|
+
db = new Database(dbPath);
|
|
14
|
+
|
|
15
|
+
// Enable WAL mode for better performance
|
|
16
|
+
db.pragma("journal_mode = WAL");
|
|
17
|
+
|
|
18
|
+
// Create tables
|
|
19
|
+
db.exec(`
|
|
20
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
21
|
+
msgId TEXT PRIMARY KEY,
|
|
22
|
+
threadId TEXT,
|
|
23
|
+
senderId TEXT,
|
|
24
|
+
senderName TEXT,
|
|
25
|
+
text TEXT,
|
|
26
|
+
timestamp INTEGER,
|
|
27
|
+
type TEXT,
|
|
28
|
+
raw_data TEXT,
|
|
29
|
+
localPath TEXT,
|
|
30
|
+
has_attachment INTEGER DEFAULT 0,
|
|
31
|
+
msgStatus INTEGER,
|
|
32
|
+
mediaPrunedAt INTEGER
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
CREATE TABLE IF NOT EXISTS threads (
|
|
36
|
+
threadId TEXT PRIMARY KEY,
|
|
37
|
+
type TEXT,
|
|
38
|
+
name TEXT,
|
|
39
|
+
lastUpdate INTEGER,
|
|
40
|
+
sync_timestamp INTEGER DEFAULT 0,
|
|
41
|
+
respondedByMe INTEGER,
|
|
42
|
+
lastGlobalId TEXT,
|
|
43
|
+
lastClientId TEXT,
|
|
44
|
+
leftAt INTEGER,
|
|
45
|
+
deleteMarkerAt INTEGER
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
CREATE TABLE IF NOT EXISTS contacts (
|
|
49
|
+
userId TEXT PRIMARY KEY,
|
|
50
|
+
name TEXT,
|
|
51
|
+
phone TEXT
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
-- Small key/value store for sync bookkeeping: lastConnectedAt,
|
|
55
|
+
-- lastDisconnectedAt, lastFullSyncAt, etc. One row per key, per-account
|
|
56
|
+
-- (this whole DB file is already per-account under accounts/<ownId>/zalo.db).
|
|
57
|
+
CREATE TABLE IF NOT EXISTS sync_state (
|
|
58
|
+
key TEXT PRIMARY KEY,
|
|
59
|
+
value TEXT
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
-- Tracks time windows where we know (or suspect) messages may have been
|
|
63
|
+
-- missed — mirrors Zalo Web's own client-side "MissingMessageRange" table
|
|
64
|
+
-- (confirmed live via its IndexedDB schema: id/convId/fromTs/toTs/reason/status)
|
|
65
|
+
-- but kept account-wide rather than per-conversation, since pullMobileMsg/
|
|
66
|
+
-- getCrossDB operate on the whole account, not a single thread.
|
|
67
|
+
CREATE TABLE IF NOT EXISTS sync_gaps (
|
|
68
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
69
|
+
fromTs INTEGER,
|
|
70
|
+
toTs INTEGER,
|
|
71
|
+
reason TEXT,
|
|
72
|
+
status TEXT DEFAULT 'pending',
|
|
73
|
+
createdAt INTEGER,
|
|
74
|
+
resolvedAt INTEGER
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
-- Board items: notes ("Ghi chú"), pinned messages and polls. These are NOT
|
|
78
|
+
-- in the transfer-sync message stream at all — Zalo keeps them behind
|
|
79
|
+
-- /api/board/list, one call per thread — so they are synced separately and
|
|
80
|
+
-- stored here. boardType mirrors zca-js BoardType: 1=Note, 2=PinnedMessage,
|
|
81
|
+
-- 3=Poll. The id column is scoped by thread because item ids repeat across
|
|
82
|
+
-- threads. (No backticks anywhere in this SQL: it sits inside a JS template
|
|
83
|
+
-- literal, so one would close it and break the whole module.)
|
|
84
|
+
CREATE TABLE IF NOT EXISTS board_items (
|
|
85
|
+
id TEXT PRIMARY KEY,
|
|
86
|
+
threadId TEXT,
|
|
87
|
+
threadType TEXT,
|
|
88
|
+
boardType INTEGER,
|
|
89
|
+
itemId TEXT,
|
|
90
|
+
title TEXT,
|
|
91
|
+
creatorId TEXT,
|
|
92
|
+
createTime INTEGER,
|
|
93
|
+
editTime INTEGER,
|
|
94
|
+
startTime INTEGER,
|
|
95
|
+
duration INTEGER,
|
|
96
|
+
repeat INTEGER,
|
|
97
|
+
emoji TEXT,
|
|
98
|
+
color INTEGER,
|
|
99
|
+
raw_data TEXT,
|
|
100
|
+
sync_timestamp INTEGER
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
-- Reminders ("Nhắc hẹn"). Also out-of-stream: /api/board/oneone/list for
|
|
104
|
+
-- DMs, /api/board/listReminder for groups. The conversation *events* that
|
|
105
|
+
-- announce them do arrive as messages (msgType 24), but the reminder
|
|
106
|
+
-- objects themselves only exist here.
|
|
107
|
+
CREATE TABLE IF NOT EXISTS reminders (
|
|
108
|
+
id TEXT PRIMARY KEY,
|
|
109
|
+
reminderId TEXT,
|
|
110
|
+
threadId TEXT,
|
|
111
|
+
threadType TEXT,
|
|
112
|
+
title TEXT,
|
|
113
|
+
creatorId TEXT,
|
|
114
|
+
createTime INTEGER,
|
|
115
|
+
editTime INTEGER,
|
|
116
|
+
startTime INTEGER,
|
|
117
|
+
duration INTEGER,
|
|
118
|
+
repeatMode INTEGER,
|
|
119
|
+
emoji TEXT,
|
|
120
|
+
color INTEGER,
|
|
121
|
+
eventType INTEGER,
|
|
122
|
+
raw_data TEXT,
|
|
123
|
+
sync_timestamp INTEGER
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
-- zCloud / "Cloud cua toi" media index. Populated from the cloud verify
|
|
127
|
+
-- queue (cmd 621 -> /cloudmedia/queue/pc/verify) and from the per-thread
|
|
128
|
+
-- media store, and keyed by Zalo's noiseId. Each row records where the
|
|
129
|
+
-- backup lives and the per-item encryptKey; the blob itself stays on
|
|
130
|
+
-- Zalo's side until something fetches and decrypts it.
|
|
131
|
+
CREATE TABLE IF NOT EXISTS cloud_items (
|
|
132
|
+
noiseId TEXT PRIMARY KEY,
|
|
133
|
+
threadId TEXT,
|
|
134
|
+
msgId TEXT,
|
|
135
|
+
msgType INTEGER,
|
|
136
|
+
mediaType INTEGER,
|
|
137
|
+
cloudUrl TEXT,
|
|
138
|
+
encryptKey TEXT,
|
|
139
|
+
checksum TEXT,
|
|
140
|
+
mediaSize INTEGER,
|
|
141
|
+
timestamp INTEGER,
|
|
142
|
+
localPath TEXT,
|
|
143
|
+
raw_data TEXT,
|
|
144
|
+
sync_timestamp INTEGER
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
-- Reactions. NOT in the mobile sync payload at all -- the Sync2 protobuf
|
|
148
|
+
-- has no reaction field. They are NOT unrecoverable, though: the socket
|
|
149
|
+
-- serves them on cmd 610 (1-1) and 611 (group), which is how Zalo Web shows
|
|
150
|
+
-- them after a fresh login, so they arrive either live (cmd 612) or from
|
|
151
|
+
-- the sync-reactions command. (No backticks in this SQL: it sits inside a
|
|
152
|
+
-- JS template literal, so one would close it and break the module.)
|
|
153
|
+
--
|
|
154
|
+
-- Keyed by (msgId, userId, ICON), because Zalo ACCUMULATES: one person can
|
|
155
|
+
-- hold several different reactions on the same message at once, and all of
|
|
156
|
+
-- them are displayed. Confirmed against the app -- three icons sent to one
|
|
157
|
+
-- message show as three. A (msgId, userId) key kept only the last one and
|
|
158
|
+
-- silently discarded the rest.
|
|
159
|
+
CREATE TABLE IF NOT EXISTS reactions (
|
|
160
|
+
id TEXT PRIMARY KEY,
|
|
161
|
+
msgId TEXT,
|
|
162
|
+
threadId TEXT,
|
|
163
|
+
userId TEXT,
|
|
164
|
+
icon TEXT,
|
|
165
|
+
rType INTEGER,
|
|
166
|
+
source TEXT,
|
|
167
|
+
timestamp INTEGER
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
-- Conversation-level state: pinned, unread-marked. Like reactions, none of
|
|
171
|
+
-- this travels in the sync payload; it comes from REST reads and live
|
|
172
|
+
-- events. Applies to 1-1 conversations as much as to groups.
|
|
173
|
+
CREATE TABLE IF NOT EXISTS conv_state (
|
|
174
|
+
threadId TEXT PRIMARY KEY,
|
|
175
|
+
pinned INTEGER,
|
|
176
|
+
pinnedAt INTEGER,
|
|
177
|
+
unreadMarked INTEGER,
|
|
178
|
+
unreadMarkedAt INTEGER,
|
|
179
|
+
updatedAt INTEGER
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
CREATE INDEX IF NOT EXISTS idx_reactions_msg ON reactions(msgId);
|
|
183
|
+
CREATE INDEX IF NOT EXISTS idx_reactions_msg_user ON reactions(msgId, userId);
|
|
184
|
+
CREATE INDEX IF NOT EXISTS idx_reactions_thread ON reactions(threadId);
|
|
185
|
+
CREATE INDEX IF NOT EXISTS idx_cloud_thread ON cloud_items(threadId);
|
|
186
|
+
CREATE INDEX IF NOT EXISTS idx_cloud_msg ON cloud_items(msgId);
|
|
187
|
+
CREATE INDEX IF NOT EXISTS idx_messages_thread_ts ON messages(threadId, timestamp DESC);
|
|
188
|
+
CREATE INDEX IF NOT EXISTS idx_messages_attach ON messages(has_attachment) WHERE has_attachment = 1;
|
|
189
|
+
CREATE INDEX IF NOT EXISTS idx_board_thread ON board_items(threadId, boardType);
|
|
190
|
+
CREATE INDEX IF NOT EXISTS idx_reminders_thread ON reminders(threadId);
|
|
191
|
+
`);
|
|
192
|
+
|
|
193
|
+
// Migration for existing DBs
|
|
194
|
+
try {
|
|
195
|
+
db.exec("ALTER TABLE messages ADD COLUMN localPath TEXT");
|
|
196
|
+
} catch {
|
|
197
|
+
// column probably already exists
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
db.exec("ALTER TABLE messages ADD COLUMN has_attachment INTEGER DEFAULT 0");
|
|
201
|
+
} catch {}
|
|
202
|
+
try {
|
|
203
|
+
db.exec("ALTER TABLE threads ADD COLUMN sync_timestamp INTEGER DEFAULT 0");
|
|
204
|
+
} catch {}
|
|
205
|
+
// Conversation-round fields from the mobile sync. Nullable: the listener
|
|
206
|
+
// never learns them, so only a transfer sync fills them in.
|
|
207
|
+
try {
|
|
208
|
+
db.exec("ALTER TABLE threads ADD COLUMN respondedByMe INTEGER");
|
|
209
|
+
} catch {}
|
|
210
|
+
try {
|
|
211
|
+
db.exec("ALTER TABLE threads ADD COLUMN lastGlobalId TEXT");
|
|
212
|
+
} catch {}
|
|
213
|
+
try {
|
|
214
|
+
db.exec("ALTER TABLE threads ADD COLUMN lastClientId TEXT");
|
|
215
|
+
} catch {}
|
|
216
|
+
// Sync2.Message.MessageStatus: 0 unspecified, 1 fail, 2 sending, 3 sent,
|
|
217
|
+
// 4 received, 5 seen. The mobile sync carries this per message -- it is the
|
|
218
|
+
// only place delivery/read state arrives -- so it gets a real column rather
|
|
219
|
+
// than living unnamed inside raw_data.
|
|
220
|
+
try {
|
|
221
|
+
db.exec("ALTER TABLE messages ADD COLUMN msgStatus INTEGER");
|
|
222
|
+
} catch {}
|
|
223
|
+
// When media was deliberately pruned. Pruning clears localPath, which would
|
|
224
|
+
// otherwise put the row straight back in the download queue -- so the next
|
|
225
|
+
// sync would re-fetch exactly what was just deleted on purpose. This marks
|
|
226
|
+
// the decision so automatic fetches skip it.
|
|
227
|
+
try {
|
|
228
|
+
db.exec("ALTER TABLE messages ADD COLUMN mediaPrunedAt INTEGER");
|
|
229
|
+
} catch {}
|
|
230
|
+
// An older listener stored the raw live msgType as the row type, so
|
|
231
|
+
// removals landed as 'chat.undo'/'chat.delete' and media as
|
|
232
|
+
// 'chat.photo'/'share.file'/... Every query keyed on the shared vocabulary
|
|
233
|
+
// (starting with the removal ones) is short by exactly those rows, so
|
|
234
|
+
// normalize them once. Idempotent: matches only the legacy spellings.
|
|
235
|
+
try {
|
|
236
|
+
db.prepare(
|
|
237
|
+
`UPDATE messages SET type = CASE type
|
|
238
|
+
WHEN 'chat.undo' THEN 'deleted'
|
|
239
|
+
WHEN 'chat.delete' THEN 'deleted'
|
|
240
|
+
WHEN 'webchat' THEN 'text'
|
|
241
|
+
WHEN 'chat.photo' THEN 'photo'
|
|
242
|
+
WHEN 'chat.video.msg' THEN 'video'
|
|
243
|
+
WHEN 'share.file' THEN 'file'
|
|
244
|
+
WHEN 'chat.gif' THEN 'gif'
|
|
245
|
+
WHEN 'chat.sticker' THEN 'sticker'
|
|
246
|
+
WHEN 'chat.voice' THEN 'voice'
|
|
247
|
+
WHEN 'chat.doodle' THEN 'doodle'
|
|
248
|
+
WHEN 'chat.ecard' THEN 'card'
|
|
249
|
+
WHEN 'chat.recommended' THEN 'link'
|
|
250
|
+
WHEN 'chat.link' THEN 'link'
|
|
251
|
+
-- msgType 18 had no sync mapping, so the same shared location
|
|
252
|
+
-- was stored as 'location' by the listener and 'type_18' by a
|
|
253
|
+
-- mobile sync. The classifier now agrees; these are the rows
|
|
254
|
+
-- written before it did.
|
|
255
|
+
WHEN 'type_18' THEN 'location'
|
|
256
|
+
-- zinstant content (a bank card): msgType 24 from a sync, stored
|
|
257
|
+
-- under its raw live spelling until the live map learned it.
|
|
258
|
+
WHEN 'chat.webcontent' THEN 'event'
|
|
259
|
+
-- msgType 2 is a doodle: live says chat.doodle for the same message.
|
|
260
|
+
WHEN 'type_2' THEN 'doodle'
|
|
261
|
+
ELSE type END
|
|
262
|
+
WHERE type IN ('chat.undo','chat.delete','webchat','chat.photo','chat.video.msg','share.file',
|
|
263
|
+
'chat.gif','chat.sticker','chat.voice','chat.doodle','chat.ecard',
|
|
264
|
+
'chat.recommended','chat.link','type_18','chat.webcontent','type_2')`,
|
|
265
|
+
).run();
|
|
266
|
+
} catch {
|
|
267
|
+
/* a fresh database has nothing to normalize */
|
|
268
|
+
}
|
|
269
|
+
// Voice notes a mobile sync stored before msgType 6 was mapped. The row
|
|
270
|
+
// type alone is not enough: the downloader keys on the kind stored INSIDE
|
|
271
|
+
// each attachment, and that also read type_6 -- so without rewriting it
|
|
272
|
+
// (and has_attachment) the audio would stay unfetchable forever.
|
|
273
|
+
try {
|
|
274
|
+
db.prepare(
|
|
275
|
+
"UPDATE messages SET type = 'voice', " +
|
|
276
|
+
"raw_data = json_set(raw_data, '$.attachments[0].kind', 'voice'), " +
|
|
277
|
+
"has_attachment = CASE WHEN json_extract(raw_data, '$.attachments[0].url') IS NOT NULL " +
|
|
278
|
+
"THEN 1 ELSE has_attachment END " +
|
|
279
|
+
"WHERE type = 'type_6' AND json_valid(raw_data)",
|
|
280
|
+
).run();
|
|
281
|
+
} catch {
|
|
282
|
+
/* nothing to repair */
|
|
283
|
+
}
|
|
284
|
+
// Repair threads an earlier listener downgraded to "dm" (see the sticky
|
|
285
|
+
// type rule in upsertThread). The evidence is a group event the LISTENER
|
|
286
|
+
// saw (a `ge:` row, written only for cmd 601 group controls). It used to be
|
|
287
|
+
// any `group_event` row, but that type is also a pin notice
|
|
288
|
+
// (msginfo.actionlist), which a 1-1 carries too: measured live 2026-09-30,
|
|
289
|
+
// pinning a message in a DM flipped the DM to "group" here on every open,
|
|
290
|
+
// and every default-typed command on it then went out as a group command.
|
|
291
|
+
try {
|
|
292
|
+
db.prepare(
|
|
293
|
+
"UPDATE threads SET type = 'group' WHERE type = 'dm' AND threadId IN " +
|
|
294
|
+
"(SELECT threadId FROM messages WHERE type = 'group_event' AND msgId LIKE 'ge:%')",
|
|
295
|
+
).run();
|
|
296
|
+
} catch {
|
|
297
|
+
/* no messages table yet, or nothing to repair */
|
|
298
|
+
}
|
|
299
|
+
// Heal a 1-1 that rule (or anything else) wrongly made a group. In a DM the
|
|
300
|
+
// conversation id IS the other person's uid, so a real message sent by the
|
|
301
|
+
// conversation id itself proves a 1-1; a group id never sends one. System
|
|
302
|
+
// lines are excluded: their sender is whoever acted.
|
|
303
|
+
try {
|
|
304
|
+
db.prepare(
|
|
305
|
+
"UPDATE threads SET type = 'dm' WHERE type = 'group' AND threadId IN " +
|
|
306
|
+
"(SELECT threadId FROM messages WHERE senderId = threadId AND msgId NOT LIKE 'ge:%' " +
|
|
307
|
+
"AND COALESCE(type, '') NOT IN ('group_event', 'event', 'poll_event', 'deleted'))",
|
|
308
|
+
).run();
|
|
309
|
+
} catch {
|
|
310
|
+
/* no messages table yet, or nothing to heal */
|
|
311
|
+
}
|
|
312
|
+
// When the conversation stopped being ours: dispersed, deleted, or we were
|
|
313
|
+
// removed. Nothing else records this, so without it a vanished group keeps
|
|
314
|
+
// its messages and media forever with no code path that would revisit them.
|
|
315
|
+
try {
|
|
316
|
+
db.exec("ALTER TABLE threads ADD COLUMN leftAt INTEGER");
|
|
317
|
+
} catch {}
|
|
318
|
+
// The cutoff `msg history` honors, split out of leftAt. leftAt used to be
|
|
319
|
+
// both "this conversation is not ours" and "nothing at or before this
|
|
320
|
+
// comes back from history". The first is now cleared when the account is
|
|
321
|
+
// seen in the conversation again; the second must not be -- a DM you
|
|
322
|
+
// deleted keeps its old messages deleted after the contact writes again.
|
|
323
|
+
// Copied once, in the same transaction that adds the column: from then on
|
|
324
|
+
// leftAt is also set by a conversation list, which is not a deletion and
|
|
325
|
+
// must never become a cutoff.
|
|
326
|
+
if (!db.prepare("SELECT 1 FROM pragma_table_info('threads') WHERE name = 'deleteMarkerAt'").get()) {
|
|
327
|
+
db.transaction(() => {
|
|
328
|
+
db.exec("ALTER TABLE threads ADD COLUMN deleteMarkerAt INTEGER");
|
|
329
|
+
db.exec("UPDATE threads SET deleteMarkerAt = leftAt WHERE leftAt IS NOT NULL");
|
|
330
|
+
})();
|
|
331
|
+
}
|
|
332
|
+
// The read watermark: the newest message this account has read in the
|
|
333
|
+
// conversation, on any device, as the server reports it (./read-state.js).
|
|
334
|
+
// Nullable: nothing is known until the listener sees a report.
|
|
335
|
+
try {
|
|
336
|
+
db.exec("ALTER TABLE conv_state ADD COLUMN lastReadMsgId TEXT");
|
|
337
|
+
} catch {}
|
|
338
|
+
try {
|
|
339
|
+
db.exec("ALTER TABLE conv_state ADD COLUMN lastReadTs INTEGER");
|
|
340
|
+
} catch {}
|
|
341
|
+
|
|
342
|
+
return db;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** Get a bookkeeping value (e.g. "lastConnectedAt"). Returns null if unset. */
|
|
346
|
+
export function getSyncState(key) {
|
|
347
|
+
if (!db) throw new Error("Database not initialized");
|
|
348
|
+
const row = db.prepare("SELECT value FROM sync_state WHERE key = ?").get(key);
|
|
349
|
+
return row ? row.value : null;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** Set a bookkeeping value. Value is stringified on write, so callers get
|
|
353
|
+
* strings back from getSyncState() and should Number()/JSON.parse() as needed. */
|
|
354
|
+
export function setSyncState(key, value) {
|
|
355
|
+
if (!db) throw new Error("Database not initialized");
|
|
356
|
+
db.prepare(
|
|
357
|
+
`
|
|
358
|
+
INSERT INTO sync_state (key, value) VALUES (@key, @value)
|
|
359
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value
|
|
360
|
+
`,
|
|
361
|
+
).run({ key, value: String(value) });
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Remove a bookkeeping value.
|
|
366
|
+
*
|
|
367
|
+
* Needed because some keys are per-thread flags rather than durable state:
|
|
368
|
+
* `boardStale:<threadId>` says "this conversation's board moved, refetch it",
|
|
369
|
+
* and a flag that is never cleared is indistinguishable from one that was
|
|
370
|
+
* never set. Without this, every thread that ever saw a pin stayed "stale"
|
|
371
|
+
* forever and the prioritization it exists for degraded to a no-op.
|
|
372
|
+
*
|
|
373
|
+
* @param {string} key
|
|
374
|
+
*/
|
|
375
|
+
export function clearSyncState(key) {
|
|
376
|
+
if (!db) throw new Error("Database not initialized");
|
|
377
|
+
return db.prepare("DELETE FROM sync_state WHERE key = ?").run(key);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** Record a window of time we may have missed messages in (reason: e.g.
|
|
381
|
+
* "startup-gap", "reconnect-gap", "manual"). Returns the new gap's id. */
|
|
382
|
+
export function recordSyncGap(fromTs, toTs, reason) {
|
|
383
|
+
if (!db) throw new Error("Database not initialized");
|
|
384
|
+
const stmt = db.prepare(`
|
|
385
|
+
INSERT INTO sync_gaps (fromTs, toTs, reason, status, createdAt)
|
|
386
|
+
VALUES (@fromTs, @toTs, @reason, 'pending', @createdAt)
|
|
387
|
+
`);
|
|
388
|
+
const info = stmt.run({ fromTs, toTs, reason, createdAt: Date.now() });
|
|
389
|
+
return info.lastInsertRowid;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/** All gaps not yet confirmed synced, oldest first. */
|
|
393
|
+
export function getPendingSyncGaps() {
|
|
394
|
+
if (!db) throw new Error("Database not initialized");
|
|
395
|
+
return db.prepare("SELECT * FROM sync_gaps WHERE status = 'pending' ORDER BY fromTs ASC").all();
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/** Mark a gap resolved after a sync round-trip has actually covered it. */
|
|
399
|
+
export function resolveSyncGap(id) {
|
|
400
|
+
if (!db) throw new Error("Database not initialized");
|
|
401
|
+
db.prepare("UPDATE sync_gaps SET status = 'resolved', resolvedAt = ? WHERE id = ?").run(Date.now(), id);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/** Mark every currently-pending gap resolved (used after a full sync cycle
|
|
405
|
+
* completes successfully — we don't currently track gaps per-thread, so a
|
|
406
|
+
* successful round-trip is treated as covering everything outstanding). */
|
|
407
|
+
export function resolveAllPendingSyncGaps() {
|
|
408
|
+
if (!db) throw new Error("Database not initialized");
|
|
409
|
+
db.prepare("UPDATE sync_gaps SET status = 'resolved', resolvedAt = ? WHERE status = 'pending'").run(Date.now());
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* How many recorded coverage gaps are still pending, and how many have been
|
|
414
|
+
* resolved (by a restore that covered them, or by the self-heal). Read-only.
|
|
415
|
+
*
|
|
416
|
+
* @returns {{pending: number, resolved: number}}
|
|
417
|
+
*/
|
|
418
|
+
export function countSyncGaps() {
|
|
419
|
+
if (!db) throw new Error("Database not initialized");
|
|
420
|
+
const row = db
|
|
421
|
+
.prepare(
|
|
422
|
+
"SELECT SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) AS pending, " +
|
|
423
|
+
"SUM(CASE WHEN status = 'resolved' THEN 1 ELSE 0 END) AS resolved FROM sync_gaps",
|
|
424
|
+
)
|
|
425
|
+
.get();
|
|
426
|
+
return { pending: Number(row?.pending) || 0, resolved: Number(row?.resolved) || 0 };
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Run `fn` inside a single SQLite transaction.
|
|
431
|
+
*
|
|
432
|
+
* Bulk restores insert tens of thousands of rows; without this each statement
|
|
433
|
+
* is its own transaction and pays a WAL commit, which is both slow and -- worse
|
|
434
|
+
* for the mobile sync -- blocks the event loop long enough that the Zalo socket
|
|
435
|
+
* keepalive ping is missed and the connection is dropped mid-run.
|
|
436
|
+
*
|
|
437
|
+
* @param {() => T} fn
|
|
438
|
+
* @returns {T}
|
|
439
|
+
* @template T
|
|
440
|
+
*/
|
|
441
|
+
export function runInTransaction(fn) {
|
|
442
|
+
if (!db) throw new Error("Database not initialized");
|
|
443
|
+
return db.transaction(fn)();
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
export function insertMessage(msg) {
|
|
447
|
+
if (!db) throw new Error("Database not initialized");
|
|
448
|
+
|
|
449
|
+
// A replay may resolve an opaque sender from an older sync. Never let an
|
|
450
|
+
// unresolved replay replace a canonical live UID, or a different numeric
|
|
451
|
+
// sender overwrite an already established identity.
|
|
452
|
+
const canResolveSender = `(
|
|
453
|
+
COALESCE(messages.senderId, '') = '' OR
|
|
454
|
+
(messages.senderId GLOB '*[^0-9]*' AND
|
|
455
|
+
excluded.senderId <> '' AND excluded.senderId <> '0' AND
|
|
456
|
+
excluded.senderId NOT GLOB '*[^0-9]*')
|
|
457
|
+
)`;
|
|
458
|
+
const stmt = db.prepare(`
|
|
459
|
+
INSERT INTO messages (msgId, threadId, senderId, senderName, text, timestamp, type, raw_data, localPath, has_attachment, msgStatus)
|
|
460
|
+
VALUES (@msgId, @threadId, @senderId, @senderName, @text, @timestamp, @type, @raw_data, @localPath, @has_attachment, @msgStatus)
|
|
461
|
+
ON CONFLICT(msgId) DO UPDATE SET
|
|
462
|
+
senderId = CASE WHEN ${canResolveSender} THEN excluded.senderId ELSE messages.senderId END,
|
|
463
|
+
senderName = CASE
|
|
464
|
+
WHEN COALESCE(messages.senderName, '') = '' AND
|
|
465
|
+
(messages.senderId = excluded.senderId OR ${canResolveSender})
|
|
466
|
+
THEN COALESCE(NULLIF(excluded.senderName, ''), messages.senderName)
|
|
467
|
+
ELSE messages.senderName END,
|
|
468
|
+
-- A removal is terminal. Once a row is a tombstone, a later write of the
|
|
469
|
+
-- same msgId -- a re-sync, a history replay, a backfill -- must not bring
|
|
470
|
+
-- the message back or replace the tombstone's record of HOW it was
|
|
471
|
+
-- removed (raw_data.removedAs: recall vs delete-for-me, originalType).
|
|
472
|
+
-- The phone keeps its own tombstone, so a sync arriving after a live
|
|
473
|
+
-- recall used to overwrite ours with a poorer one.
|
|
474
|
+
text = CASE WHEN messages.type = 'deleted' THEN messages.text ELSE excluded.text END,
|
|
475
|
+
timestamp = excluded.timestamp,
|
|
476
|
+
type = CASE WHEN messages.type = 'deleted' THEN messages.type ELSE excluded.type END,
|
|
477
|
+
raw_data = CASE WHEN messages.type = 'deleted' THEN messages.raw_data ELSE excluded.raw_data END,
|
|
478
|
+
localPath = COALESCE(excluded.localPath, messages.localPath),
|
|
479
|
+
has_attachment = CASE WHEN messages.type = 'deleted' THEN messages.has_attachment ELSE excluded.has_attachment END,
|
|
480
|
+
-- Never regress delivery state: a re-sync of an older snapshot must not
|
|
481
|
+
-- turn a seen message back into merely received. NULL is preserved as
|
|
482
|
+
-- NULL, because "we were never told" is not the same as status 0, and
|
|
483
|
+
-- with read receipts switched off it is the normal state.
|
|
484
|
+
msgStatus = CASE
|
|
485
|
+
WHEN excluded.msgStatus IS NULL AND messages.msgStatus IS NULL THEN NULL
|
|
486
|
+
ELSE MAX(COALESCE(excluded.msgStatus, 0), COALESCE(messages.msgStatus, 0)) END
|
|
487
|
+
`);
|
|
488
|
+
|
|
489
|
+
const raw_data =
|
|
490
|
+
typeof msg.raw_data === "object" && msg.raw_data !== null ? JSON.stringify(msg.raw_data) : msg.raw_data;
|
|
491
|
+
|
|
492
|
+
return stmt.run({
|
|
493
|
+
msgId: msg.msgId,
|
|
494
|
+
threadId: msg.threadId,
|
|
495
|
+
senderId: msg.senderId,
|
|
496
|
+
senderName: msg.senderName,
|
|
497
|
+
text: msg.text,
|
|
498
|
+
timestamp: msg.timestamp,
|
|
499
|
+
type: msg.type,
|
|
500
|
+
raw_data: raw_data,
|
|
501
|
+
localPath: msg.localPath || null,
|
|
502
|
+
has_attachment: msg.has_attachment ? 1 : 0,
|
|
503
|
+
msgStatus: Number.isFinite(Number(msg.msgStatus)) ? Number(msg.msgStatus) : null,
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Insert a message only if its msgId is not stored yet -- never update one.
|
|
509
|
+
*
|
|
510
|
+
* msg history's write-back (storeHistoryMessage in ./live-store.js). Zalo Web
|
|
511
|
+
* writes the history it fetches the same way, ZStorage.setMessage with
|
|
512
|
+
* replace:false. A stored row keeps every column, which is the point: the
|
|
513
|
+
* listener and later events put things on it that a re-fetch cannot know --
|
|
514
|
+
* the media downloader's localPath, a receipt's msgStatus, a recall's
|
|
515
|
+
* tombstone, the st/at/cmd a seen receipt echoes back. {@link insertMessage},
|
|
516
|
+
* the listener's upsert, would replace most of them.
|
|
517
|
+
*
|
|
518
|
+
* Same columns and value mapping as insertMessage.
|
|
519
|
+
*
|
|
520
|
+
* @param {object} msg - the row, as for insertMessage
|
|
521
|
+
* @returns {{changes: number}} 1 when inserted, 0 when the msgId was already stored
|
|
522
|
+
*/
|
|
523
|
+
export function insertMessageIfAbsent(msg) {
|
|
524
|
+
if (!db) throw new Error("Database not initialized");
|
|
525
|
+
|
|
526
|
+
const stmt = db.prepare(`
|
|
527
|
+
INSERT INTO messages (msgId, threadId, senderId, senderName, text, timestamp, type, raw_data, localPath, has_attachment, msgStatus)
|
|
528
|
+
VALUES (@msgId, @threadId, @senderId, @senderName, @text, @timestamp, @type, @raw_data, @localPath, @has_attachment, @msgStatus)
|
|
529
|
+
ON CONFLICT(msgId) DO NOTHING
|
|
530
|
+
`);
|
|
531
|
+
|
|
532
|
+
const raw_data =
|
|
533
|
+
typeof msg.raw_data === "object" && msg.raw_data !== null ? JSON.stringify(msg.raw_data) : msg.raw_data;
|
|
534
|
+
|
|
535
|
+
return stmt.run({
|
|
536
|
+
msgId: msg.msgId,
|
|
537
|
+
threadId: msg.threadId,
|
|
538
|
+
senderId: msg.senderId,
|
|
539
|
+
senderName: msg.senderName,
|
|
540
|
+
text: msg.text,
|
|
541
|
+
timestamp: msg.timestamp,
|
|
542
|
+
type: msg.type,
|
|
543
|
+
raw_data: raw_data,
|
|
544
|
+
localPath: msg.localPath || null,
|
|
545
|
+
has_attachment: msg.has_attachment ? 1 : 0,
|
|
546
|
+
msgStatus: Number.isFinite(Number(msg.msgStatus)) ? Number(msg.msgStatus) : null,
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* When a conversation stopped being ours, or null while it is ours.
|
|
552
|
+
*
|
|
553
|
+
* The gone marker. Set by `conv delete` and our own leave, removal or block
|
|
554
|
+
* event ({@link markThreadGone}) and by a full-history conversation list that
|
|
555
|
+
* no longer holds the thread ({@link recordConversationList}); cleared when
|
|
556
|
+
* the account is seen in the conversation again ({@link markThreadBack}).
|
|
557
|
+
* What `sync-media --prune-orphans` and `conv forget --orphans` act on, via
|
|
558
|
+
* {@link getOrphanThreads}.
|
|
559
|
+
*
|
|
560
|
+
* Not the history cutoff: that is {@link getThreadDeleteMarker}, which a
|
|
561
|
+
* return does not clear.
|
|
562
|
+
*
|
|
563
|
+
* @param {string} threadId
|
|
564
|
+
* @returns {number|null} epoch ms
|
|
565
|
+
*/
|
|
566
|
+
export function getThreadLeftAt(threadId) {
|
|
567
|
+
if (!db) throw new Error("Database not initialized");
|
|
568
|
+
const row = db.prepare("SELECT leftAt FROM threads WHERE threadId = ?").get(String(threadId));
|
|
569
|
+
const at = Number(row?.leftAt);
|
|
570
|
+
return Number.isFinite(at) && at > 0 ? at : null;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* The conversation's delete marker: nothing at or before this time may come
|
|
575
|
+
* back from fetched history. Null when there is none.
|
|
576
|
+
*
|
|
577
|
+
* The nearest thing this cache has to Zalo Web's per-conversation delete
|
|
578
|
+
* marker. Set with the gone marker by `conv delete` and by our own leave,
|
|
579
|
+
* removal or block ({@link markThreadGone}), and kept when the account comes
|
|
580
|
+
* back -- a deleted DM's old messages stay deleted after the contact writes
|
|
581
|
+
* again, while the conversation itself stops being an orphan.
|
|
582
|
+
*
|
|
583
|
+
* @param {string} threadId
|
|
584
|
+
* @returns {number|null} epoch ms
|
|
585
|
+
*/
|
|
586
|
+
export function getThreadDeleteMarker(threadId) {
|
|
587
|
+
if (!db) throw new Error("Database not initialized");
|
|
588
|
+
const row = db.prepare("SELECT deleteMarkerAt FROM threads WHERE threadId = ?").get(String(threadId));
|
|
589
|
+
const at = Number(row?.deleteMarkerAt);
|
|
590
|
+
return Number.isFinite(at) && at > 0 ? at : null;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
export function getMessages(threadId, limit = 50, fromTimestamp = null) {
|
|
594
|
+
if (!db) throw new Error("Database not initialized");
|
|
595
|
+
|
|
596
|
+
let query = "SELECT * FROM messages WHERE threadId = ?";
|
|
597
|
+
const params = [threadId];
|
|
598
|
+
|
|
599
|
+
if (fromTimestamp) {
|
|
600
|
+
query += " AND timestamp < ?";
|
|
601
|
+
params.push(fromTimestamp);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
query += " ORDER BY timestamp DESC LIMIT ?";
|
|
605
|
+
params.push(limit);
|
|
606
|
+
|
|
607
|
+
return db.prepare(query).all(...params);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* One message by its global id, or null.
|
|
612
|
+
*
|
|
613
|
+
* Callers that hold a msgId and nothing else (the MCP media tool, a delete
|
|
614
|
+
* that needs the row's cliMsgId) had to scan a thread's messages to find it.
|
|
615
|
+
*
|
|
616
|
+
* @param {string} msgId
|
|
617
|
+
* @returns {object|null}
|
|
618
|
+
*/
|
|
619
|
+
export function getMessageById(msgId) {
|
|
620
|
+
if (!db) throw new Error("Database not initialized");
|
|
621
|
+
return db.prepare("SELECT * FROM messages WHERE msgId = ?").get(String(msgId)) || null;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Most recently active threads, newest first.
|
|
626
|
+
*
|
|
627
|
+
* `type` filters in SQL rather than after the fact. That distinction is the
|
|
628
|
+
* whole point: `getRecentThreads(5)` followed by a JS filter returns the
|
|
629
|
+
* groups *among the newest five threads of any kind* — frequently none —
|
|
630
|
+
* whereas asking for 5 groups should return the 5 newest groups.
|
|
631
|
+
*
|
|
632
|
+
* @param {number} [limit=20]
|
|
633
|
+
* @param {"dm"|"group"|null} [type=null] - null means both
|
|
634
|
+
*/
|
|
635
|
+
export function getRecentThreads(limit = 20, type = null) {
|
|
636
|
+
if (!db) throw new Error("Database not initialized");
|
|
637
|
+
if (type) {
|
|
638
|
+
return db.prepare("SELECT * FROM threads WHERE type = ? ORDER BY lastUpdate DESC LIMIT ?").all(type, limit);
|
|
639
|
+
}
|
|
640
|
+
return db.prepare("SELECT * FROM threads ORDER BY lastUpdate DESC LIMIT ?").all(limit);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* One conversation's kind, or null when the cache has never seen it.
|
|
645
|
+
*
|
|
646
|
+
* Callers use this to answer "is this a group?" without loading the thread, so
|
|
647
|
+
* they must be able to tell "not a group" from "don't know" -- hence null
|
|
648
|
+
* rather than a default of "dm".
|
|
649
|
+
*
|
|
650
|
+
* @param {string} threadId
|
|
651
|
+
* @returns {"dm"|"group"|null}
|
|
652
|
+
*/
|
|
653
|
+
export function getThreadType(threadId) {
|
|
654
|
+
if (!db) throw new Error("Database not initialized");
|
|
655
|
+
const row = db.prepare("SELECT type FROM threads WHERE threadId = ?").get(String(threadId));
|
|
656
|
+
return row?.type || null;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
export function upsertThread(thread) {
|
|
660
|
+
if (!db) throw new Error("Database not initialized");
|
|
661
|
+
|
|
662
|
+
const stmt = db.prepare(`
|
|
663
|
+
INSERT INTO threads (threadId, type, name, lastUpdate, sync_timestamp, respondedByMe, lastGlobalId, lastClientId)
|
|
664
|
+
VALUES (@threadId, @type, @name, @lastUpdate, @sync_timestamp, @respondedByMe, @lastGlobalId, @lastClientId)
|
|
665
|
+
ON CONFLICT(threadId) DO UPDATE SET
|
|
666
|
+
-- Group-ness is durable, so it is sticky. A live message is classified by
|
|
667
|
+
-- the socket command that carried it -- cmd 501 builds a UserMessage, cmd
|
|
668
|
+
-- 521 a GroupMessage -- and the server echoes the sender's own message
|
|
669
|
+
-- back on whichever channel matches the thread type the SEND declared.
|
|
670
|
+
-- Send to a group with the wrong type and the echo returns on the DM
|
|
671
|
+
-- channel, and a blind assignment here rewrote a synced group to "dm".
|
|
672
|
+
-- Measured: two threads holding group_event rows were stored as "dm".
|
|
673
|
+
-- A group id is never later a user id, so promoting dm -> group is a
|
|
674
|
+
-- correction worth taking and the reverse is wrong for a LIVE write.
|
|
675
|
+
-- An authoritative write is the exception: the phone sync reads the type
|
|
676
|
+
-- from its own partition (oneone/ vs group/), which no wrong-type send
|
|
677
|
+
-- can skew, so it may undo a "group" that weak evidence put on a 1-1.
|
|
678
|
+
type = CASE
|
|
679
|
+
WHEN @authoritative = 1 AND excluded.type IN ('dm', 'group') THEN excluded.type
|
|
680
|
+
WHEN threads.type = 'group' THEN 'group'
|
|
681
|
+
WHEN excluded.type IS NULL OR excluded.type = '' THEN threads.type
|
|
682
|
+
ELSE excluded.type END,
|
|
683
|
+
-- Never overwrite a known name with a blank one: the sync upserts threads
|
|
684
|
+
-- per message and only some of those carry a display name.
|
|
685
|
+
--
|
|
686
|
+
-- A nameHint write is weaker still: it fills a name in but never replaces
|
|
687
|
+
-- one. A live message carries the SENDER's display name (dName), which is
|
|
688
|
+
-- not the conversation's name -- a group has exactly one name, and a 1-1
|
|
689
|
+
-- is titled by the contact's alias when we set one. Letting a per-message
|
|
690
|
+
-- field win renamed a synced group after its next message and replaced a
|
|
691
|
+
-- deliberate alias with whatever the contact currently calls themselves.
|
|
692
|
+
name = CASE
|
|
693
|
+
WHEN excluded.name IS NULL OR excluded.name = '' THEN threads.name
|
|
694
|
+
WHEN @nameHint = 1 AND threads.name IS NOT NULL AND threads.name != '' THEN threads.name
|
|
695
|
+
ELSE excluded.name END,
|
|
696
|
+
-- MAX, not assignment. A sync calls this once per message, and messages
|
|
697
|
+
-- do not arrive newest-first, so a plain assignment left every thread
|
|
698
|
+
-- stamped with whichever message happened to be processed last -- which
|
|
699
|
+
-- is what made conv recent order wrongly after a sync. (No backticks:
|
|
700
|
+
-- this sits inside a JS template literal.)
|
|
701
|
+
lastUpdate = MAX(COALESCE(excluded.lastUpdate, 0), COALESCE(threads.lastUpdate, 0)),
|
|
702
|
+
sync_timestamp = COALESCE(excluded.sync_timestamp, threads.sync_timestamp),
|
|
703
|
+
respondedByMe = COALESCE(excluded.respondedByMe, threads.respondedByMe),
|
|
704
|
+
lastGlobalId = COALESCE(excluded.lastGlobalId, threads.lastGlobalId),
|
|
705
|
+
lastClientId = COALESCE(excluded.lastClientId, threads.lastClientId)
|
|
706
|
+
`);
|
|
707
|
+
|
|
708
|
+
return stmt.run({
|
|
709
|
+
threadId: thread.threadId,
|
|
710
|
+
type: thread.type,
|
|
711
|
+
name: thread.name,
|
|
712
|
+
lastUpdate: thread.lastUpdate,
|
|
713
|
+
sync_timestamp: thread.sync_timestamp || null,
|
|
714
|
+
// Only a mobile sync knows these; a listener write leaves them alone.
|
|
715
|
+
respondedByMe: thread.respondedByMe === undefined ? null : thread.respondedByMe ? 1 : 0,
|
|
716
|
+
lastGlobalId: thread.lastGlobalId === undefined ? null : String(thread.lastGlobalId),
|
|
717
|
+
lastClientId: thread.lastClientId === undefined ? null : String(thread.lastClientId),
|
|
718
|
+
nameHint: thread.nameHint ? 1 : 0,
|
|
719
|
+
// Only the phone sync passes this (see the type rule above).
|
|
720
|
+
authoritative: thread.authoritative ? 1 : 0,
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* threadId -> {name, type} for every cached conversation.
|
|
726
|
+
*
|
|
727
|
+
* One map, one definition of a conversation's folder and label, shared by the
|
|
728
|
+
* sync, the listener and `msg history`. They each used to derive it
|
|
729
|
+
* differently -- the sync from this table, the listener from the arriving
|
|
730
|
+
* message's `dName`, `msg history` not at all -- so one conversation's media
|
|
731
|
+
* could land in three different folders depending on which command fetched it.
|
|
732
|
+
*
|
|
733
|
+
* @param {number} [limit=5000]
|
|
734
|
+
* @returns {Map<string, {name: string, type: string}>}
|
|
735
|
+
*/
|
|
736
|
+
export function getThreadNames(limit = 5000) {
|
|
737
|
+
const map = new Map();
|
|
738
|
+
try {
|
|
739
|
+
for (const t of getRecentThreads(limit)) {
|
|
740
|
+
map.set(String(t.threadId), { name: t.name || String(t.threadId), type: t.type });
|
|
741
|
+
}
|
|
742
|
+
} catch {
|
|
743
|
+
/* an empty or unopened cache just means folders fall back to thread ids */
|
|
744
|
+
}
|
|
745
|
+
return map;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Messages carrying at least one downloadable attachment, newest first.
|
|
750
|
+
*
|
|
751
|
+
* Reads the `has_attachment` flag rather than scanning `raw_data`, which is
|
|
752
|
+
* why the sync path must set it — a JSON scan over a six-figure message table
|
|
753
|
+
* is not something a CLI command can afford to do per run.
|
|
754
|
+
*
|
|
755
|
+
* @param {object} [opts]
|
|
756
|
+
* @param {string} [opts.threadId] - restrict to one thread
|
|
757
|
+
* @param {number} [opts.since] - only messages at/after this epoch ms
|
|
758
|
+
* @param {number} [opts.until] - only messages at/before this epoch ms
|
|
759
|
+
* @param {boolean} [opts.onlyMissing=true] - skip rows already downloaded
|
|
760
|
+
* @param {boolean} [opts.includePruned=false] - also consider media deliberately pruned
|
|
761
|
+
* @param {number} [opts.limit=500]
|
|
762
|
+
*/
|
|
763
|
+
export function getAttachmentMessages(opts = {}) {
|
|
764
|
+
if (!db) throw new Error("Database not initialized");
|
|
765
|
+
const { threadId, since, until, onlyMissing = true, includePruned = false, limit = 500 } = opts;
|
|
766
|
+
const where = ["has_attachment = 1"];
|
|
767
|
+
const params = [];
|
|
768
|
+
if (onlyMissing) where.push("localPath IS NULL");
|
|
769
|
+
// A deliberate prune is a decision, not a gap to be refilled. Automatic
|
|
770
|
+
// fetches respect it; an explicit re-fetch can opt back in.
|
|
771
|
+
if (!includePruned) where.push("mediaPrunedAt IS NULL");
|
|
772
|
+
if (threadId) (where.push("threadId = ?"), params.push(String(threadId)));
|
|
773
|
+
if (Number.isFinite(since)) (where.push("timestamp >= ?"), params.push(since));
|
|
774
|
+
if (Number.isFinite(until)) (where.push("timestamp <= ?"), params.push(until));
|
|
775
|
+
params.push(limit);
|
|
776
|
+
return db
|
|
777
|
+
.prepare(`SELECT * FROM messages WHERE ${where.join(" AND ")} ORDER BY timestamp DESC LIMIT ?`)
|
|
778
|
+
.all(...params);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Links shared in conversations, newest first — the local equivalent of the
|
|
783
|
+
* "Link" tab in Zalo's media store, which is a third media kind alongside
|
|
784
|
+
* images and files rather than something separate.
|
|
785
|
+
*
|
|
786
|
+
* Kept as a query over `raw_data` rather than its own table: a link has no
|
|
787
|
+
* bytes to download and no lifecycle of its own, so the message row is already
|
|
788
|
+
* the record. One row per link, so a message carrying several yields several.
|
|
789
|
+
*
|
|
790
|
+
* @param {object} [opts]
|
|
791
|
+
* @param {string} [opts.threadId]
|
|
792
|
+
* @param {number} [opts.limit=200]
|
|
793
|
+
* @returns {Array<{msgId: string, threadId: string, timestamp: number, url: string, title: string|null, description: string|null, thumbUrl: string|null}>}
|
|
794
|
+
*/
|
|
795
|
+
export function getLinkMessages(opts = {}) {
|
|
796
|
+
if (!db) throw new Error("Database not initialized");
|
|
797
|
+
const { threadId, limit = 200 } = opts;
|
|
798
|
+
const where = ["json_extract(a.value, '$.kind') = 'link'", "json_extract(a.value, '$.url') IS NOT NULL"];
|
|
799
|
+
const params = [];
|
|
800
|
+
if (threadId) {
|
|
801
|
+
where.push("m.threadId = ?");
|
|
802
|
+
params.push(String(threadId));
|
|
803
|
+
}
|
|
804
|
+
params.push(limit);
|
|
805
|
+
return db
|
|
806
|
+
.prepare(
|
|
807
|
+
`SELECT m.msgId, m.threadId, m.senderId, m.timestamp,
|
|
808
|
+
json_extract(a.value, '$.url') AS url,
|
|
809
|
+
json_extract(a.value, '$.title') AS title,
|
|
810
|
+
json_extract(a.value, '$.description') AS description,
|
|
811
|
+
json_extract(a.value, '$.thumbUrl') AS thumbUrl
|
|
812
|
+
FROM messages m, json_each(CASE WHEN json_valid(m.raw_data) THEN json_extract(m.raw_data, '$.attachments') END) a
|
|
813
|
+
WHERE ${where.join(" AND ")}
|
|
814
|
+
ORDER BY m.timestamp DESC LIMIT ?`,
|
|
815
|
+
)
|
|
816
|
+
.all(...params);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Downloaded attachments older than `before`, for pruning.
|
|
821
|
+
*
|
|
822
|
+
* Returns the rows rather than deleting anything: the caller has to see what it
|
|
823
|
+
* is about to remove from disk, and a dry run has to be possible.
|
|
824
|
+
*
|
|
825
|
+
* @param {number} before - epoch ms; rows with an older timestamp are returned
|
|
826
|
+
* @param {string} [threadId] - restrict to one thread
|
|
827
|
+
* @returns {Array<{msgId: string, threadId: string, timestamp: number, localPath: string}>}
|
|
828
|
+
*/
|
|
829
|
+
export function getDownloadedMediaBefore(before, threadId = null) {
|
|
830
|
+
if (!db) throw new Error("Database not initialized");
|
|
831
|
+
// `before === null` means every downloaded file regardless of age. It is a
|
|
832
|
+
// separate mode rather than a very large cutoff so that an accidental
|
|
833
|
+
// arithmetic slip on a date can never become "delete everything".
|
|
834
|
+
const wherePieces = ["localPath IS NOT NULL"];
|
|
835
|
+
const params = [];
|
|
836
|
+
if (before !== null) {
|
|
837
|
+
wherePieces.push("timestamp < ?");
|
|
838
|
+
params.push(before);
|
|
839
|
+
}
|
|
840
|
+
if (threadId) {
|
|
841
|
+
wherePieces.push("threadId = ?");
|
|
842
|
+
params.push(String(threadId));
|
|
843
|
+
}
|
|
844
|
+
return db
|
|
845
|
+
.prepare(
|
|
846
|
+
`SELECT msgId, threadId, timestamp, localPath FROM messages WHERE ${wherePieces.join(" AND ")} ORDER BY timestamp ASC`,
|
|
847
|
+
)
|
|
848
|
+
.all(...params);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Conversations the account no longer has that are still cached here: a
|
|
853
|
+
* dispersed group, a deleted chat, a group you left or were removed from.
|
|
854
|
+
*
|
|
855
|
+
* One definition, read alike by `sync`'s orphan warning, `sync-media
|
|
856
|
+
* --prune-orphans` and `conv forget --orphans`: a thread is an orphan when its
|
|
857
|
+
* gone marker (threads.leftAt) is set and nothing in it is newer. Every signal
|
|
858
|
+
* that a conversation is gone records that marker -- our own leave, removal or
|
|
859
|
+
* block event, `conv delete`, and a full-history conversation list that no
|
|
860
|
+
* longer holds it -- so every reader sees the same set however long after the
|
|
861
|
+
* signal it runs. `sync` used to take its warning from the list it had just
|
|
862
|
+
* restored, which nothing recorded, and then recommend a prune that could not
|
|
863
|
+
* see that list.
|
|
864
|
+
*
|
|
865
|
+
* A message newer than the marker means the account is back, whichever writer
|
|
866
|
+
* stored it. The listener clears the marker itself; a restore or a history
|
|
867
|
+
* fetch does not, and this is what keeps them from leaving a conversation you
|
|
868
|
+
* are in marked gone. System lines do not count: the phone's row for our own
|
|
869
|
+
* leave is dated after the leave itself.
|
|
870
|
+
*
|
|
871
|
+
* @param {string[]|null} [liveThreadIds] - PREVIEW ONLY: also count every
|
|
872
|
+
* thread missing from this list, recording nothing. No command passes one --
|
|
873
|
+
* an unrecorded list is exactly how the warning and the prune came to
|
|
874
|
+
* disagree. Recording a list is {@link recordConversationList}.
|
|
875
|
+
* @returns {Array<{threadId: string, name: string, type: string, leftAt: number|null, messages: number, files: number, lastSeenAt: number|null}>}
|
|
876
|
+
*/
|
|
877
|
+
export function getOrphanThreads(liveThreadIds = null) {
|
|
878
|
+
if (!db) throw new Error("Database not initialized");
|
|
879
|
+
const rows = db
|
|
880
|
+
.prepare(
|
|
881
|
+
`SELECT t.threadId, t.name, t.type, t.leftAt,
|
|
882
|
+
COUNT(m.msgId) AS messages,
|
|
883
|
+
SUM(CASE WHEN m.localPath IS NOT NULL THEN 1 ELSE 0 END) AS files,
|
|
884
|
+
MAX(CASE WHEN m.type = 'group_event' THEN NULL ELSE m.timestamp END) AS lastSeenAt
|
|
885
|
+
FROM threads t LEFT JOIN messages m ON m.threadId = t.threadId
|
|
886
|
+
GROUP BY t.threadId, t.name, t.type, t.leftAt`,
|
|
887
|
+
)
|
|
888
|
+
.all();
|
|
889
|
+
const gone = (r) => Boolean(r.leftAt) && !(Number(r.lastSeenAt) > Number(r.leftAt));
|
|
890
|
+
if (!Array.isArray(liveThreadIds)) return rows.filter(gone);
|
|
891
|
+
const live = new Set(liveThreadIds.map(String));
|
|
892
|
+
return rows.filter((r) => gone(r) || !live.has(String(r.threadId)));
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Record what a conversation list says about which cached threads are ours.
|
|
897
|
+
*
|
|
898
|
+
* A restore's conversation round is the phone's list of the conversations the
|
|
899
|
+
* account has. Recording it, rather than holding it for one report, is what
|
|
900
|
+
* lets `sync-media --prune-orphans` and `conv forget --orphans` -- run any time
|
|
901
|
+
* later -- act on the same conversations `sync` said were gone.
|
|
902
|
+
*
|
|
903
|
+
* One transaction, two writes:
|
|
904
|
+
*
|
|
905
|
+
* - a listed thread whose gone marker predates the list is back: a group
|
|
906
|
+
* rejoined since we left it, a deleted DM the contact wrote to again. Any
|
|
907
|
+
* list counts, windowed or partial -- a thread IN it is present, however
|
|
908
|
+
* little else the list covers.
|
|
909
|
+
* - where the caller vouches the list is complete for a kind of thread, a
|
|
910
|
+
* cached thread of that kind missing from it is gone -- unless it was active
|
|
911
|
+
* at or after `markBefore`, because a list cannot hold a conversation that
|
|
912
|
+
* started while it was being taken.
|
|
913
|
+
*
|
|
914
|
+
* The delete marker is never touched. A list is evidence of absence, not a
|
|
915
|
+
* deletion, and must not stop `msg history` from filling a thread in.
|
|
916
|
+
*
|
|
917
|
+
* @param {object} list
|
|
918
|
+
* @param {string[]} list.present - every thread id the list accounted for
|
|
919
|
+
* @param {number} list.clearBefore - a present thread's marker is cleared only if older than this
|
|
920
|
+
* @param {number} list.markBefore - only threads quiet since before this are marked, and at this time
|
|
921
|
+
* @param {string[]} [list.markKinds=[]] - thread types ("dm", "group") the list is complete for
|
|
922
|
+
* @param {string[]} [list.keep=[]] - ids a list never marks: the account's own threads
|
|
923
|
+
* @returns {{cleared: string[], marked: string[], unproven: number}} `unproven` counts the cached
|
|
924
|
+
* conversations with messages that are missing from the list and were left unmarked, because
|
|
925
|
+
* the list is not complete for their kind
|
|
926
|
+
*/
|
|
927
|
+
export function recordConversationList({ present, clearBefore, markBefore, markKinds = [], keep = [] }) {
|
|
928
|
+
if (!db) throw new Error("Database not initialized");
|
|
929
|
+
const params = {
|
|
930
|
+
present: JSON.stringify((present || []).map(String)),
|
|
931
|
+
kinds: JSON.stringify(markKinds.map(String)),
|
|
932
|
+
keep: JSON.stringify(keep.filter(Boolean).map(String)),
|
|
933
|
+
clearBefore: Number(clearBefore),
|
|
934
|
+
markBefore: Number(markBefore),
|
|
935
|
+
};
|
|
936
|
+
// Not an orphan right now, by the rule getOrphanThreads reads with.
|
|
937
|
+
const notGone =
|
|
938
|
+
"(threads.leftAt IS NULL OR EXISTS (SELECT 1 FROM messages m WHERE m.threadId = threads.threadId " +
|
|
939
|
+
"AND m.timestamp > threads.leftAt AND COALESCE(m.type, '') <> 'group_event'))";
|
|
940
|
+
const missingAndQuiet =
|
|
941
|
+
"threads.threadId NOT IN (SELECT value FROM json_each(@present)) " +
|
|
942
|
+
"AND threads.threadId NOT IN (SELECT value FROM json_each(@keep)) " +
|
|
943
|
+
`AND COALESCE(threads.lastUpdate, 0) < @markBefore AND ${notGone}`;
|
|
944
|
+
return db.transaction(() => {
|
|
945
|
+
const cleared = db
|
|
946
|
+
.prepare(
|
|
947
|
+
"UPDATE threads SET leftAt = NULL WHERE leftAt IS NOT NULL AND leftAt < @clearBefore " +
|
|
948
|
+
"AND threadId IN (SELECT value FROM json_each(@present)) RETURNING threadId",
|
|
949
|
+
)
|
|
950
|
+
.all(params)
|
|
951
|
+
.map((r) => r.threadId);
|
|
952
|
+
const marked = db
|
|
953
|
+
.prepare(
|
|
954
|
+
`UPDATE threads SET leftAt = @markBefore WHERE ${missingAndQuiet} ` +
|
|
955
|
+
"AND threads.type IN (SELECT value FROM json_each(@kinds)) RETURNING threadId",
|
|
956
|
+
)
|
|
957
|
+
.all(params)
|
|
958
|
+
.map((r) => r.threadId);
|
|
959
|
+
const { unproven } = db
|
|
960
|
+
.prepare(
|
|
961
|
+
`SELECT COUNT(*) AS unproven FROM threads WHERE ${missingAndQuiet} ` +
|
|
962
|
+
"AND (threads.type IS NULL OR threads.type NOT IN (SELECT value FROM json_each(@kinds))) " +
|
|
963
|
+
"AND EXISTS (SELECT 1 FROM messages m WHERE m.threadId = threads.threadId)",
|
|
964
|
+
)
|
|
965
|
+
.get(params);
|
|
966
|
+
return { cleared, marked, unproven };
|
|
967
|
+
})();
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
/**
|
|
971
|
+
* Id prefix of a group-event row the LISTENER wrote.
|
|
972
|
+
*
|
|
973
|
+
* A group system line (a rename, a member joining, a pin) reaches the socket
|
|
974
|
+
* only as a cmd 601 control, and that control carries no message id: its
|
|
975
|
+
* actionId/controlId are queue sequence numbers. The row a mobile sync
|
|
976
|
+
* restores for the same event is keyed by the PHONE's own clientId -- a local
|
|
977
|
+
* timestamp taken when the phone processed the event, 0.3-5.7 s after it
|
|
978
|
+
* happened (measured). No shared key exists, so the listener writes under this
|
|
979
|
+
* prefix and a sync replaces it.
|
|
980
|
+
*/
|
|
981
|
+
export const LIVE_GROUP_EVENT_PREFIX = "ge:";
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Delete the listener's placeholder for a group event a sync just restored.
|
|
985
|
+
*
|
|
986
|
+
* Matches on thread and time only, because nothing else is shared: the synced
|
|
987
|
+
* row's timestamp landed 1 and 4 ms from the live event's time on the two
|
|
988
|
+
* events measured. Only the single closest placeholder is removed, so two
|
|
989
|
+
* events a few seconds apart each keep their own row.
|
|
990
|
+
*
|
|
991
|
+
* @param {string} threadId
|
|
992
|
+
* @param {number} timestamp - the synced row's timestamp
|
|
993
|
+
* @param {number} [windowMs=2000]
|
|
994
|
+
* @returns {number} rows deleted (0 or 1)
|
|
995
|
+
*/
|
|
996
|
+
export function replaceLiveGroupEventPlaceholder(threadId, timestamp, windowMs = 2000) {
|
|
997
|
+
if (!db) throw new Error("Database not initialized");
|
|
998
|
+
return db
|
|
999
|
+
.prepare(
|
|
1000
|
+
"DELETE FROM messages WHERE msgId = (SELECT msgId FROM messages WHERE threadId = @threadId " +
|
|
1001
|
+
"AND type = 'group_event' AND msgId LIKE @prefix AND abs(timestamp - @ts) <= @win " +
|
|
1002
|
+
"ORDER BY abs(timestamp - @ts) LIMIT 1)",
|
|
1003
|
+
)
|
|
1004
|
+
.run({
|
|
1005
|
+
threadId: String(threadId),
|
|
1006
|
+
ts: Number(timestamp),
|
|
1007
|
+
win: windowMs,
|
|
1008
|
+
prefix: `${LIVE_GROUP_EVENT_PREFIX}%`,
|
|
1009
|
+
}).changes;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
/**
|
|
1013
|
+
* Whether a sync has already stored the system line for an event at this time.
|
|
1014
|
+
*
|
|
1015
|
+
* @param {string} threadId
|
|
1016
|
+
* @param {number} timestamp
|
|
1017
|
+
* @param {number} [windowMs=2000]
|
|
1018
|
+
* @returns {boolean}
|
|
1019
|
+
*/
|
|
1020
|
+
export function hasSyncedGroupEventNear(threadId, timestamp, windowMs = 2000) {
|
|
1021
|
+
if (!db) throw new Error("Database not initialized");
|
|
1022
|
+
return Boolean(
|
|
1023
|
+
db
|
|
1024
|
+
.prepare(
|
|
1025
|
+
"SELECT 1 FROM messages WHERE threadId = ? AND type = 'group_event' AND msgId NOT LIKE ? " +
|
|
1026
|
+
"AND abs(timestamp - ?) <= ? LIMIT 1",
|
|
1027
|
+
)
|
|
1028
|
+
.get(String(threadId), `${LIVE_GROUP_EVENT_PREFIX}%`, Number(timestamp), windowMs),
|
|
1029
|
+
);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* Record that a conversation is no longer ours: deleted, or we left, were
|
|
1034
|
+
* removed or were blocked.
|
|
1035
|
+
*
|
|
1036
|
+
* Sets the gone marker and the delete marker together, because both callers
|
|
1037
|
+
* -- `conv delete` and our own leave/removal/block event -- are a deletion as
|
|
1038
|
+
* far as fetched history goes. A conversation list is not, and records the
|
|
1039
|
+
* gone marker alone ({@link recordConversationList}).
|
|
1040
|
+
*
|
|
1041
|
+
* @param {string} threadId
|
|
1042
|
+
* @param {number} [at=Date.now()] - epoch ms; the event's own time when it has one
|
|
1043
|
+
*/
|
|
1044
|
+
export function markThreadGone(threadId, at = Date.now()) {
|
|
1045
|
+
if (!db) throw new Error("Database not initialized");
|
|
1046
|
+
return db
|
|
1047
|
+
.prepare(
|
|
1048
|
+
"UPDATE threads SET leftAt = @at, deleteMarkerAt = MAX(COALESCE(deleteMarkerAt, 0), @at) " +
|
|
1049
|
+
"WHERE threadId = @threadId",
|
|
1050
|
+
)
|
|
1051
|
+
.run({ at: Number(at), threadId: String(threadId) });
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* Record that the account was seen in a conversation: clear its gone marker,
|
|
1056
|
+
* if that marker predates the sighting.
|
|
1057
|
+
*
|
|
1058
|
+
* Zalo delivers a conversation's traffic only to the people in it, so a
|
|
1059
|
+
* message or group event dated after we left means we are back. One dated
|
|
1060
|
+
* before it proves nothing -- a backlog replay, or a late delivery of
|
|
1061
|
+
* something said before we left, must not undo the departure. The delete
|
|
1062
|
+
* marker stays either way ({@link getThreadDeleteMarker}).
|
|
1063
|
+
*
|
|
1064
|
+
* @param {string} threadId
|
|
1065
|
+
* @param {number} seenAt - epoch ms of the sighting
|
|
1066
|
+
* @returns {{changes: number}} 1 when a marker was cleared
|
|
1067
|
+
*/
|
|
1068
|
+
export function markThreadBack(threadId, seenAt) {
|
|
1069
|
+
if (!db) throw new Error("Database not initialized");
|
|
1070
|
+
const at = Number(seenAt);
|
|
1071
|
+
if (!Number.isFinite(at)) return { changes: 0 };
|
|
1072
|
+
return db
|
|
1073
|
+
.prepare("UPDATE threads SET leftAt = NULL WHERE threadId = ? AND leftAt IS NOT NULL AND leftAt < ?")
|
|
1074
|
+
.run(String(threadId), at);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
/**
|
|
1078
|
+
* Delete every local trace of one conversation.
|
|
1079
|
+
*
|
|
1080
|
+
* Deliberately explicit and never automatic. Files on disk are the caller's
|
|
1081
|
+
* job (see pruneDownloadedMedia); this clears the rows, and reports what it
|
|
1082
|
+
* removed so the caller can say so rather than claim a silent success.
|
|
1083
|
+
*
|
|
1084
|
+
* @returns {{messages:number, threads:number, boardItems:number, reminders:number, reactions:number, cloudItems:number, convState:number}}
|
|
1085
|
+
*/
|
|
1086
|
+
export function forgetThread(threadId) {
|
|
1087
|
+
if (!db) throw new Error("Database not initialized");
|
|
1088
|
+
const id = String(threadId);
|
|
1089
|
+
const counts = {};
|
|
1090
|
+
const run = (key, sql) => {
|
|
1091
|
+
counts[key] = db.prepare(sql).run(id).changes;
|
|
1092
|
+
};
|
|
1093
|
+
db.transaction(() => {
|
|
1094
|
+
run("messages", "DELETE FROM messages WHERE threadId = ?");
|
|
1095
|
+
run("reactions", "DELETE FROM reactions WHERE threadId = ?");
|
|
1096
|
+
run("boardItems", "DELETE FROM board_items WHERE threadId = ?");
|
|
1097
|
+
run("reminders", "DELETE FROM reminders WHERE threadId = ?");
|
|
1098
|
+
run("cloudItems", "DELETE FROM cloud_items WHERE threadId = ?");
|
|
1099
|
+
run("convState", "DELETE FROM conv_state WHERE threadId = ?");
|
|
1100
|
+
run("threads", "DELETE FROM threads WHERE threadId = ?");
|
|
1101
|
+
})();
|
|
1102
|
+
return counts;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
/**
|
|
1106
|
+
* Forget where an attachment was stored, after its file has been removed.
|
|
1107
|
+
*
|
|
1108
|
+
* Clearing `localPath` is what puts the row back in the download queue, so a
|
|
1109
|
+
* pruned message can be fetched again later if its link is still alive.
|
|
1110
|
+
*/
|
|
1111
|
+
export function clearMessageLocalPath(msgId, prunedAt = null) {
|
|
1112
|
+
if (!db) throw new Error("Database not initialized");
|
|
1113
|
+
return db
|
|
1114
|
+
.prepare("UPDATE messages SET localPath = NULL, mediaPrunedAt = ? WHERE msgId = ?")
|
|
1115
|
+
.run(prunedAt === null ? null : Number(prunedAt), String(msgId));
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* Record where an attachment was written to disk.
|
|
1120
|
+
*
|
|
1121
|
+
* Clears any prune marker: the file is back, so the earlier decision to remove
|
|
1122
|
+
* it no longer applies.
|
|
1123
|
+
*/
|
|
1124
|
+
export function setMessageLocalPath(msgId, localPath) {
|
|
1125
|
+
if (!db) throw new Error("Database not initialized");
|
|
1126
|
+
return db
|
|
1127
|
+
.prepare("UPDATE messages SET localPath = ?, mediaPrunedAt = NULL WHERE msgId = ?")
|
|
1128
|
+
.run(localPath, String(msgId));
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
/** How many attachment-bearing messages are still undownloaded. */
|
|
1132
|
+
export function countPendingAttachments(threadId = null) {
|
|
1133
|
+
if (!db) throw new Error("Database not initialized");
|
|
1134
|
+
const sql =
|
|
1135
|
+
"SELECT count(*) AS n FROM messages WHERE has_attachment = 1 AND localPath IS NULL AND mediaPrunedAt IS NULL" +
|
|
1136
|
+
(threadId ? " AND threadId = ?" : "");
|
|
1137
|
+
return (threadId ? db.prepare(sql).get(String(threadId)) : db.prepare(sql).get()).n;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
export function upsertBoardItem(item) {
|
|
1141
|
+
if (!db) throw new Error("Database not initialized");
|
|
1142
|
+
return db
|
|
1143
|
+
.prepare(
|
|
1144
|
+
`INSERT INTO board_items
|
|
1145
|
+
(id, threadId, threadType, boardType, itemId, title, creatorId, createTime, editTime,
|
|
1146
|
+
startTime, duration, repeat, emoji, color, raw_data, sync_timestamp)
|
|
1147
|
+
VALUES (@id, @threadId, @threadType, @boardType, @itemId, @title, @creatorId, @createTime, @editTime,
|
|
1148
|
+
@startTime, @duration, @repeat, @emoji, @color, @raw_data, @sync_timestamp)
|
|
1149
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
1150
|
+
title = excluded.title, editTime = excluded.editTime, startTime = excluded.startTime,
|
|
1151
|
+
duration = excluded.duration, repeat = excluded.repeat, emoji = excluded.emoji,
|
|
1152
|
+
color = excluded.color, raw_data = excluded.raw_data, sync_timestamp = excluded.sync_timestamp`,
|
|
1153
|
+
)
|
|
1154
|
+
.run({
|
|
1155
|
+
id: `${item.threadId}:${item.boardType}:${item.itemId}`,
|
|
1156
|
+
threadId: String(item.threadId),
|
|
1157
|
+
threadType: item.threadType || null,
|
|
1158
|
+
boardType: Number(item.boardType) || 0,
|
|
1159
|
+
itemId: String(item.itemId),
|
|
1160
|
+
title: item.title ?? null,
|
|
1161
|
+
creatorId: item.creatorId ?? null,
|
|
1162
|
+
createTime: Number(item.createTime) || null,
|
|
1163
|
+
editTime: Number(item.editTime) || null,
|
|
1164
|
+
startTime: Number(item.startTime) || null,
|
|
1165
|
+
duration: Number(item.duration) || null,
|
|
1166
|
+
repeat: Number(item.repeat) || null,
|
|
1167
|
+
emoji: item.emoji ?? null,
|
|
1168
|
+
color: Number(item.color) || null,
|
|
1169
|
+
raw_data: typeof item.raw_data === "object" ? JSON.stringify(item.raw_data) : (item.raw_data ?? null),
|
|
1170
|
+
sync_timestamp: item.sync_timestamp || Date.now(),
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
/** Board items for a thread (or all threads), optionally one boardType. */
|
|
1175
|
+
export function getBoardItems(threadId = null, boardType = null) {
|
|
1176
|
+
if (!db) throw new Error("Database not initialized");
|
|
1177
|
+
const where = [];
|
|
1178
|
+
const params = [];
|
|
1179
|
+
if (threadId) (where.push("threadId = ?"), params.push(String(threadId)));
|
|
1180
|
+
if (boardType) (where.push("boardType = ?"), params.push(Number(boardType)));
|
|
1181
|
+
const sql = `SELECT * FROM board_items${where.length ? ` WHERE ${where.join(" AND ")}` : ""} ORDER BY createTime DESC`;
|
|
1182
|
+
return db.prepare(sql).all(...params);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
export function upsertReminder(r) {
|
|
1186
|
+
if (!db) throw new Error("Database not initialized");
|
|
1187
|
+
return db
|
|
1188
|
+
.prepare(
|
|
1189
|
+
`INSERT INTO reminders
|
|
1190
|
+
(id, reminderId, threadId, threadType, title, creatorId, createTime, editTime,
|
|
1191
|
+
startTime, duration, repeatMode, emoji, color, eventType, raw_data, sync_timestamp)
|
|
1192
|
+
VALUES (@id, @reminderId, @threadId, @threadType, @title, @creatorId, @createTime, @editTime,
|
|
1193
|
+
@startTime, @duration, @repeatMode, @emoji, @color, @eventType, @raw_data, @sync_timestamp)
|
|
1194
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
1195
|
+
title = excluded.title, editTime = excluded.editTime, startTime = excluded.startTime,
|
|
1196
|
+
duration = excluded.duration, repeatMode = excluded.repeatMode, emoji = excluded.emoji,
|
|
1197
|
+
color = excluded.color, eventType = excluded.eventType, raw_data = excluded.raw_data,
|
|
1198
|
+
sync_timestamp = excluded.sync_timestamp`,
|
|
1199
|
+
)
|
|
1200
|
+
.run({
|
|
1201
|
+
id: `${r.threadId}:${r.reminderId}`,
|
|
1202
|
+
reminderId: String(r.reminderId),
|
|
1203
|
+
threadId: String(r.threadId),
|
|
1204
|
+
threadType: r.threadType || null,
|
|
1205
|
+
title: r.title ?? null,
|
|
1206
|
+
creatorId: r.creatorId ?? null,
|
|
1207
|
+
createTime: Number(r.createTime) || null,
|
|
1208
|
+
editTime: Number(r.editTime) || null,
|
|
1209
|
+
startTime: Number(r.startTime) || null,
|
|
1210
|
+
duration: Number(r.duration) || null,
|
|
1211
|
+
repeatMode: Number(r.repeatMode) || 0,
|
|
1212
|
+
emoji: r.emoji ?? null,
|
|
1213
|
+
color: Number(r.color) || null,
|
|
1214
|
+
eventType: Number(r.eventType) || null,
|
|
1215
|
+
raw_data: typeof r.raw_data === "object" ? JSON.stringify(r.raw_data) : (r.raw_data ?? null),
|
|
1216
|
+
sync_timestamp: r.sync_timestamp || Date.now(),
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/** Reminders for a thread, or all of them, soonest start first. */
|
|
1221
|
+
export function getReminders(threadId = null) {
|
|
1222
|
+
if (!db) throw new Error("Database not initialized");
|
|
1223
|
+
return threadId
|
|
1224
|
+
? db.prepare("SELECT * FROM reminders WHERE threadId = ? ORDER BY startTime ASC").all(String(threadId))
|
|
1225
|
+
: db.prepare("SELECT * FROM reminders ORDER BY startTime ASC").all();
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
/**
|
|
1229
|
+
* Record a zCloud media item. Keyed by Zalo's `noiseId`, which is the id the
|
|
1230
|
+
* cloud queue and the download endpoint both speak.
|
|
1231
|
+
*/
|
|
1232
|
+
export function upsertCloudItem(item) {
|
|
1233
|
+
if (!db) throw new Error("Database not initialized");
|
|
1234
|
+
return db
|
|
1235
|
+
.prepare(
|
|
1236
|
+
`INSERT INTO cloud_items
|
|
1237
|
+
(noiseId, threadId, msgId, msgType, mediaType, cloudUrl, encryptKey, checksum,
|
|
1238
|
+
mediaSize, timestamp, localPath, raw_data, sync_timestamp)
|
|
1239
|
+
VALUES (@noiseId, @threadId, @msgId, @msgType, @mediaType, @cloudUrl, @encryptKey, @checksum,
|
|
1240
|
+
@mediaSize, @timestamp, @localPath, @raw_data, @sync_timestamp)
|
|
1241
|
+
ON CONFLICT(noiseId) DO UPDATE SET
|
|
1242
|
+
threadId = COALESCE(excluded.threadId, cloud_items.threadId),
|
|
1243
|
+
msgId = COALESCE(excluded.msgId, cloud_items.msgId),
|
|
1244
|
+
cloudUrl = excluded.cloudUrl,
|
|
1245
|
+
encryptKey = COALESCE(excluded.encryptKey, cloud_items.encryptKey),
|
|
1246
|
+
checksum = COALESCE(excluded.checksum, cloud_items.checksum),
|
|
1247
|
+
mediaSize = COALESCE(excluded.mediaSize, cloud_items.mediaSize),
|
|
1248
|
+
localPath = COALESCE(excluded.localPath, cloud_items.localPath),
|
|
1249
|
+
raw_data = excluded.raw_data,
|
|
1250
|
+
sync_timestamp = excluded.sync_timestamp`,
|
|
1251
|
+
)
|
|
1252
|
+
.run({
|
|
1253
|
+
noiseId: String(item.noiseId),
|
|
1254
|
+
threadId: item.threadId === undefined || item.threadId === null ? null : String(item.threadId),
|
|
1255
|
+
msgId: item.msgId === undefined || item.msgId === null ? null : String(item.msgId),
|
|
1256
|
+
msgType: Number(item.msgType) || null,
|
|
1257
|
+
mediaType: Number(item.mediaType) || null,
|
|
1258
|
+
cloudUrl: item.cloudUrl ?? null,
|
|
1259
|
+
encryptKey: item.encryptKey ?? null,
|
|
1260
|
+
checksum: item.checksum ?? null,
|
|
1261
|
+
mediaSize: Number(item.mediaSize) || null,
|
|
1262
|
+
timestamp: Number(item.timestamp) || null,
|
|
1263
|
+
localPath: item.localPath ?? null,
|
|
1264
|
+
raw_data: typeof item.raw_data === "object" ? JSON.stringify(item.raw_data) : (item.raw_data ?? null),
|
|
1265
|
+
sync_timestamp: item.sync_timestamp || Date.now(),
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/** zCloud items, newest first; optionally one thread's. */
|
|
1270
|
+
export function getCloudItems(threadId = null, limit = 500) {
|
|
1271
|
+
if (!db) throw new Error("Database not initialized");
|
|
1272
|
+
return threadId
|
|
1273
|
+
? db
|
|
1274
|
+
.prepare("SELECT * FROM cloud_items WHERE threadId = ? ORDER BY timestamp DESC LIMIT ?")
|
|
1275
|
+
.all(String(threadId), limit)
|
|
1276
|
+
: db.prepare("SELECT * FROM cloud_items ORDER BY timestamp DESC LIMIT ?").all(limit);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
/** The zCloud backup for one message, if the cloud queue has told us about it. */
|
|
1280
|
+
export function getCloudItemByMsgId(msgId) {
|
|
1281
|
+
if (!db) throw new Error("Database not initialized");
|
|
1282
|
+
return db.prepare("SELECT * FROM cloud_items WHERE msgId = ? LIMIT 1").get(String(msgId)) || null;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
/**
|
|
1286
|
+
* Advance a message's delivery state.
|
|
1287
|
+
*
|
|
1288
|
+
* Only ever forwards: a late-arriving "delivered" must not undo a "seen" that
|
|
1289
|
+
* already landed, and re-syncing an older snapshot must not either.
|
|
1290
|
+
*/
|
|
1291
|
+
export function setMessageStatus(msgId, status) {
|
|
1292
|
+
if (!db) throw new Error("Database not initialized");
|
|
1293
|
+
return db
|
|
1294
|
+
.prepare("UPDATE messages SET msgStatus = MAX(COALESCE(msgStatus, 0), ?) WHERE msgId = ?")
|
|
1295
|
+
.run(Number(status) || 0, String(msgId));
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
/** Sync2.Message.MessageStatus: delivery and read state. */
|
|
1299
|
+
export const MESSAGE_STATUS = {
|
|
1300
|
+
0: "unspecified",
|
|
1301
|
+
1: "failed",
|
|
1302
|
+
2: "sending",
|
|
1303
|
+
3: "sent",
|
|
1304
|
+
4: "received",
|
|
1305
|
+
5: "seen",
|
|
1306
|
+
};
|
|
1307
|
+
|
|
1308
|
+
/**
|
|
1309
|
+
* Record a reaction.
|
|
1310
|
+
*
|
|
1311
|
+
* One person holds at most one reaction per message, so the key is
|
|
1312
|
+
* (msgId, userId): reacting again replaces it, and Zalo signals removal by
|
|
1313
|
+
* sending an empty icon, which deletes the row rather than storing a blank.
|
|
1314
|
+
*/
|
|
1315
|
+
/**
|
|
1316
|
+
* A reaction type as a number, or null when Zalo did not send one.
|
|
1317
|
+
*
|
|
1318
|
+
* `Number.isFinite(Number(v))` is not enough on its own: `Number(null)` is 0,
|
|
1319
|
+
* and 0 is a real reaction type (HAHA), so a missing type silently became a
|
|
1320
|
+
* haha -- and a removal that named no type deleted the wrong row.
|
|
1321
|
+
*
|
|
1322
|
+
* @param {unknown} v
|
|
1323
|
+
* @returns {number|null}
|
|
1324
|
+
*/
|
|
1325
|
+
function reactionType(v) {
|
|
1326
|
+
if (v === null || v === undefined || v === "") return null;
|
|
1327
|
+
const n = Number(v);
|
|
1328
|
+
return Number.isFinite(n) ? n : null;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
export function upsertReaction(r) {
|
|
1332
|
+
if (!db) throw new Error("Database not initialized");
|
|
1333
|
+
// The icon is part of the identity: the same person holding both a heart
|
|
1334
|
+
// and a haha on one message is two rows, which is what Zalo displays.
|
|
1335
|
+
const id = `${r.msgId}:${r.userId}:${r.icon}`;
|
|
1336
|
+
if (!r.icon) {
|
|
1337
|
+
// Removal is ALL-OR-NOTHING, measured against a real frame: un-reacting
|
|
1338
|
+
// sends rIcon "" with rType -1 -- a sentinel, not one of the types to
|
|
1339
|
+
// match -- and the app offers no way to drop one of several icons. An
|
|
1340
|
+
// earlier build read that -1 as a type and deleted WHERE rType = -1,
|
|
1341
|
+
// which matched nothing, so a removal silently left the reactions in
|
|
1342
|
+
// place.
|
|
1343
|
+
return db
|
|
1344
|
+
.prepare("DELETE FROM reactions WHERE msgId = ? AND userId = ?")
|
|
1345
|
+
.run(String(r.msgId), String(r.userId));
|
|
1346
|
+
}
|
|
1347
|
+
return db
|
|
1348
|
+
.prepare(
|
|
1349
|
+
`INSERT INTO reactions (id, msgId, threadId, userId, icon, rType, source, timestamp)
|
|
1350
|
+
VALUES (@id, @msgId, @threadId, @userId, @icon, @rType, @source, @timestamp)
|
|
1351
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
1352
|
+
icon = excluded.icon, rType = excluded.rType,
|
|
1353
|
+
source = excluded.source, timestamp = excluded.timestamp`,
|
|
1354
|
+
)
|
|
1355
|
+
.run({
|
|
1356
|
+
id,
|
|
1357
|
+
msgId: String(r.msgId),
|
|
1358
|
+
threadId: r.threadId === null || r.threadId === undefined ? null : String(r.threadId),
|
|
1359
|
+
userId: String(r.userId),
|
|
1360
|
+
icon: r.icon,
|
|
1361
|
+
// 0 is a real reaction type (HAHA), and a missing one is not 0.
|
|
1362
|
+
rType: reactionType(r.rType),
|
|
1363
|
+
source: r.source || "listen",
|
|
1364
|
+
timestamp: Number(r.timestamp) || Date.now(),
|
|
1365
|
+
});
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
/** Reactions on one message, or across a thread. */
|
|
1369
|
+
export function getReactions({ msgId = null, threadId = null, limit = 500 } = {}) {
|
|
1370
|
+
if (!db) throw new Error("Database not initialized");
|
|
1371
|
+
if (msgId) return db.prepare("SELECT * FROM reactions WHERE msgId = ?").all(String(msgId));
|
|
1372
|
+
if (threadId)
|
|
1373
|
+
return db
|
|
1374
|
+
.prepare("SELECT * FROM reactions WHERE threadId = ? ORDER BY timestamp DESC LIMIT ?")
|
|
1375
|
+
.all(String(threadId), limit);
|
|
1376
|
+
return db.prepare("SELECT * FROM reactions ORDER BY timestamp DESC LIMIT ?").all(limit);
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
/**
|
|
1380
|
+
* Mark a message recalled without deleting the row.
|
|
1381
|
+
*
|
|
1382
|
+
* Zalo's "Thu hồi" removes the message for everyone; a cache that keeps the
|
|
1383
|
+
* text readable afterwards is retaining something the sender withdrew. The row
|
|
1384
|
+
* stays so the conversation still shows that something was there and when.
|
|
1385
|
+
*/
|
|
1386
|
+
export function markMessageRecalled(msgId, at = Date.now(), opts = {}) {
|
|
1387
|
+
if (!db) throw new Error("Database not initialized");
|
|
1388
|
+
const row = db
|
|
1389
|
+
.prepare("SELECT type, raw_data, localPath, has_attachment FROM messages WHERE msgId = ?")
|
|
1390
|
+
.get(String(msgId));
|
|
1391
|
+
// Nothing to tombstone. Reported rather than swallowed: the previous
|
|
1392
|
+
// version always claimed success, which is how a recall handler that
|
|
1393
|
+
// updated zero rows went unnoticed.
|
|
1394
|
+
if (!row) return { changes: 0, localPath: null, missing: true };
|
|
1395
|
+
|
|
1396
|
+
// Keep the identifiers, drop the content. `cliMsgId` is the only local copy
|
|
1397
|
+
// of a client id and `msg delete` / `msg undo` / `conv delete` all need it,
|
|
1398
|
+
// so replacing raw_data wholesale made a recalled message undeletable.
|
|
1399
|
+
// `originalType` mirrors what the phone itself preserves for a removed
|
|
1400
|
+
// message (params.original_type), so our tombstone is no poorer than
|
|
1401
|
+
// Zalo's own.
|
|
1402
|
+
let cliMsgId;
|
|
1403
|
+
try {
|
|
1404
|
+
cliMsgId = JSON.parse(row.raw_data)?.cliMsgId;
|
|
1405
|
+
} catch {
|
|
1406
|
+
/* 108k legacy rows hold the bare content string, not JSON */
|
|
1407
|
+
}
|
|
1408
|
+
const raw = JSON.stringify({
|
|
1409
|
+
src: "listen",
|
|
1410
|
+
removedAs: opts.reason === "delete-for-me" ? "delete-for-me" : "recall",
|
|
1411
|
+
removedAt: Number(at) || Date.now(),
|
|
1412
|
+
originalType: row.type,
|
|
1413
|
+
cliMsgId: cliMsgId === undefined || cliMsgId === null ? undefined : String(cliMsgId),
|
|
1414
|
+
});
|
|
1415
|
+
const hadMedia = row.has_attachment === 1 || !!row.localPath;
|
|
1416
|
+
|
|
1417
|
+
const res = db
|
|
1418
|
+
.prepare(
|
|
1419
|
+
`UPDATE messages SET
|
|
1420
|
+
type = 'deleted',
|
|
1421
|
+
text = '[deleted]',
|
|
1422
|
+
raw_data = @raw,
|
|
1423
|
+
-- The media goes with the message. Leaving has_attachment set would
|
|
1424
|
+
-- queue the attachment of a just-withdrawn message for download;
|
|
1425
|
+
-- mediaPrunedAt is the existing "do not fetch this again" marker.
|
|
1426
|
+
has_attachment = 0,
|
|
1427
|
+
localPath = NULL,
|
|
1428
|
+
mediaPrunedAt = CASE WHEN @hadMedia = 1 THEN @at ELSE mediaPrunedAt END
|
|
1429
|
+
WHERE msgId = @msgId`,
|
|
1430
|
+
)
|
|
1431
|
+
.run({ raw, at: Number(at) || Date.now(), hadMedia: hadMedia ? 1 : 0, msgId: String(msgId) });
|
|
1432
|
+
|
|
1433
|
+
return { changes: res.changes, localPath: row.localPath || null, originalType: row.type, missing: false };
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
/**
|
|
1437
|
+
* Find a message by the client-side id its sender generated.
|
|
1438
|
+
*
|
|
1439
|
+
* A "delete for me" frame names its target by `clientDelMsgId` when the
|
|
1440
|
+
* deleting client did not know the server id, so this is the only way to
|
|
1441
|
+
* resolve it. Guarded with json_valid() because an older writer stored the
|
|
1442
|
+
* bare content string in raw_data on six figures of rows, and an unguarded
|
|
1443
|
+
* json_extract over this table raises "malformed JSON" and takes the whole
|
|
1444
|
+
* query with it.
|
|
1445
|
+
*
|
|
1446
|
+
* @param {string|number} cliMsgId
|
|
1447
|
+
* @returns {object|null}
|
|
1448
|
+
*/
|
|
1449
|
+
export function findMessageByClientId(cliMsgId) {
|
|
1450
|
+
if (!db) throw new Error("Database not initialized");
|
|
1451
|
+
if (cliMsgId === undefined || cliMsgId === null || cliMsgId === "") return null;
|
|
1452
|
+
return (
|
|
1453
|
+
db
|
|
1454
|
+
.prepare(
|
|
1455
|
+
`SELECT * FROM messages
|
|
1456
|
+
WHERE json_valid(raw_data) AND json_extract(raw_data, '$.cliMsgId') = ?
|
|
1457
|
+
ORDER BY timestamp DESC LIMIT 1`,
|
|
1458
|
+
)
|
|
1459
|
+
.get(String(cliMsgId)) || null
|
|
1460
|
+
);
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
/** Pinned / unread-marked state for a conversation. Applies to DMs too. */
|
|
1464
|
+
export function upsertConvState(st) {
|
|
1465
|
+
if (!db) throw new Error("Database not initialized");
|
|
1466
|
+
return db
|
|
1467
|
+
.prepare(
|
|
1468
|
+
`INSERT INTO conv_state (threadId, pinned, pinnedAt, unreadMarked, unreadMarkedAt, updatedAt)
|
|
1469
|
+
VALUES (@threadId, @pinned, @pinnedAt, @unreadMarked, @unreadMarkedAt, @updatedAt)
|
|
1470
|
+
ON CONFLICT(threadId) DO UPDATE SET
|
|
1471
|
+
pinned = COALESCE(excluded.pinned, conv_state.pinned),
|
|
1472
|
+
pinnedAt = COALESCE(excluded.pinnedAt, conv_state.pinnedAt),
|
|
1473
|
+
unreadMarked = COALESCE(excluded.unreadMarked, conv_state.unreadMarked),
|
|
1474
|
+
unreadMarkedAt = COALESCE(excluded.unreadMarkedAt, conv_state.unreadMarkedAt),
|
|
1475
|
+
updatedAt = excluded.updatedAt`,
|
|
1476
|
+
)
|
|
1477
|
+
.run({
|
|
1478
|
+
threadId: String(st.threadId),
|
|
1479
|
+
pinned: st.pinned === undefined ? null : st.pinned ? 1 : 0,
|
|
1480
|
+
pinnedAt: st.pinned === undefined ? null : Number(st.pinnedAt) || Date.now(),
|
|
1481
|
+
unreadMarked: st.unreadMarked === undefined ? null : st.unreadMarked ? 1 : 0,
|
|
1482
|
+
unreadMarkedAt: st.unreadMarked === undefined ? null : Number(st.unreadMarkedAt) || Date.now(),
|
|
1483
|
+
updatedAt: Date.now(),
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
/** Conversation state for one thread, or every thread that has any. */
|
|
1488
|
+
export function getConvState(threadId = null) {
|
|
1489
|
+
if (!db) throw new Error("Database not initialized");
|
|
1490
|
+
return threadId
|
|
1491
|
+
? db.prepare("SELECT * FROM conv_state WHERE threadId = ?").get(String(threadId)) || null
|
|
1492
|
+
: db.prepare("SELECT * FROM conv_state ORDER BY updatedAt DESC").all();
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
/**
|
|
1496
|
+
* Move a conversation's read watermark: the newest message this account has
|
|
1497
|
+
* read in it, on any device.
|
|
1498
|
+
*
|
|
1499
|
+
* It only moves forward. The same read is reported more than once -- on its
|
|
1500
|
+
* own cmd and again inside the next envelope -- and an offline-queue page can
|
|
1501
|
+
* arrive after a newer live push, so an older or equal id changes nothing.
|
|
1502
|
+
* Message ids are global and increase with time, so a larger id is a later
|
|
1503
|
+
* message; they are compared as integers, never as strings or doubles.
|
|
1504
|
+
*
|
|
1505
|
+
* @param {{threadId: string, msgId: string, ts?: number}} read - `ts` is when
|
|
1506
|
+
* the read was reported; now when the report carries none
|
|
1507
|
+
* @returns {boolean} whether the watermark moved
|
|
1508
|
+
*/
|
|
1509
|
+
export function recordReadWatermark({ threadId, msgId, ts }) {
|
|
1510
|
+
if (!db) throw new Error("Database not initialized");
|
|
1511
|
+
const id = String(msgId ?? "");
|
|
1512
|
+
if (!/^\d+$/.test(id) || BigInt(id) === 0n) return false;
|
|
1513
|
+
const now = Date.now();
|
|
1514
|
+
return db.transaction(() => {
|
|
1515
|
+
const cur = db.prepare("SELECT lastReadMsgId FROM conv_state WHERE threadId = ?").get(String(threadId));
|
|
1516
|
+
if (/^\d+$/.test(cur?.lastReadMsgId ?? "") && BigInt(cur.lastReadMsgId) >= BigInt(id)) return false;
|
|
1517
|
+
db.prepare(
|
|
1518
|
+
`INSERT INTO conv_state (threadId, lastReadMsgId, lastReadTs, updatedAt)
|
|
1519
|
+
VALUES (?, ?, ?, ?)
|
|
1520
|
+
ON CONFLICT(threadId) DO UPDATE SET
|
|
1521
|
+
lastReadMsgId = excluded.lastReadMsgId,
|
|
1522
|
+
lastReadTs = excluded.lastReadTs,
|
|
1523
|
+
updatedAt = excluded.updatedAt`,
|
|
1524
|
+
).run(String(threadId), id, Number(ts) > 0 ? Number(ts) : now, now);
|
|
1525
|
+
return true;
|
|
1526
|
+
})();
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
/**
|
|
1530
|
+
* Where the account stands on reading each conversation: its read watermark,
|
|
1531
|
+
* its manual unread mark, and how many messages from others the cache holds
|
|
1532
|
+
* after the watermark.
|
|
1533
|
+
*
|
|
1534
|
+
* `unreadAfter` counts cached messages only -- ids after the watermark that
|
|
1535
|
+
* someone other than this account sent, not recalled or deleted -- so it is a
|
|
1536
|
+
* floor, and null when no watermark is known yet ("never told", not "all
|
|
1537
|
+
* read"). Own messages are told apart as everywhere else: zca-js rewrites an
|
|
1538
|
+
* own message's raw sender "0" to this account's uid before it is stored.
|
|
1539
|
+
*
|
|
1540
|
+
* @param {string[]} threadIds
|
|
1541
|
+
* @param {string} ownId
|
|
1542
|
+
* @returns {Map<string, {lastReadMsgId: string|null, lastReadTs: number|null,
|
|
1543
|
+
* unreadMarked: boolean, unreadAfter: number|null}>} one entry per thread with any state
|
|
1544
|
+
*/
|
|
1545
|
+
export function getReadStates(threadIds, ownId) {
|
|
1546
|
+
if (!db) throw new Error("Database not initialized");
|
|
1547
|
+
const state = db.prepare(
|
|
1548
|
+
"SELECT threadId, lastReadMsgId, lastReadTs, unreadMarked FROM conv_state WHERE threadId = ?",
|
|
1549
|
+
);
|
|
1550
|
+
const after = db.prepare(
|
|
1551
|
+
`SELECT COUNT(*) AS n FROM messages
|
|
1552
|
+
WHERE threadId = ? AND msgId NOT GLOB '*[^0-9]*' AND msgId != ''
|
|
1553
|
+
AND CAST(msgId AS INTEGER) > CAST(? AS INTEGER)
|
|
1554
|
+
AND COALESCE(senderId, '') != ? AND COALESCE(type, '') != 'deleted'`,
|
|
1555
|
+
);
|
|
1556
|
+
const out = new Map();
|
|
1557
|
+
for (const threadId of threadIds) {
|
|
1558
|
+
const row = state.get(String(threadId));
|
|
1559
|
+
if (!row) continue;
|
|
1560
|
+
const known = /^\d+$/.test(row.lastReadMsgId ?? "");
|
|
1561
|
+
out.set(String(threadId), {
|
|
1562
|
+
lastReadMsgId: known ? row.lastReadMsgId : null,
|
|
1563
|
+
lastReadTs: known ? row.lastReadTs : null,
|
|
1564
|
+
unreadMarked: Boolean(row.unreadMarked),
|
|
1565
|
+
unreadAfter: known ? after.get(String(threadId), row.lastReadMsgId, String(ownId ?? "")).n : null,
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
return out;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
export function upsertContact(contact) {
|
|
1572
|
+
if (!db) throw new Error("Database not initialized");
|
|
1573
|
+
|
|
1574
|
+
const stmt = db.prepare(`
|
|
1575
|
+
INSERT INTO contacts (userId, name, phone)
|
|
1576
|
+
VALUES (@userId, @name, @phone)
|
|
1577
|
+
ON CONFLICT(userId) DO UPDATE SET
|
|
1578
|
+
name = excluded.name,
|
|
1579
|
+
phone = excluded.phone
|
|
1580
|
+
`);
|
|
1581
|
+
|
|
1582
|
+
return stmt.run({
|
|
1583
|
+
userId: contact.userId,
|
|
1584
|
+
name: contact.name,
|
|
1585
|
+
phone: contact.phone,
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
/**
|
|
1590
|
+
* The display name this account has seen for a user id, or null.
|
|
1591
|
+
*
|
|
1592
|
+
* Used to turn a `@[uid]` mention token into "@Display Name". Two sources,
|
|
1593
|
+
* in order:
|
|
1594
|
+
*
|
|
1595
|
+
* 1. `contacts` — authoritative, and the only source that can name someone
|
|
1596
|
+
* who has never posted. `msg send` fills it in: any uid it has to look
|
|
1597
|
+
* up through `getGroupMembersInfo` is written back here, so the next
|
|
1598
|
+
* send needs no call.
|
|
1599
|
+
* 2. the most recent `messages.senderName` for that sender — the live
|
|
1600
|
+
* listener stores Zalo's own `dName` on every row it writes, so anyone
|
|
1601
|
+
* who has spoken is named here without a network call.
|
|
1602
|
+
*
|
|
1603
|
+
* Null when the uid has never been seen; the caller falls back to the uid,
|
|
1604
|
+
* which still produces a valid mention.
|
|
1605
|
+
*
|
|
1606
|
+
* @param {string|number} userId
|
|
1607
|
+
* @returns {string|null}
|
|
1608
|
+
*/
|
|
1609
|
+
export function getDisplayName(userId) {
|
|
1610
|
+
if (!db) throw new Error("Database not initialized");
|
|
1611
|
+
if (userId === undefined || userId === null || userId === "") return null;
|
|
1612
|
+
const id = String(userId);
|
|
1613
|
+
|
|
1614
|
+
const contact = db.prepare("SELECT name FROM contacts WHERE userId = ?").get(id);
|
|
1615
|
+
if (contact?.name) return contact.name;
|
|
1616
|
+
|
|
1617
|
+
const seen = db
|
|
1618
|
+
.prepare(
|
|
1619
|
+
`SELECT senderName FROM messages
|
|
1620
|
+
WHERE senderId = ? AND senderName IS NOT NULL AND senderName <> ''
|
|
1621
|
+
ORDER BY timestamp DESC LIMIT 1`,
|
|
1622
|
+
)
|
|
1623
|
+
.get(id);
|
|
1624
|
+
return seen?.senderName || null;
|
|
1625
|
+
}
|