@spectratools/xapi-cli 0.5.0 → 0.6.1
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/README.md +36 -9
- package/dist/cli.js +398 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,8 +27,8 @@ xapi-cli mcp add
|
|
|
27
27
|
# Read-only endpoints (search, profiles, trends, list reads)
|
|
28
28
|
export X_BEARER_TOKEN=your_app_bearer_token
|
|
29
29
|
|
|
30
|
-
# Write endpoints (
|
|
31
|
-
# OAuth 2.0 user context token with
|
|
30
|
+
# Write endpoints (post interactions, list mutations, user moderation, and send DM)
|
|
31
|
+
# OAuth 2.0 user context token with required write scopes
|
|
32
32
|
export X_ACCESS_TOKEN=your_oauth2_user_access_token
|
|
33
33
|
```
|
|
34
34
|
|
|
@@ -38,10 +38,10 @@ Auth behavior:
|
|
|
38
38
|
|
|
39
39
|
## Command Group Intent Summary
|
|
40
40
|
|
|
41
|
-
- `posts` — Read/search/create/delete posts and
|
|
42
|
-
- `users` — Profile lookup, social graph traversal, and
|
|
41
|
+
- `posts` — Read/search/create/delete posts, plus like/unlike, retweet, and bookmark workflows
|
|
42
|
+
- `users` — Profile lookup, social graph traversal, timelines, and follow/block/mute moderation actions
|
|
43
43
|
- `timeline` — Home timeline and mention stream monitoring
|
|
44
|
-
- `lists` — List
|
|
44
|
+
- `lists` — List lookup, creation/deletion, member management, and list feed reads
|
|
45
45
|
- `trends` — Trend place discovery and per-location trend fetch
|
|
46
46
|
- `dm` — Conversation listing and outbound direct messages
|
|
47
47
|
|
|
@@ -63,24 +63,51 @@ xapi-cli users followers jack --max-results 100 --format json
|
|
|
63
63
|
# Baseline file format: one follower ID per line
|
|
64
64
|
xapi-cli users followers jack --new-only --seen-ids-file ./seen-followers.txt --format json
|
|
65
65
|
|
|
66
|
-
# 3)
|
|
66
|
+
# 3) Engagement + moderation helper flow
|
|
67
67
|
xapi-cli posts get 1234567890 --format json
|
|
68
|
+
xapi-cli posts like 1234567890 --format json
|
|
69
|
+
xapi-cli posts unlike 1234567890 --format json
|
|
70
|
+
xapi-cli posts retweet 1234567890 --format json
|
|
71
|
+
xapi-cli posts bookmark 1234567890 --format json
|
|
72
|
+
xapi-cli posts unbookmark 1234567890 --format json
|
|
68
73
|
xapi-cli posts likes 1234567890 --max-results 100 --format json
|
|
69
74
|
xapi-cli posts retweets 1234567890 --max-results 100 --format json
|
|
75
|
+
xapi-cli users follow interesting_dev --format json
|
|
76
|
+
xapi-cli users unfollow inactive_account --format json
|
|
77
|
+
xapi-cli users block spammer123 --format json
|
|
78
|
+
xapi-cli users unblock spammer123 --format json
|
|
79
|
+
xapi-cli users mute noisyaccount --format json
|
|
80
|
+
xapi-cli users unmute noisyaccount --format json
|
|
70
81
|
|
|
71
82
|
# 4) Timeline monitor
|
|
72
83
|
xapi-cli timeline home --max-results 50 --format json
|
|
73
84
|
xapi-cli timeline mentions --max-results 50 --format json
|
|
74
85
|
|
|
86
|
+
# 4b) Timeline polling with --since-id (resume from last-seen post)
|
|
87
|
+
# Store the newest post ID from the previous fetch, then pass it on the next call
|
|
88
|
+
# to retrieve only new posts since that point.
|
|
89
|
+
xapi-cli timeline home --since-id 1900123456789012345 --max-results 50 --format json
|
|
90
|
+
xapi-cli timeline mentions --since-id 1900123456789012345 --max-results 50 --format json
|
|
91
|
+
|
|
75
92
|
# 5) DM assistant loop
|
|
76
93
|
xapi-cli dm conversations --max-results 20 --format json
|
|
77
94
|
xapi-cli dm send 12345 --text "hello from agent" --format json
|
|
95
|
+
|
|
96
|
+
# 6) List curation loop
|
|
97
|
+
xapi-cli lists create --name "Core devs" --description "Builders only" --private --format json
|
|
98
|
+
xapi-cli lists get 1234567890 --format json
|
|
99
|
+
xapi-cli lists add-member 1234567890 jack --format json
|
|
100
|
+
xapi-cli lists remove-member 1234567890 jack --format json
|
|
101
|
+
xapi-cli lists members 1234567890 --max-results 100 --format json
|
|
102
|
+
xapi-cli lists posts 1234567890 --max-results 25 --format json
|
|
103
|
+
xapi-cli lists delete 1234567890 --format json
|
|
78
104
|
```
|
|
79
105
|
|
|
80
106
|
## Notes
|
|
81
107
|
|
|
82
108
|
- All commands support JSON output with `--format json`.
|
|
83
|
-
- `
|
|
84
|
-
-
|
|
109
|
+
- `timeline home` and `timeline mentions` support `--since-id <post-id>` for incremental polling — only posts newer than the given ID are returned. Store the newest ID from each fetch and pass it on the next call to avoid re-processing.
|
|
110
|
+
- `users followers --new-only` performs **client-side diffing** against `--seen-ids-file`; it does not use an API-native `since_id` filter for follower deltas. The baseline file is a newline-delimited list of follower IDs, one per line.
|
|
111
|
+
- Baseline files are read-only input and are never mutated by the CLI. Your application is responsible for updating the baseline after processing new followers.
|
|
85
112
|
- `X_BEARER_TOKEN` is for read-only app auth.
|
|
86
|
-
- `X_ACCESS_TOKEN` is required for write actions (`posts create`, `
|
|
113
|
+
- `X_ACCESS_TOKEN` is required for write actions (`posts create|delete|like|unlike|bookmark|unbookmark|retweet`, `users follow|unfollow|block|unblock|mute|unmute`, `lists create|delete|add-member|remove-member`, `dm send`).
|
package/dist/cli.js
CHANGED
|
@@ -522,10 +522,28 @@ import { Cli as Cli2, z as z3 } from "incur";
|
|
|
522
522
|
var lists = Cli2.create("lists", {
|
|
523
523
|
description: "Manage and browse X lists."
|
|
524
524
|
});
|
|
525
|
+
var listIdSchema = z3.string().min(1).describe("List ID");
|
|
526
|
+
var memberSchema = z3.string().trim().min(1).describe("Member to target (username with or without @, or user ID)");
|
|
527
|
+
var createListOptionsSchema = z3.object({
|
|
528
|
+
name: z3.string().trim().min(1).max(25).describe("List name (1-25 characters)"),
|
|
529
|
+
description: z3.string().trim().min(1).max(100).optional().describe("Optional list description (1-100 characters)"),
|
|
530
|
+
private: z3.boolean().default(false).describe("Create as a private list")
|
|
531
|
+
});
|
|
532
|
+
async function resolveMemberTarget(client, usernameOrId) {
|
|
533
|
+
const normalized = usernameOrId.replace(/^@/, "");
|
|
534
|
+
if (/^\d+$/.test(normalized)) {
|
|
535
|
+
return { id: normalized };
|
|
536
|
+
}
|
|
537
|
+
const user = await client.getUserByUsername(normalized);
|
|
538
|
+
return {
|
|
539
|
+
id: user.data.id,
|
|
540
|
+
username: user.data.username
|
|
541
|
+
};
|
|
542
|
+
}
|
|
525
543
|
lists.command("get", {
|
|
526
544
|
description: "Get a list by ID.",
|
|
527
545
|
args: z3.object({
|
|
528
|
-
id:
|
|
546
|
+
id: listIdSchema
|
|
529
547
|
}),
|
|
530
548
|
env: xApiReadEnv,
|
|
531
549
|
output: z3.object({
|
|
@@ -568,10 +586,180 @@ lists.command("get", {
|
|
|
568
586
|
);
|
|
569
587
|
}
|
|
570
588
|
});
|
|
589
|
+
lists.command("create", {
|
|
590
|
+
description: "Create a new list.",
|
|
591
|
+
options: createListOptionsSchema,
|
|
592
|
+
env: xApiWriteEnv,
|
|
593
|
+
output: z3.object({
|
|
594
|
+
id: z3.string(),
|
|
595
|
+
name: z3.string()
|
|
596
|
+
}),
|
|
597
|
+
examples: [
|
|
598
|
+
{
|
|
599
|
+
options: { name: "Core devs", description: "Builders only", private: true },
|
|
600
|
+
description: "Create a private list"
|
|
601
|
+
}
|
|
602
|
+
],
|
|
603
|
+
async run(c) {
|
|
604
|
+
try {
|
|
605
|
+
const client = createXApiClient(writeAuthToken(c.env));
|
|
606
|
+
const res = await client.createList(c.options.name, c.options.description, c.options.private);
|
|
607
|
+
return c.ok(
|
|
608
|
+
res.data,
|
|
609
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
610
|
+
cta: {
|
|
611
|
+
description: "Next steps:",
|
|
612
|
+
commands: [
|
|
613
|
+
{
|
|
614
|
+
command: "lists get",
|
|
615
|
+
args: { id: res.data.id },
|
|
616
|
+
description: "View the list details"
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
command: "lists add-member",
|
|
620
|
+
args: { id: res.data.id, member: "username-or-id" },
|
|
621
|
+
description: "Add members to the new list"
|
|
622
|
+
}
|
|
623
|
+
]
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
);
|
|
627
|
+
} catch (error) {
|
|
628
|
+
const authError = toWriteAuthError("lists create", error);
|
|
629
|
+
if (authError) return c.error(authError);
|
|
630
|
+
throw error;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
lists.command("delete", {
|
|
635
|
+
description: "Delete a list by ID.",
|
|
636
|
+
args: z3.object({
|
|
637
|
+
id: listIdSchema.describe("List ID to delete")
|
|
638
|
+
}),
|
|
639
|
+
env: xApiWriteEnv,
|
|
640
|
+
output: z3.object({
|
|
641
|
+
deleted: z3.boolean(),
|
|
642
|
+
id: z3.string()
|
|
643
|
+
}),
|
|
644
|
+
examples: [{ args: { id: "1234567890" }, description: "Delete a list" }],
|
|
645
|
+
async run(c) {
|
|
646
|
+
try {
|
|
647
|
+
const client = createXApiClient(writeAuthToken(c.env));
|
|
648
|
+
const res = await client.deleteList(c.args.id);
|
|
649
|
+
return c.ok({ deleted: res.data.deleted, id: c.args.id });
|
|
650
|
+
} catch (error) {
|
|
651
|
+
const authError = toWriteAuthError("lists delete", error);
|
|
652
|
+
if (authError) return c.error(authError);
|
|
653
|
+
throw error;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
});
|
|
657
|
+
lists.command("add-member", {
|
|
658
|
+
description: "Add a member to an X list.",
|
|
659
|
+
args: z3.object({
|
|
660
|
+
id: listIdSchema,
|
|
661
|
+
member: memberSchema
|
|
662
|
+
}),
|
|
663
|
+
env: xApiWriteEnv,
|
|
664
|
+
output: z3.object({
|
|
665
|
+
id: z3.string(),
|
|
666
|
+
member_id: z3.string(),
|
|
667
|
+
member_username: z3.string().optional(),
|
|
668
|
+
is_member: z3.boolean()
|
|
669
|
+
}),
|
|
670
|
+
examples: [
|
|
671
|
+
{
|
|
672
|
+
args: { id: "1234567890", member: "jack" },
|
|
673
|
+
description: "Add @jack to a list"
|
|
674
|
+
}
|
|
675
|
+
],
|
|
676
|
+
async run(c) {
|
|
677
|
+
try {
|
|
678
|
+
const client = createXApiClient(writeAuthToken(c.env));
|
|
679
|
+
const member = await resolveMemberTarget(client, c.args.member);
|
|
680
|
+
const res = await client.addListMember(c.args.id, member.id);
|
|
681
|
+
return c.ok(
|
|
682
|
+
{
|
|
683
|
+
id: c.args.id,
|
|
684
|
+
member_id: member.id,
|
|
685
|
+
member_username: member.username,
|
|
686
|
+
is_member: res.data.is_member
|
|
687
|
+
},
|
|
688
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
689
|
+
cta: {
|
|
690
|
+
description: "Verify list membership:",
|
|
691
|
+
commands: [
|
|
692
|
+
{
|
|
693
|
+
command: "lists members",
|
|
694
|
+
args: { id: c.args.id },
|
|
695
|
+
description: "View current list members"
|
|
696
|
+
}
|
|
697
|
+
]
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
);
|
|
701
|
+
} catch (error) {
|
|
702
|
+
const authError = toWriteAuthError("lists add-member", error);
|
|
703
|
+
if (authError) return c.error(authError);
|
|
704
|
+
throw error;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
lists.command("remove-member", {
|
|
709
|
+
description: "Remove a member from an X list.",
|
|
710
|
+
args: z3.object({
|
|
711
|
+
id: listIdSchema,
|
|
712
|
+
member: memberSchema
|
|
713
|
+
}),
|
|
714
|
+
env: xApiWriteEnv,
|
|
715
|
+
output: z3.object({
|
|
716
|
+
id: z3.string(),
|
|
717
|
+
member_id: z3.string(),
|
|
718
|
+
member_username: z3.string().optional(),
|
|
719
|
+
is_member: z3.boolean()
|
|
720
|
+
}),
|
|
721
|
+
examples: [
|
|
722
|
+
{
|
|
723
|
+
args: { id: "1234567890", member: "jack" },
|
|
724
|
+
description: "Remove @jack from a list"
|
|
725
|
+
}
|
|
726
|
+
],
|
|
727
|
+
async run(c) {
|
|
728
|
+
try {
|
|
729
|
+
const client = createXApiClient(writeAuthToken(c.env));
|
|
730
|
+
const member = await resolveMemberTarget(client, c.args.member);
|
|
731
|
+
const res = await client.removeListMember(c.args.id, member.id);
|
|
732
|
+
return c.ok(
|
|
733
|
+
{
|
|
734
|
+
id: c.args.id,
|
|
735
|
+
member_id: member.id,
|
|
736
|
+
member_username: member.username,
|
|
737
|
+
is_member: res.data.is_member
|
|
738
|
+
},
|
|
739
|
+
c.format === "json" || c.format === "jsonl" ? void 0 : {
|
|
740
|
+
cta: {
|
|
741
|
+
description: "Next steps:",
|
|
742
|
+
commands: [
|
|
743
|
+
{
|
|
744
|
+
command: "lists members",
|
|
745
|
+
args: { id: c.args.id },
|
|
746
|
+
description: "Confirm updated membership"
|
|
747
|
+
}
|
|
748
|
+
]
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
);
|
|
752
|
+
} catch (error) {
|
|
753
|
+
const authError = toWriteAuthError("lists remove-member", error);
|
|
754
|
+
if (authError) return c.error(authError);
|
|
755
|
+
throw error;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
});
|
|
571
759
|
lists.command("members", {
|
|
572
760
|
description: "List members of an X list.",
|
|
573
761
|
args: z3.object({
|
|
574
|
-
id:
|
|
762
|
+
id: listIdSchema
|
|
575
763
|
}),
|
|
576
764
|
options: z3.object({
|
|
577
765
|
maxResults: z3.number().default(100).describe("Maximum members to return")
|
|
@@ -608,7 +796,7 @@ lists.command("members", {
|
|
|
608
796
|
lists.command("posts", {
|
|
609
797
|
description: "Get posts from an X list.",
|
|
610
798
|
args: z3.object({
|
|
611
|
-
id:
|
|
799
|
+
id: listIdSchema
|
|
612
800
|
}),
|
|
613
801
|
options: z3.object({
|
|
614
802
|
maxResults: z3.number().default(25).describe("Maximum posts to return"),
|
|
@@ -869,6 +1057,78 @@ posts.command("like", {
|
|
|
869
1057
|
}
|
|
870
1058
|
}
|
|
871
1059
|
});
|
|
1060
|
+
posts.command("unlike", {
|
|
1061
|
+
description: "Unlike a post by ID.",
|
|
1062
|
+
args: z4.object({
|
|
1063
|
+
id: z4.string().describe("Post ID to unlike")
|
|
1064
|
+
}),
|
|
1065
|
+
env: xApiWriteEnv,
|
|
1066
|
+
output: z4.object({
|
|
1067
|
+
liked: z4.boolean(),
|
|
1068
|
+
id: z4.string()
|
|
1069
|
+
}),
|
|
1070
|
+
examples: [{ args: { id: "1234567890" }, description: "Unlike a post" }],
|
|
1071
|
+
async run(c) {
|
|
1072
|
+
try {
|
|
1073
|
+
const client = createXApiClient(writeAuthToken(c.env));
|
|
1074
|
+
const me = await client.getMe();
|
|
1075
|
+
const res = await client.unlikePost(me.data.id, c.args.id);
|
|
1076
|
+
return c.ok({ liked: res.data.liked, id: c.args.id });
|
|
1077
|
+
} catch (error) {
|
|
1078
|
+
const authError = toWriteAuthError("posts unlike", error);
|
|
1079
|
+
if (authError) return c.error(authError);
|
|
1080
|
+
throw error;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
1084
|
+
posts.command("bookmark", {
|
|
1085
|
+
description: "Bookmark a post by ID.",
|
|
1086
|
+
args: z4.object({
|
|
1087
|
+
id: z4.string().describe("Post ID to bookmark")
|
|
1088
|
+
}),
|
|
1089
|
+
env: xApiWriteEnv,
|
|
1090
|
+
output: z4.object({
|
|
1091
|
+
bookmarked: z4.boolean(),
|
|
1092
|
+
id: z4.string()
|
|
1093
|
+
}),
|
|
1094
|
+
examples: [{ args: { id: "1234567890" }, description: "Bookmark a post" }],
|
|
1095
|
+
async run(c) {
|
|
1096
|
+
try {
|
|
1097
|
+
const client = createXApiClient(writeAuthToken(c.env));
|
|
1098
|
+
const me = await client.getMe();
|
|
1099
|
+
const res = await client.bookmarkPost(me.data.id, c.args.id);
|
|
1100
|
+
return c.ok({ bookmarked: res.data.bookmarked, id: c.args.id });
|
|
1101
|
+
} catch (error) {
|
|
1102
|
+
const authError = toWriteAuthError("posts bookmark", error);
|
|
1103
|
+
if (authError) return c.error(authError);
|
|
1104
|
+
throw error;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
});
|
|
1108
|
+
posts.command("unbookmark", {
|
|
1109
|
+
description: "Remove bookmark from a post by ID.",
|
|
1110
|
+
args: z4.object({
|
|
1111
|
+
id: z4.string().describe("Post ID to unbookmark")
|
|
1112
|
+
}),
|
|
1113
|
+
env: xApiWriteEnv,
|
|
1114
|
+
output: z4.object({
|
|
1115
|
+
bookmarked: z4.boolean(),
|
|
1116
|
+
id: z4.string()
|
|
1117
|
+
}),
|
|
1118
|
+
examples: [{ args: { id: "1234567890" }, description: "Remove a bookmark from a post" }],
|
|
1119
|
+
async run(c) {
|
|
1120
|
+
try {
|
|
1121
|
+
const client = createXApiClient(writeAuthToken(c.env));
|
|
1122
|
+
const me = await client.getMe();
|
|
1123
|
+
const res = await client.unbookmarkPost(me.data.id, c.args.id);
|
|
1124
|
+
return c.ok({ bookmarked: res.data.bookmarked, id: c.args.id });
|
|
1125
|
+
} catch (error) {
|
|
1126
|
+
const authError = toWriteAuthError("posts unbookmark", error);
|
|
1127
|
+
if (authError) return c.error(authError);
|
|
1128
|
+
throw error;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
});
|
|
872
1132
|
posts.command("retweet", {
|
|
873
1133
|
description: "Retweet a post by ID.",
|
|
874
1134
|
args: z4.object({
|
|
@@ -1376,6 +1636,130 @@ users.command("unfollow", {
|
|
|
1376
1636
|
}
|
|
1377
1637
|
}
|
|
1378
1638
|
});
|
|
1639
|
+
users.command("block", {
|
|
1640
|
+
description: "Block a user by username or ID.",
|
|
1641
|
+
args: z7.object({
|
|
1642
|
+
username: z7.string().describe("Username (with or without @) or user ID")
|
|
1643
|
+
}),
|
|
1644
|
+
env: xApiWriteEnv,
|
|
1645
|
+
output: z7.object({
|
|
1646
|
+
id: z7.string(),
|
|
1647
|
+
username: z7.string(),
|
|
1648
|
+
blocking: z7.boolean()
|
|
1649
|
+
}),
|
|
1650
|
+
examples: [{ args: { username: "jack" }, description: "Block @jack" }],
|
|
1651
|
+
async run(c) {
|
|
1652
|
+
try {
|
|
1653
|
+
const client = createXApiClient(writeAuthToken(c.env));
|
|
1654
|
+
const me = await client.getMe();
|
|
1655
|
+
const targetRes = await resolveUser(client, c.args.username);
|
|
1656
|
+
const target = targetRes.data;
|
|
1657
|
+
const res = await client.blockUser(me.data.id, target.id);
|
|
1658
|
+
return c.ok({
|
|
1659
|
+
id: target.id,
|
|
1660
|
+
username: target.username,
|
|
1661
|
+
blocking: res.data.blocking
|
|
1662
|
+
});
|
|
1663
|
+
} catch (error) {
|
|
1664
|
+
const authError = toWriteAuthError("users block", error);
|
|
1665
|
+
if (authError) return c.error(authError);
|
|
1666
|
+
throw error;
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
});
|
|
1670
|
+
users.command("unblock", {
|
|
1671
|
+
description: "Unblock a user by username or ID.",
|
|
1672
|
+
args: z7.object({
|
|
1673
|
+
username: z7.string().describe("Username (with or without @) or user ID")
|
|
1674
|
+
}),
|
|
1675
|
+
env: xApiWriteEnv,
|
|
1676
|
+
output: z7.object({
|
|
1677
|
+
id: z7.string(),
|
|
1678
|
+
username: z7.string(),
|
|
1679
|
+
blocking: z7.boolean()
|
|
1680
|
+
}),
|
|
1681
|
+
examples: [{ args: { username: "jack" }, description: "Unblock @jack" }],
|
|
1682
|
+
async run(c) {
|
|
1683
|
+
try {
|
|
1684
|
+
const client = createXApiClient(writeAuthToken(c.env));
|
|
1685
|
+
const me = await client.getMe();
|
|
1686
|
+
const targetRes = await resolveUser(client, c.args.username);
|
|
1687
|
+
const target = targetRes.data;
|
|
1688
|
+
const res = await client.unblockUser(me.data.id, target.id);
|
|
1689
|
+
return c.ok({
|
|
1690
|
+
id: target.id,
|
|
1691
|
+
username: target.username,
|
|
1692
|
+
blocking: res.data.blocking
|
|
1693
|
+
});
|
|
1694
|
+
} catch (error) {
|
|
1695
|
+
const authError = toWriteAuthError("users unblock", error);
|
|
1696
|
+
if (authError) return c.error(authError);
|
|
1697
|
+
throw error;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
});
|
|
1701
|
+
users.command("mute", {
|
|
1702
|
+
description: "Mute a user by username or ID.",
|
|
1703
|
+
args: z7.object({
|
|
1704
|
+
username: z7.string().describe("Username (with or without @) or user ID")
|
|
1705
|
+
}),
|
|
1706
|
+
env: xApiWriteEnv,
|
|
1707
|
+
output: z7.object({
|
|
1708
|
+
id: z7.string(),
|
|
1709
|
+
username: z7.string(),
|
|
1710
|
+
muting: z7.boolean()
|
|
1711
|
+
}),
|
|
1712
|
+
examples: [{ args: { username: "jack" }, description: "Mute @jack" }],
|
|
1713
|
+
async run(c) {
|
|
1714
|
+
try {
|
|
1715
|
+
const client = createXApiClient(writeAuthToken(c.env));
|
|
1716
|
+
const me = await client.getMe();
|
|
1717
|
+
const targetRes = await resolveUser(client, c.args.username);
|
|
1718
|
+
const target = targetRes.data;
|
|
1719
|
+
const res = await client.muteUser(me.data.id, target.id);
|
|
1720
|
+
return c.ok({
|
|
1721
|
+
id: target.id,
|
|
1722
|
+
username: target.username,
|
|
1723
|
+
muting: res.data.muting
|
|
1724
|
+
});
|
|
1725
|
+
} catch (error) {
|
|
1726
|
+
const authError = toWriteAuthError("users mute", error);
|
|
1727
|
+
if (authError) return c.error(authError);
|
|
1728
|
+
throw error;
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
});
|
|
1732
|
+
users.command("unmute", {
|
|
1733
|
+
description: "Unmute a user by username or ID.",
|
|
1734
|
+
args: z7.object({
|
|
1735
|
+
username: z7.string().describe("Username (with or without @) or user ID")
|
|
1736
|
+
}),
|
|
1737
|
+
env: xApiWriteEnv,
|
|
1738
|
+
output: z7.object({
|
|
1739
|
+
id: z7.string(),
|
|
1740
|
+
username: z7.string(),
|
|
1741
|
+
muting: z7.boolean()
|
|
1742
|
+
}),
|
|
1743
|
+
examples: [{ args: { username: "jack" }, description: "Unmute @jack" }],
|
|
1744
|
+
async run(c) {
|
|
1745
|
+
try {
|
|
1746
|
+
const client = createXApiClient(writeAuthToken(c.env));
|
|
1747
|
+
const me = await client.getMe();
|
|
1748
|
+
const targetRes = await resolveUser(client, c.args.username);
|
|
1749
|
+
const target = targetRes.data;
|
|
1750
|
+
const res = await client.unmuteUser(me.data.id, target.id);
|
|
1751
|
+
return c.ok({
|
|
1752
|
+
id: target.id,
|
|
1753
|
+
username: target.username,
|
|
1754
|
+
muting: res.data.muting
|
|
1755
|
+
});
|
|
1756
|
+
} catch (error) {
|
|
1757
|
+
const authError = toWriteAuthError("users unmute", error);
|
|
1758
|
+
if (authError) return c.error(authError);
|
|
1759
|
+
throw error;
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
});
|
|
1379
1763
|
users.command("followers", {
|
|
1380
1764
|
description: "List followers of a user. Supports optional client-side baseline diffing for new follower detection.",
|
|
1381
1765
|
args: z7.object({
|
|
@@ -1621,9 +2005,20 @@ var WRITE_OPERATIONS = /* @__PURE__ */ new Set([
|
|
|
1621
2005
|
"posts create",
|
|
1622
2006
|
"posts delete",
|
|
1623
2007
|
"posts like",
|
|
2008
|
+
"posts unlike",
|
|
2009
|
+
"posts bookmark",
|
|
2010
|
+
"posts unbookmark",
|
|
1624
2011
|
"posts retweet",
|
|
1625
2012
|
"users follow",
|
|
1626
2013
|
"users unfollow",
|
|
2014
|
+
"users block",
|
|
2015
|
+
"users unblock",
|
|
2016
|
+
"users mute",
|
|
2017
|
+
"users unmute",
|
|
2018
|
+
"lists create",
|
|
2019
|
+
"lists delete",
|
|
2020
|
+
"lists add-member",
|
|
2021
|
+
"lists remove-member",
|
|
1627
2022
|
"dm send"
|
|
1628
2023
|
]);
|
|
1629
2024
|
cli.use(async ({ command, error }, next) => {
|