openzca 0.1.44 → 0.1.46

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.
Files changed (3) hide show
  1. package/README.md +7 -0
  2. package/dist/cli.js +100 -3
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -126,6 +126,11 @@ For media debugging, grep these events in the debug log:
126
126
  | `openzca group info <groupId>` | Get group details |
127
127
  | `openzca group members <groupId>` | List members |
128
128
  | `openzca group create <name> <members...>` | Create a group |
129
+ | `openzca group poll create <groupId>` | Create a poll (`--question`, repeatable `--option`, optional poll flags) |
130
+ | `openzca group poll detail <pollId>` | Get poll details |
131
+ | `openzca group poll vote <pollId>` | Vote on a poll with repeatable `--option <id>` |
132
+ | `openzca group poll lock <pollId>` | Close a poll |
133
+ | `openzca group poll share <pollId>` | Share a poll |
129
134
  | `openzca group rename <groupId> <name>` | Rename group |
130
135
  | `openzca group avatar <groupId> <file>` | Change group avatar |
131
136
  | `openzca group settings <groupId>` | Update settings (`--lock-name`, `--sign-admin`, etc.) |
@@ -146,6 +151,8 @@ For media debugging, grep these events in the debug log:
146
151
  | `openzca group leave <groupId>` | Leave group |
147
152
  | `openzca group disperse <groupId>` | Disperse group |
148
153
 
154
+ Poll creation currently targets group threads only and maps to the existing `zca-js` group poll APIs. `group poll create` requires `--question` plus at least two `--option` values, and also supports `--multi`, `--allow-add-option`, `--hide-vote-preview`, `--anonymous`, and `--expire-ms`.
155
+
149
156
  ### friend — Friend management
150
157
 
151
158
  | Command | Description |
package/dist/cli.js CHANGED
@@ -578,6 +578,60 @@ async function assertFilesExist(files) {
578
578
  }
579
579
  }
580
580
 
581
+ // src/lib/group-poll.ts
582
+ function parsePositiveInteger(value, label) {
583
+ const normalized = value.trim();
584
+ if (!/^[1-9]\d*$/.test(normalized)) {
585
+ throw new Error(`${label} must be a positive integer.`);
586
+ }
587
+ const parsed = Number.parseInt(normalized, 10);
588
+ if (!Number.isSafeInteger(parsed) || parsed <= 0) {
589
+ throw new Error(`${label} must be a positive integer.`);
590
+ }
591
+ return parsed;
592
+ }
593
+ function requireQuestion(value) {
594
+ const normalized = value?.trim() ?? "";
595
+ if (!normalized) {
596
+ throw new Error("Poll question is required.");
597
+ }
598
+ return normalized;
599
+ }
600
+ function normalizeOptions(values) {
601
+ const normalized = (values ?? []).map((value) => value.trim());
602
+ if (normalized.length < 2) {
603
+ throw new Error("Poll must include at least two options.");
604
+ }
605
+ for (let index = 0; index < normalized.length; index += 1) {
606
+ if (!normalized[index]) {
607
+ throw new Error(`Poll option ${index + 1} cannot be empty.`);
608
+ }
609
+ }
610
+ return normalized;
611
+ }
612
+ function parsePollId(value) {
613
+ return parsePositiveInteger(value, "Poll id");
614
+ }
615
+ function parsePollOptionIds(values) {
616
+ const optionIds = values ?? [];
617
+ if (optionIds.length === 0) {
618
+ throw new Error("Provide at least one option id.");
619
+ }
620
+ return optionIds.map((value) => parsePositiveInteger(value, "Option id"));
621
+ }
622
+ function buildCreatePollOptions(options) {
623
+ const expireMs = options.expireMs?.trim();
624
+ return {
625
+ question: requireQuestion(options.question),
626
+ options: normalizeOptions(options.option),
627
+ expiredTime: expireMs ? parsePositiveInteger(expireMs, "expire-ms") : void 0,
628
+ allowMultiChoices: Boolean(options.multi),
629
+ allowAddNewOption: Boolean(options.allowAddOption),
630
+ hideVotePreview: Boolean(options.hideVotePreview),
631
+ isAnonymous: Boolean(options.anonymous)
632
+ };
633
+ }
634
+
581
635
  // src/lib/text-send.ts
582
636
  import { ThreadType } from "zca-js";
583
637
 
@@ -692,7 +746,7 @@ var TAG_STYLE_MAP = {
692
746
  orange: TextStyle.Orange,
693
747
  yellow: TextStyle.Yellow,
694
748
  green: TextStyle.Green,
695
- small: TextStyle.Small,
749
+ small: null,
696
750
  big: TextStyle.Big,
697
751
  underline: TextStyle.Underline
698
752
  };
@@ -740,8 +794,6 @@ function parseTextStyles(input) {
740
794
  lineStyles.push({ lineIndex, style: TextStyle.Bold });
741
795
  if (depth === 1) {
742
796
  lineStyles.push({ lineIndex, style: TextStyle.Big });
743
- } else if (depth === 3 || depth === 4) {
744
- lineStyles.push({ lineIndex, style: TextStyle.Small });
745
797
  }
746
798
  processedLines.push(headingMatch[2]);
747
799
  continue;
@@ -3869,6 +3921,51 @@ group.command("create <name> <members...>").description("Create new group").acti
3869
3921
  output(response, false);
3870
3922
  })
3871
3923
  );
3924
+ var groupPoll = group.command("poll").description("Group poll management");
3925
+ groupPoll.command("create <groupId>").requiredOption("-q, --question <text>", "Poll question").requiredOption("-o, --option <text>", "Poll option (repeatable)", collectValues, []).option("--multi", "Allow multiple choices").option("--allow-add-option", "Allow members to add new options").option("--hide-vote-preview", "Hide results until the member votes").option("--anonymous", "Hide voters").option("--expire-ms <ms>", "Poll expiration time in milliseconds").description("Create a poll in a group").action(
3926
+ wrapAction(
3927
+ async (groupId, opts, command) => {
3928
+ const pollOptions = buildCreatePollOptions(opts);
3929
+ const { api } = await requireApi(command);
3930
+ output(await api.createPoll(pollOptions, groupId), false);
3931
+ }
3932
+ )
3933
+ );
3934
+ groupPoll.command("detail <pollId>").description("Get poll detail").action(
3935
+ wrapAction(async (pollId, command) => {
3936
+ const normalizedPollId = parsePollId(pollId);
3937
+ const { api } = await requireApi(command);
3938
+ output(await api.getPollDetail(normalizedPollId), false);
3939
+ })
3940
+ );
3941
+ groupPoll.command("vote <pollId>").requiredOption("-o, --option <id>", "Poll option id (repeatable)", collectValues, []).description("Vote on a group poll").action(
3942
+ wrapAction(
3943
+ async (pollId, opts, command) => {
3944
+ const normalizedPollId = parsePollId(pollId);
3945
+ const optionIds = parsePollOptionIds(opts.option);
3946
+ const { api } = await requireApi(command);
3947
+ const response = await api.votePoll(
3948
+ normalizedPollId,
3949
+ optionIds.length === 1 ? optionIds[0] : optionIds
3950
+ );
3951
+ output(response, false);
3952
+ }
3953
+ )
3954
+ );
3955
+ groupPoll.command("lock <pollId>").description("Close a poll").action(
3956
+ wrapAction(async (pollId, command) => {
3957
+ const normalizedPollId = parsePollId(pollId);
3958
+ const { api } = await requireApi(command);
3959
+ output(await api.lockPoll(normalizedPollId), false);
3960
+ })
3961
+ );
3962
+ groupPoll.command("share <pollId>").description("Share a poll").action(
3963
+ wrapAction(async (pollId, command) => {
3964
+ const normalizedPollId = parsePollId(pollId);
3965
+ const { api } = await requireApi(command);
3966
+ output(await api.sharePoll(normalizedPollId), false);
3967
+ })
3968
+ );
3872
3969
  group.command("rename <groupId> <name>").description("Rename group").action(
3873
3970
  wrapAction(async (groupId, name, command) => {
3874
3971
  const { api } = await requireApi(command);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzca",
3
- "version": "0.1.44",
3
+ "version": "0.1.46",
4
4
  "description": "Open-source zca-compatible CLI to integrate Zalo with OpenClaw",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,6 +16,7 @@
16
16
  "scripts": {
17
17
  "build": "tsup src/cli.ts --format esm --target node18 --out-dir dist --clean",
18
18
  "dev": "tsx src/cli.ts",
19
+ "test": "tsx --test tests/*.test.ts",
19
20
  "typecheck": "tsc -p tsconfig.json",
20
21
  "lint": "tsc -p tsconfig.json --noEmit",
21
22
  "prepublishOnly": "npm run build"