Skip to content

Commit ba8e83b

Browse files
committed
feat(twitter): allow leading numbers and wildcard in target usernames
Fix for #1556 - Added support for Twitter usernames starting with numbers - Added support for wildcard '*' in TWITTER_TARGET_USERS - Fixed test discovery in test.sh to find all .test.ts files - Updated Jest config for better ESM support The main change allows Twitter target usernames to start with numbers and use wildcards, matching current Twitter/X username rules. Test infrastructure was improved to properly handle ESM modules and find all test files. Changes: - Updated username validation regex in Twitter client - Added test cases for numeric usernames and wildcards - Switched test.sh to use find for reliable test discovery - Simplified Jest config to focus on ESM support
1 parent cddc9ee commit ba8e83b

File tree

7 files changed

+192
-31
lines changed

7 files changed

+192
-31
lines changed

jest.config.json

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"testEnvironment": "node",
3+
"extensionsToTreatAsEsm": [".ts"],
4+
"transform": {
5+
"^.+\\.tsx?$": [
6+
"ts-jest",
7+
{
8+
"useESM": true,
9+
"tsconfig": {
10+
"module": "esnext",
11+
"target": "esnext",
12+
"moduleResolution": "bundler"
13+
}
14+
}
15+
]
16+
},
17+
"moduleNameMapper": {
18+
"^@elizaos/core$": "<rootDir>/packages/core/src/index.ts",
19+
"^@elizaos/(.*)$": "<rootDir>/packages/$1/src/index.ts"
20+
}
21+
}

package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@
4343
"typescript": "5.6.3",
4444
"vite": "5.4.11",
4545
"vitest": "2.1.5",
46-
"viem": "2.21.58"
46+
"viem": "2.21.58",
47+
"ts-jest": "^29.1.1",
48+
"@types/jest": "^29.5.11",
49+
"jest": "^29.7.0"
4750
},
4851
"pnpm": {
4952
"overrides": {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { twitterEnvSchema } from "../environment";
2+
3+
describe("Twitter Environment Configuration", () => {
4+
describe("Username Validation", () => {
5+
const validateUsername = (username: string) => {
6+
return twitterEnvSchema.parse({
7+
TWITTER_DRY_RUN: false,
8+
TWITTER_USERNAME: "test_user",
9+
TWITTER_PASSWORD: "password",
10+
TWITTER_EMAIL: "test@example.com",
11+
TWITTER_2FA_SECRET: "",
12+
TWITTER_RETRY_LIMIT: 5,
13+
TWITTER_POLL_INTERVAL: 120,
14+
POST_INTERVAL_MIN: 90,
15+
POST_INTERVAL_MAX: 180,
16+
ENABLE_ACTION_PROCESSING: false,
17+
ACTION_INTERVAL: 5,
18+
POST_IMMEDIATELY: false,
19+
TWITTER_TARGET_USERS: [username],
20+
});
21+
};
22+
23+
it("should allow valid traditional usernames", () => {
24+
expect(() => validateUsername("normal_user")).not.toThrow();
25+
expect(() => validateUsername("user123")).not.toThrow();
26+
expect(() => validateUsername("a_1_b_2")).not.toThrow();
27+
});
28+
29+
it("should allow usernames starting with digits", () => {
30+
expect(() => validateUsername("123user")).not.toThrow();
31+
expect(() => validateUsername("42_test")).not.toThrow();
32+
expect(() => validateUsername("007james")).not.toThrow();
33+
});
34+
35+
it("should allow wildcard", () => {
36+
expect(() => validateUsername("*")).not.toThrow();
37+
});
38+
39+
it("should reject invalid usernames", () => {
40+
expect(() => validateUsername("")).toThrow();
41+
expect(() => validateUsername("user@123")).toThrow();
42+
expect(() => validateUsername("user-123")).toThrow();
43+
expect(() => validateUsername("user.123")).toThrow();
44+
expect(() => validateUsername("a".repeat(16))).toThrow();
45+
});
46+
47+
it("should handle array of usernames", () => {
48+
const config = {
49+
TWITTER_DRY_RUN: false,
50+
TWITTER_USERNAME: "test_user",
51+
TWITTER_PASSWORD: "password",
52+
TWITTER_EMAIL: "test@example.com",
53+
TWITTER_2FA_SECRET: "",
54+
TWITTER_RETRY_LIMIT: 5,
55+
TWITTER_POLL_INTERVAL: 120,
56+
POST_INTERVAL_MIN: 90,
57+
POST_INTERVAL_MAX: 180,
58+
ENABLE_ACTION_PROCESSING: false,
59+
ACTION_INTERVAL: 5,
60+
POST_IMMEDIATELY: false,
61+
TWITTER_TARGET_USERS: ["normal_user", "123digit", "*"],
62+
};
63+
64+
expect(() => twitterEnvSchema.parse(config)).not.toThrow();
65+
});
66+
});
67+
});

packages/client-twitter/src/environment.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,18 @@ export const DEFAULT_MAX_TWEET_LENGTH = 280;
55

66
const twitterUsernameSchema = z
77
.string()
8-
.min(1, "An X/Twitter Username must be at least 1 characters long")
8+
.min(1, "An X/Twitter Username must be at least 1 character long")
99
.max(15, "An X/Twitter Username cannot exceed 15 characters")
10-
.regex(
11-
/^[A-Za-z0-9_]*$/,
12-
"An X Username can only contain letters, numbers, and underscores"
13-
);
10+
.refine((username) => {
11+
// Allow wildcard '*' as a special case
12+
if (username === "*") return true;
13+
14+
// Twitter usernames can:
15+
// - Start with digits now
16+
// - Contain letters, numbers, underscores
17+
// - Must not be empty
18+
return /^[A-Za-z0-9_]+$/.test(username);
19+
}, "An X Username can only contain letters, numbers, and underscores");
1420

1521
/**
1622
* This schema defines all required/optional environment settings,

pnpm-lock.yaml

+41-13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/test.sh

+35-5
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,57 @@
44
REQUIRED_NODE_VERSION=22
55
CURRENT_NODE_VERSION=$(node -v | cut -d'.' -f1 | sed 's/v//')
66

7-
if (( CURRENT_NODE_VERSION < REQUIRED_NODE_VERSION )); then
7+
if ((CURRENT_NODE_VERSION < REQUIRED_NODE_VERSION)); then
88
echo "Error: Node.js version must be $REQUIRED_NODE_VERSION or higher. Current version is $CURRENT_NODE_VERSION."
99
exit 1
1010
fi
1111

1212
# Navigate to the script's directory
1313
cd "$(dirname "$0")"/..
1414

15+
# If specific test file provided, run just that
16+
if [[ "$1" == *".ts" ]]; then
17+
echo -e "\033[1mRunning specific test: $1\033[0m"
18+
node --experimental-vm-modules $(which jest) "$1"
19+
exit $?
20+
fi
21+
22+
# If package name provided, run just that package
23+
if [ ! -z "$1" ]; then
24+
package="$1"
25+
package_path="packages/$package"
26+
27+
if [ ! -d "$package_path" ]; then
28+
echo -e "\033[1mPackage directory '$package' not found\033[0m"
29+
exit 1
30+
fi
31+
32+
echo -e "\033[1mTesting package: $package\033[0m"
33+
# Use find to get all test files and pass them explicitly to jest
34+
test_files=$(find "packages/$package/src" -name "*.test.ts" -type f)
35+
if [ -z "$test_files" ]; then
36+
echo "No test files found"
37+
exit 1
38+
fi
39+
echo "Found test files:"
40+
echo "$test_files"
41+
node --experimental-vm-modules $(which jest) $test_files
42+
exit $?
43+
fi
44+
1545
# Check if the packages directory exists
1646
if [ ! -d "packages" ]; then
1747
echo "Error: 'packages' directory not found."
1848
exit 1
1949
fi
2050

2151
# Find all packages under the packages directory
22-
PACKAGES=( $(find packages -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) )
52+
PACKAGES=($(find packages -mindepth 1 -maxdepth 1 -type d -exec basename {} \;))
2353

2454
# Test packages in specified order
2555
for package in "${PACKAGES[@]}"; do
2656
package_path="packages/$package"
27-
57+
2858
if [ ! -d "$package_path" ]; then
2959
echo -e "\033[1mPackage directory '$package' not found, skipping...\033[0m"
3060
continue
@@ -57,7 +87,7 @@ for package in "${PACKAGES[@]}"; do
5787
echo "No package.json found in $package, skipping..."
5888
fi
5989

60-
cd - > /dev/null || exit
90+
cd - >/dev/null || exit
6191
done
6292

63-
echo -e "\033[1mTest process completed.😎\033[0m"
93+
echo -e "\033[1mTest process completed.😎\033[0m"

tsconfig.json

+13-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
{
22
"compilerOptions": {
3-
"target": "ES2020",
4-
"module": "commonjs",
5-
"moduleResolution": "node",
3+
"target": "ESNext",
4+
"module": "ESNext",
5+
"moduleResolution": "Bundler",
66
"esModuleInterop": true,
77
"skipLibCheck": true,
8-
"forceConsistentCasingInFileNames": true
8+
"forceConsistentCasingInFileNames": true,
9+
"allowImportingTsExtensions": true,
10+
"noEmit": true
911
},
1012
"files": [],
1113
"references": [
12-
{ "path": "packages/core" },
13-
{ "path": "packages/client-slack" }
14+
{
15+
"path": "packages/core"
16+
},
17+
{
18+
"path": "packages/client-slack"
19+
}
1420
]
15-
}
21+
}

0 commit comments

Comments
 (0)