socialdatax-skills 0.2.21 → 0.2.22

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/cli.mjs CHANGED
@@ -1,17 +1,45 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import { randomUUID } from "node:crypto";
3
4
  import { existsSync, realpathSync } from "node:fs";
4
- import { cp, mkdir, readFile, rm, stat } from "node:fs/promises";
5
+ import {
6
+ cp,
7
+ mkdir,
8
+ open as openFile,
9
+ readFile,
10
+ rename,
11
+ rm,
12
+ stat,
13
+ unlink,
14
+ } from "node:fs/promises";
5
15
  import { homedir } from "node:os";
6
16
  import { dirname, join, resolve } from "node:path";
7
17
  import { fileURLToPath } from "node:url";
8
18
 
9
19
  const PACKAGE_NAME = "socialdatax-skills";
10
- const PACKAGE_VERSION = "0.2.21";
20
+ const PACKAGE_VERSION = "0.2.22";
11
21
  const PACKAGE_SPEC = `${PACKAGE_NAME}@latest`;
12
22
  const LOG_PREFIX = `[${PACKAGE_NAME}]`;
13
23
  const MIN_NODE_VERSION = "20.18.1";
14
24
  const HOMEPAGE_URL = "https://socialdatax.com";
25
+ const MEDIA_DOWNLOAD_USER_AGENT =
26
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36";
27
+ const WECHAT_MEDIA_DOWNLOAD_REFERER = "https://weixin.qq.com/";
28
+ const MEDIA_DOWNLOAD_TIMEOUT_MS = 10 * 60 * 1000;
29
+ const MEDIA_DOWNLOAD_MAX_BYTES = 2 * 1024 * 1024 * 1024;
30
+ const MEDIA_DECRYPT_CHUNK_SIZE = 64 * 1024;
31
+ const MEDIA_DOWNLOAD_MAX_REDIRECTS = 5;
32
+ // Keep this local helper scoped to WeChat media host trees; require k for the decrypt key.
33
+ const WECHAT_MEDIA_DOWNLOAD_HOST_SUFFIXES = [
34
+ ".wxapp.tc.qq.com",
35
+ ".video.qq.com",
36
+ ];
37
+ const WECHAT_MEDIA_DOWNLOAD_EXACT_HOSTS = new Set(["wxapp.tc.qq.com"]);
38
+ const WECHAT_MEDIA_DECODE_KEY_MAX = (1n << 64n) - 1n;
39
+ const WECHAT_DETAIL_MEDIA_URL_ERROR =
40
+ "Media URL cannot be decrypted. Use the video.video_url returned by SocialDataX WeChat detail.";
41
+ const WECHAT_MEDIA_RESPONSE_DECRYPT_ERROR =
42
+ "Downloaded media cannot be decrypted. Use the video.video_url returned by SocialDataX WeChat detail.";
15
43
  const PRIMARY_API_KEY_ENV = "SOCIALDATAX_API_KEY";
16
44
  const LEGACY_API_KEY_ENV = "SOCIAL_MEDIA_MCP_API_KEY";
17
45
  const API_KEY_ENV_NAMES = [PRIMARY_API_KEY_ENV, LEGACY_API_KEY_ENV];
@@ -254,6 +282,7 @@ const DOUYIN_OPTION_DISPLAY_NAMES = {
254
282
  jobId: "--job-id",
255
283
  };
256
284
  const KUAISHOU_DIRECT_ACTION_OPTIONS = {
285
+ "hot-search": ["pretty"],
257
286
  search: ["keyword", "pageToken", "pages", "all", "maxItems", "sinceDays", "pretty"],
258
287
  "user-search": ["keyword", "pageToken", "pages", "maxItems", "pretty"],
259
288
  detail: ["photoId", "url", "pretty"],
@@ -410,7 +439,9 @@ const WECHAT_DIRECT_ACTION_OPTIONS = {
410
439
  "pretty",
411
440
  ],
412
441
  transcript: ["url", "encryptedObjectId", "jobId", "pretty"],
442
+ "decrypt-media": ["mediaUrl", "output", "pretty"],
413
443
  };
444
+ const WECHAT_LOCAL_DIRECT_ACTIONS = new Set(["decrypt-media"]);
414
445
  const WECHAT_DIRECT_ACTION_NAMES = Object.keys(WECHAT_DIRECT_ACTION_OPTIONS).join(", ");
415
446
  const WECHAT_SEARCH_SORT_TYPES = [
416
447
  "all",
@@ -440,6 +471,8 @@ const WECHAT_OPTION_DISPLAY_NAMES = {
440
471
  commentId: "--comment-id",
441
472
  userId: "--user-id",
442
473
  jobId: "--job-id",
474
+ mediaUrl: "--media-url",
475
+ output: "--output",
443
476
  };
444
477
  const SENSITIVE_CHECK_DIRECT_ACTION_OPTIONS = {
445
478
  text: ["text", "platform", "pretty"],
@@ -633,6 +666,10 @@ const PLATFORMS = {
633
666
  "KUAISHOU_MCP_UPSTREAM_URL",
634
667
  ],
635
668
  tools: [
669
+ {
670
+ name: "kuaishou_get_hot_search_list",
671
+ description: "Fetch the current Kuaishou / 快手 hot-search list.",
672
+ },
636
673
  {
637
674
  name: "kuaishou_search_videos",
638
675
  description:
@@ -904,7 +941,7 @@ function isMainModule() {
904
941
  }
905
942
  }
906
943
 
907
- if (isMainModule()) {
944
+ async function main() {
908
945
  try {
909
946
  if (command === "install") {
910
947
  await installSkills(cliArgs.slice(1));
@@ -1092,9 +1129,14 @@ function validateWechatDirectActionOptions(action, options) {
1092
1129
  return;
1093
1130
  }
1094
1131
 
1095
- validateKnownOptions(options, allowedDirectOptions(allowedOptions));
1096
- validateDirectMetaOptions(options);
1097
- validateDirectPaginationOptions("wechat", action, options);
1132
+ if (WECHAT_LOCAL_DIRECT_ACTIONS.has(action)) {
1133
+ validateKnownOptions(options, allowedOptions);
1134
+ validateFlagOption(options, "pretty", "--pretty");
1135
+ } else {
1136
+ validateKnownOptions(options, allowedDirectOptions(allowedOptions));
1137
+ validateDirectMetaOptions(options);
1138
+ validateDirectPaginationOptions("wechat", action, options);
1139
+ }
1098
1140
  for (const key of allowedOptions) {
1099
1141
  if (!DIRECT_BOOLEAN_OPTIONS.has(key)) {
1100
1142
  requireOptionValue(options, key, WECHAT_OPTION_DISPLAY_NAMES[key]);
@@ -1786,6 +1828,9 @@ function printHelp() {
1786
1828
  console.log(` npx -y ${PACKAGE_SPEC} douyin transcript --aweme-id "<aweme_id>" --pretty`);
1787
1829
  console.log(" Submit or check a Douyin video speech-to-text transcript job.");
1788
1830
  console.log("");
1831
+ console.log(` npx -y ${PACKAGE_SPEC} kuaishou hot-search --pretty`);
1832
+ console.log(" Call the Kuaishou hot-search list tool directly and print JSON.");
1833
+ console.log("");
1789
1834
  console.log(` npx -y ${PACKAGE_SPEC} kuaishou search --keyword "露营" --pretty`);
1790
1835
  console.log(" Call the Kuaishou work search tool directly and print JSON.");
1791
1836
  console.log("");
@@ -1883,6 +1928,9 @@ function printHelp() {
1883
1928
  console.log(` npx -y ${PACKAGE_SPEC} wechat detail --url "<wechat_video_url_or_share_text>" --pretty`);
1884
1929
  console.log(" Call the WeChat Channels / 视频号 video detail tool from a video link or share text.");
1885
1930
  console.log("");
1931
+ console.log(` npx -y ${PACKAGE_SPEC} wechat decrypt-media --media-url "<video.video_url>" --output video.mp4`);
1932
+ console.log(" Save the WeChat Channels / 视频号 media URL returned by detail and locally decrypt when needed.");
1933
+ console.log("");
1886
1934
  console.log(` npx -y ${PACKAGE_SPEC} wechat article --url "<mp_article_url_or_share_text>" --pretty`);
1887
1935
  console.log(" Call the WeChat Official Account / 微信公众号 article detail tool from an article link or share text.");
1888
1936
  console.log("");
@@ -1980,6 +2028,10 @@ function printHelp() {
1980
2028
  console.log(" --url <url-or-share-text>");
1981
2029
  console.log(" Content link, short link, or share text for URL-based detail/comment/article commands.");
1982
2030
  console.log(" For Douyin detail/comments, pass a content page link, not video.play_url.");
2031
+ console.log(" --media-url <video.video_url>");
2032
+ console.log(" WeChat detail result media URL for local decrypt-media download.");
2033
+ console.log(" --output <file>");
2034
+ console.log(" Output file path for local decrypt-media download.");
1983
2035
  console.log(" --note-id <note_id>");
1984
2036
  console.log(" --aweme-id <aweme_id>");
1985
2037
  console.log(" --photo-id <photo_id>");
@@ -2215,6 +2267,13 @@ async function runWechatDirectCommand(args) {
2215
2267
  }
2216
2268
  validateWechatDirectActionOptions(action, options);
2217
2269
 
2270
+ if (action === "decrypt-media") {
2271
+ const data = await decryptWechatMediaCommand(options);
2272
+ process.stdout.write(JSON.stringify(data, null, options.pretty ? 2 : 0));
2273
+ process.stdout.write("\n");
2274
+ return;
2275
+ }
2276
+
2218
2277
  const operation = attachDirectMetadata(buildWechatOperation(action, options), options);
2219
2278
  const data = shouldUsePaginatedDirectOutput(options)
2220
2279
  ? await callPaginatedDirectOperation(operation, options)
@@ -2486,6 +2545,15 @@ function buildDouyinOperation(action, options) {
2486
2545
 
2487
2546
  function buildKuaishouOperation(action, options) {
2488
2547
  switch (action) {
2548
+ case "hot-search":
2549
+ return buildDirectOperation(
2550
+ "hot-search",
2551
+ {
2552
+ tool: "kuaishou_get_hot_search_list",
2553
+ toolArguments: {},
2554
+ },
2555
+ PLATFORMS.kuaishou
2556
+ );
2489
2557
  case "search":
2490
2558
  return buildDirectOperation(
2491
2559
  "search",
@@ -2819,6 +2887,524 @@ function buildWechatOperation(action, options) {
2819
2887
  }
2820
2888
  }
2821
2889
 
2890
+ export async function decryptWechatMediaCommand(
2891
+ options,
2892
+ {
2893
+ fetchMedia = fetch,
2894
+ downloadTimeoutMs = MEDIA_DOWNLOAD_TIMEOUT_MS,
2895
+ maxDownloadBytes = MEDIA_DOWNLOAD_MAX_BYTES,
2896
+ } = {}
2897
+ ) {
2898
+ if (!options.mediaUrl) {
2899
+ throw new Error("Missing --media-url for wechat decrypt-media.");
2900
+ }
2901
+ if (!options.output) {
2902
+ throw new Error("Missing --output for wechat decrypt-media.");
2903
+ }
2904
+
2905
+ const { mediaUrl, decodeKey } = parseWechatDecryptMediaUrl(options.mediaUrl);
2906
+ const outputPath = resolve(options.output);
2907
+ const tempPath = `${outputPath}.tmp-${process.pid}-${Date.now()}-${randomUUID()}`;
2908
+ await assertWechatMediaOutputPath(outputPath);
2909
+ await mkdir(dirname(outputPath), { recursive: true });
2910
+
2911
+ let encryptedLength;
2912
+ let decrypted = false;
2913
+ let bytesWritten = 0;
2914
+ try {
2915
+ const signal = createMediaDownloadAbortSignal(downloadTimeoutMs);
2916
+ const response = await fetchWechatMedia(mediaUrl, {
2917
+ fetchMedia,
2918
+ signal,
2919
+ });
2920
+ if (!response.ok) {
2921
+ throw new Error(`Media download failed with HTTP ${response.status}.`);
2922
+ }
2923
+ assertMediaDownloadWithinLimit(
2924
+ parseOptionalIntegerHeader(response.headers.get("content-length")),
2925
+ maxDownloadBytes
2926
+ );
2927
+ encryptedLength = parseOptionalWechatEncryptedLengthHeader(
2928
+ response.headers.get("x-enclen")
2929
+ );
2930
+ assertMediaDownloadWithinLimit(encryptedLength, maxDownloadBytes);
2931
+ bytesWritten = await writeResponseBodyToFile(response, tempPath, {
2932
+ maxBytes: maxDownloadBytes,
2933
+ });
2934
+ if (encryptedLength !== undefined) {
2935
+ await decryptWechatMediaFilePrefix(tempPath, {
2936
+ encryptedLength,
2937
+ decodeKey,
2938
+ });
2939
+ decrypted = true;
2940
+ }
2941
+ await rename(tempPath, outputPath);
2942
+ } catch (error) {
2943
+ await unlink(tempPath).catch(() => {});
2944
+ if (isAbortLikeError(error)) {
2945
+ throw new Error("Media download timed out.");
2946
+ }
2947
+ throw error;
2948
+ }
2949
+
2950
+ return {
2951
+ platform: "wechat",
2952
+ action: "decrypt-media",
2953
+ output_path: outputPath,
2954
+ decrypted,
2955
+ bytes_written: bytesWritten,
2956
+ };
2957
+ }
2958
+
2959
+ async function assertWechatMediaOutputPath(outputPath) {
2960
+ const existingOutput = await stat(outputPath).catch((error) => {
2961
+ if (error?.code === "ENOENT") {
2962
+ return undefined;
2963
+ }
2964
+ throw error;
2965
+ });
2966
+ if (existingOutput?.isDirectory()) {
2967
+ throw new Error("--output must be a file path for wechat decrypt-media.");
2968
+ }
2969
+ }
2970
+
2971
+ async function fetchWechatMedia(
2972
+ mediaUrl,
2973
+ {
2974
+ fetchMedia,
2975
+ signal,
2976
+ maxRedirects = MEDIA_DOWNLOAD_MAX_REDIRECTS,
2977
+ }
2978
+ ) {
2979
+ let currentUrl = mediaUrl;
2980
+ for (let redirectCount = 0; redirectCount <= maxRedirects; redirectCount += 1) {
2981
+ const response = await fetchMedia(currentUrl, {
2982
+ headers: {
2983
+ Referer: WECHAT_MEDIA_DOWNLOAD_REFERER,
2984
+ "User-Agent": MEDIA_DOWNLOAD_USER_AGENT,
2985
+ },
2986
+ redirect: "manual",
2987
+ ...(signal ? { signal } : {}),
2988
+ });
2989
+ if (!isHttpRedirectStatus(response.status)) {
2990
+ return response;
2991
+ }
2992
+ currentUrl = resolveWechatMediaRedirectUrl(
2993
+ currentUrl,
2994
+ response.headers?.get("location")
2995
+ );
2996
+ }
2997
+ throw new Error("Media download redirected too many times.");
2998
+ }
2999
+
3000
+ function isHttpRedirectStatus(status) {
3001
+ return [301, 302, 303, 307, 308].includes(status);
3002
+ }
3003
+
3004
+ function resolveWechatMediaRedirectUrl(currentUrl, location) {
3005
+ if (
3006
+ location === undefined ||
3007
+ location === null ||
3008
+ String(location).trim() === ""
3009
+ ) {
3010
+ throw new Error("Media download redirect is invalid.");
3011
+ }
3012
+ let parsed;
3013
+ try {
3014
+ parsed = new URL(String(location), currentUrl);
3015
+ } catch {
3016
+ throw new Error("Media download redirect is invalid.");
3017
+ }
3018
+ if (!isWechatDetailMediaDownloadUrl(parsed)) {
3019
+ throw new Error(WECHAT_DETAIL_MEDIA_URL_ERROR);
3020
+ }
3021
+ return parsed.href;
3022
+ }
3023
+
3024
+ function createMediaDownloadAbortSignal(timeoutMs) {
3025
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
3026
+ return undefined;
3027
+ }
3028
+ if (
3029
+ typeof AbortSignal !== "undefined" &&
3030
+ typeof AbortSignal.timeout === "function"
3031
+ ) {
3032
+ return AbortSignal.timeout(timeoutMs);
3033
+ }
3034
+ const controller = new AbortController();
3035
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
3036
+ timer.unref?.();
3037
+ return controller.signal;
3038
+ }
3039
+
3040
+ function parseWechatDecryptMediaUrl(mediaUrl) {
3041
+ let parsed;
3042
+ try {
3043
+ parsed = new URL(mediaUrl);
3044
+ } catch {
3045
+ throw new Error("Invalid --media-url.");
3046
+ }
3047
+ if (!["http:", "https:"].includes(parsed.protocol)) {
3048
+ throw new Error("Invalid --media-url.");
3049
+ }
3050
+ if (!isWechatDetailMediaDownloadUrl(parsed)) {
3051
+ throw new Error(WECHAT_DETAIL_MEDIA_URL_ERROR);
3052
+ }
3053
+
3054
+ const decodeKeyText = parsed.searchParams.get("k");
3055
+ if (!decodeKeyText) {
3056
+ throw new Error(WECHAT_DETAIL_MEDIA_URL_ERROR);
3057
+ }
3058
+ if (!/^\d+$/.test(decodeKeyText)) {
3059
+ throw new Error(WECHAT_DETAIL_MEDIA_URL_ERROR);
3060
+ }
3061
+ if (decodeKeyText.length > 20) {
3062
+ throw new Error(WECHAT_DETAIL_MEDIA_URL_ERROR);
3063
+ }
3064
+ const decodeKey = BigInt(decodeKeyText);
3065
+ if (decodeKey > WECHAT_MEDIA_DECODE_KEY_MAX) {
3066
+ throw new Error(WECHAT_DETAIL_MEDIA_URL_ERROR);
3067
+ }
3068
+ return {
3069
+ mediaUrl,
3070
+ decodeKey,
3071
+ };
3072
+ }
3073
+
3074
+ function isWechatDetailMediaDownloadUrl(parsed) {
3075
+ return (
3076
+ parsed.username === "" &&
3077
+ parsed.password === "" &&
3078
+ parsed.port === "" &&
3079
+ isAllowedWechatMediaDownloadHost(parsed.hostname)
3080
+ );
3081
+ }
3082
+
3083
+ function isAllowedWechatMediaDownloadHost(hostname) {
3084
+ return (
3085
+ WECHAT_MEDIA_DOWNLOAD_EXACT_HOSTS.has(hostname) ||
3086
+ WECHAT_MEDIA_DOWNLOAD_HOST_SUFFIXES.some((suffix) => hostname.endsWith(suffix))
3087
+ );
3088
+ }
3089
+
3090
+ function parseOptionalWechatEncryptedLengthHeader(value) {
3091
+ if (value === undefined || value === null || String(value).trim() === "") {
3092
+ return undefined;
3093
+ }
3094
+ if (!/^\d+$/.test(String(value).trim())) {
3095
+ throw new Error(WECHAT_MEDIA_RESPONSE_DECRYPT_ERROR);
3096
+ }
3097
+ const encryptedLength = Number.parseInt(String(value).trim(), 10);
3098
+ if (!Number.isSafeInteger(encryptedLength)) {
3099
+ throw new Error(WECHAT_MEDIA_RESPONSE_DECRYPT_ERROR);
3100
+ }
3101
+ return encryptedLength > 0 ? encryptedLength : undefined;
3102
+ }
3103
+
3104
+ function parseOptionalIntegerHeader(value) {
3105
+ if (value === null || String(value).trim() === "") {
3106
+ return undefined;
3107
+ }
3108
+ if (!/^\d+$/.test(String(value).trim())) {
3109
+ return undefined;
3110
+ }
3111
+ const parsed = Number.parseInt(String(value).trim(), 10);
3112
+ return Number.isSafeInteger(parsed) ? parsed : Number.POSITIVE_INFINITY;
3113
+ }
3114
+
3115
+ function assertMediaDownloadWithinLimit(bytes, maxBytes) {
3116
+ if (!Number.isFinite(maxBytes) || maxBytes <= 0 || bytes === undefined) {
3117
+ return;
3118
+ }
3119
+ if (bytes > maxBytes) {
3120
+ throw new Error("Media download is too large for local decrypt-media.");
3121
+ }
3122
+ }
3123
+
3124
+ async function writeResponseBodyToFile(response, filePath, { maxBytes } = {}) {
3125
+ if (!response.body) {
3126
+ throw new Error("Media download response has no body.");
3127
+ }
3128
+ const file = await openFile(filePath, "w");
3129
+ let bytesWritten = 0;
3130
+ try {
3131
+ for await (const chunk of response.body) {
3132
+ const buffer = Buffer.from(chunk);
3133
+ if (buffer.length === 0) {
3134
+ continue;
3135
+ }
3136
+ assertMediaDownloadWithinLimit(
3137
+ bytesWritten + buffer.length,
3138
+ maxBytes
3139
+ );
3140
+ await writeAllToFile(file, buffer);
3141
+ bytesWritten += buffer.length;
3142
+ }
3143
+ } finally {
3144
+ await file.close();
3145
+ }
3146
+ return bytesWritten;
3147
+ }
3148
+
3149
+ async function decryptWechatMediaFilePrefix(
3150
+ filePath,
3151
+ {
3152
+ encryptedLength,
3153
+ decodeKey,
3154
+ }
3155
+ ) {
3156
+ const file = await openFile(filePath, "r+");
3157
+ try {
3158
+ const stats = await file.stat();
3159
+ if (encryptedLength > stats.size) {
3160
+ throw new Error("Downloaded media is incomplete and cannot be decrypted.");
3161
+ }
3162
+ const decryptStream = createWechatMediaDecryptStream(decodeKey);
3163
+ let offset = 0;
3164
+ while (offset < encryptedLength) {
3165
+ const chunkLength = Math.min(
3166
+ MEDIA_DECRYPT_CHUNK_SIZE,
3167
+ encryptedLength - offset
3168
+ );
3169
+ const encryptedChunk = Buffer.allocUnsafe(chunkLength);
3170
+ const { bytesRead } = await file.read(
3171
+ encryptedChunk,
3172
+ 0,
3173
+ chunkLength,
3174
+ offset
3175
+ );
3176
+ if (bytesRead !== chunkLength) {
3177
+ throw new Error("Downloaded media is incomplete and cannot be decrypted.");
3178
+ }
3179
+ decryptWechatMediaBufferChunk(encryptedChunk, decryptStream);
3180
+ await writeAllToFile(file, encryptedChunk, offset);
3181
+ offset += chunkLength;
3182
+ }
3183
+ } finally {
3184
+ await file.close();
3185
+ }
3186
+ }
3187
+
3188
+ function isAbortLikeError(error) {
3189
+ return error?.name === "AbortError" || error?.name === "TimeoutError";
3190
+ }
3191
+
3192
+ async function writeAllToFile(file, buffer, position) {
3193
+ let offset = 0;
3194
+ while (offset < buffer.length) {
3195
+ const writePosition = position === undefined ? undefined : position + offset;
3196
+ const { bytesWritten } = await file.write(
3197
+ buffer,
3198
+ offset,
3199
+ buffer.length - offset,
3200
+ writePosition
3201
+ );
3202
+ if (bytesWritten <= 0) {
3203
+ throw new Error("Downloaded media could not be written.");
3204
+ }
3205
+ offset += bytesWritten;
3206
+ }
3207
+ }
3208
+
3209
+ function createWechatMediaDecryptStream(decodeKey) {
3210
+ return {
3211
+ context: new WechatFinderIsaac64Context(decodeKey),
3212
+ block: Buffer.allocUnsafe(8),
3213
+ index: 8,
3214
+ };
3215
+ }
3216
+
3217
+ function decryptWechatMediaBufferChunk(buffer, stream) {
3218
+ for (let index = 0; index < buffer.length; index += 1) {
3219
+ if (stream.index === 8) {
3220
+ stream.block.writeBigUInt64BE(stream.context.next());
3221
+ stream.index = 0;
3222
+ }
3223
+ buffer[index] ^= stream.block[stream.index];
3224
+ stream.index += 1;
3225
+ }
3226
+ }
3227
+
3228
+ class WechatFinderIsaac64Context {
3229
+ static MASK = (1n << 64n) - 1n;
3230
+ static RANDSIZ = 1 << 8;
3231
+
3232
+ constructor(decodeKey) {
3233
+ this.seed = Array(WechatFinderIsaac64Context.RANDSIZ).fill(0n);
3234
+ this.mm = Array(WechatFinderIsaac64Context.RANDSIZ).fill(0n);
3235
+ this.aa = 0n;
3236
+ this.bb = 0n;
3237
+ this.cc = 0n;
3238
+ this.randcnt = 0;
3239
+ this.seed[0] = this.u64(decodeKey);
3240
+ this.init(true);
3241
+ }
3242
+
3243
+ next() {
3244
+ if (this.randcnt === 0) {
3245
+ this.isaac64();
3246
+ this.randcnt = WechatFinderIsaac64Context.RANDSIZ;
3247
+ }
3248
+ this.randcnt -= 1;
3249
+ return this.seed[this.randcnt];
3250
+ }
3251
+
3252
+ init(seeded) {
3253
+ let a = 0x647c4677a2884b7cn;
3254
+ let b = 0xb9f8b322c73ac862n;
3255
+ let c = 0x8c0ea5053d4712a0n;
3256
+ let d = 0xb29b2e824a595524n;
3257
+ let e = 0x82f053db8355e0cen;
3258
+ let f = 0x48fe4a0fa5a09315n;
3259
+ let g = 0xae985bf2cbfc89edn;
3260
+ let h = 0x98f5704f6c44c0abn;
3261
+
3262
+ for (let index = 0; index < WechatFinderIsaac64Context.RANDSIZ; index += 8) {
3263
+ if (seeded) {
3264
+ a = this.u64(a + this.seed[index]);
3265
+ b = this.u64(b + this.seed[index + 1]);
3266
+ c = this.u64(c + this.seed[index + 2]);
3267
+ d = this.u64(d + this.seed[index + 3]);
3268
+ e = this.u64(e + this.seed[index + 4]);
3269
+ f = this.u64(f + this.seed[index + 5]);
3270
+ g = this.u64(g + this.seed[index + 6]);
3271
+ h = this.u64(h + this.seed[index + 7]);
3272
+ }
3273
+ [a, b, c, d, e, f, g, h] = this.mix64(a, b, c, d, e, f, g, h);
3274
+ this.mm.splice(index, 8, a, b, c, d, e, f, g, h);
3275
+ }
3276
+
3277
+ if (seeded) {
3278
+ for (let index = 0; index < WechatFinderIsaac64Context.RANDSIZ; index += 8) {
3279
+ a = this.u64(a + this.mm[index]);
3280
+ b = this.u64(b + this.mm[index + 1]);
3281
+ c = this.u64(c + this.mm[index + 2]);
3282
+ d = this.u64(d + this.mm[index + 3]);
3283
+ e = this.u64(e + this.mm[index + 4]);
3284
+ f = this.u64(f + this.mm[index + 5]);
3285
+ g = this.u64(g + this.mm[index + 6]);
3286
+ h = this.u64(h + this.mm[index + 7]);
3287
+ [a, b, c, d, e, f, g, h] = this.mix64(a, b, c, d, e, f, g, h);
3288
+ this.mm.splice(index, 8, a, b, c, d, e, f, g, h);
3289
+ }
3290
+ }
3291
+
3292
+ this.isaac64();
3293
+ this.randcnt = WechatFinderIsaac64Context.RANDSIZ;
3294
+ }
3295
+
3296
+ isaac64() {
3297
+ let a = this.aa;
3298
+ let b = this.u64(this.bb + this.cc + 1n);
3299
+ this.cc = this.u64(this.cc + 1n);
3300
+
3301
+ let resultIndex = 0;
3302
+ for (let index = 0; index < WechatFinderIsaac64Context.RANDSIZ / 2; index += 4) {
3303
+ [a, b] = this.rngstep64(
3304
+ this.u64(~(a ^ this.u64(a << 21n))),
3305
+ a,
3306
+ b,
3307
+ index,
3308
+ index + 128,
3309
+ resultIndex
3310
+ );
3311
+ resultIndex += 1;
3312
+ [a, b] = this.rngstep64(a ^ (a >> 5n), a, b, index + 1, index + 129, resultIndex);
3313
+ resultIndex += 1;
3314
+ [a, b] = this.rngstep64(
3315
+ a ^ this.u64(a << 12n),
3316
+ a,
3317
+ b,
3318
+ index + 2,
3319
+ index + 130,
3320
+ resultIndex
3321
+ );
3322
+ resultIndex += 1;
3323
+ [a, b] = this.rngstep64(a ^ (a >> 33n), a, b, index + 3, index + 131, resultIndex);
3324
+ resultIndex += 1;
3325
+ }
3326
+
3327
+ for (
3328
+ let index = WechatFinderIsaac64Context.RANDSIZ / 2;
3329
+ index < WechatFinderIsaac64Context.RANDSIZ;
3330
+ index += 4
3331
+ ) {
3332
+ const m2Index = index - WechatFinderIsaac64Context.RANDSIZ / 2;
3333
+ [a, b] = this.rngstep64(
3334
+ this.u64(~(a ^ this.u64(a << 21n))),
3335
+ a,
3336
+ b,
3337
+ index,
3338
+ m2Index,
3339
+ resultIndex
3340
+ );
3341
+ resultIndex += 1;
3342
+ [a, b] = this.rngstep64(a ^ (a >> 5n), a, b, index + 1, m2Index + 1, resultIndex);
3343
+ resultIndex += 1;
3344
+ [a, b] = this.rngstep64(
3345
+ a ^ this.u64(a << 12n),
3346
+ a,
3347
+ b,
3348
+ index + 2,
3349
+ m2Index + 2,
3350
+ resultIndex
3351
+ );
3352
+ resultIndex += 1;
3353
+ [a, b] = this.rngstep64(a ^ (a >> 33n), a, b, index + 3, m2Index + 3, resultIndex);
3354
+ resultIndex += 1;
3355
+ }
3356
+
3357
+ this.bb = b;
3358
+ this.aa = a;
3359
+ }
3360
+
3361
+ rngstep64(mix, a, b, mIndex, m2Index, resultIndex) {
3362
+ const x = this.mm[mIndex];
3363
+ const nextA = this.u64(mix + this.mm[m2Index]);
3364
+ const y = this.u64(this.ind64(x) + nextA + b);
3365
+ this.mm[mIndex] = y;
3366
+ const nextB = this.u64(this.ind64(y >> 8n) + x);
3367
+ this.seed[resultIndex] = nextB;
3368
+ return [nextA, nextB];
3369
+ }
3370
+
3371
+ ind64(value) {
3372
+ return this.mm[Number((value >> 3n) & BigInt(WechatFinderIsaac64Context.RANDSIZ - 1))];
3373
+ }
3374
+
3375
+ mix64(a, b, c, d, e, f, g, h) {
3376
+ a = this.u64(a - e);
3377
+ f ^= h >> 9n;
3378
+ h = this.u64(h + a);
3379
+ b = this.u64(b - f);
3380
+ g ^= this.u64(a << 9n);
3381
+ a = this.u64(a + b);
3382
+ c = this.u64(c - g);
3383
+ h ^= b >> 23n;
3384
+ b = this.u64(b + c);
3385
+ d = this.u64(d - h);
3386
+ a ^= this.u64(c << 15n);
3387
+ c = this.u64(c + d);
3388
+ e = this.u64(e - a);
3389
+ b ^= d >> 14n;
3390
+ d = this.u64(d + e);
3391
+ f = this.u64(f - b);
3392
+ c ^= this.u64(e << 20n);
3393
+ e = this.u64(e + f);
3394
+ g = this.u64(g - c);
3395
+ d ^= f >> 17n;
3396
+ f = this.u64(f + g);
3397
+ h = this.u64(h - d);
3398
+ e ^= this.u64(g << 14n);
3399
+ g = this.u64(g + h);
3400
+ return [a, b, c, d, e, f, g, h].map((value) => this.u64(value));
3401
+ }
3402
+
3403
+ u64(value) {
3404
+ return value & WechatFinderIsaac64Context.MASK;
3405
+ }
3406
+ }
3407
+
2822
3408
  function buildSensitiveCheckOperation(action, options) {
2823
3409
  switch (action) {
2824
3410
  case "text":
@@ -4014,3 +4600,7 @@ function extractTextContent(content) {
4014
4600
  .join("\n")
4015
4601
  .trim();
4016
4602
  }
4603
+
4604
+ if (isMainModule()) {
4605
+ await main();
4606
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "socialdatax-skills",
3
- "version": "0.2.21",
3
+ "version": "0.2.22",
4
4
  "description": "SocialDataX / 社媒数据助手 skill installer and direct CLI helper for hosted platform social media MCP services.",
5
5
  "license": "MIT",
6
6
  "type": "module",