Skip to content

Commit

Permalink
feat(twitter): allow leading numbers and wildcard in target usernames
Browse files Browse the repository at this point in the history
Fix for elizaOS#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
  • Loading branch information
augchan42 committed Jan 4, 2025
1 parent cddc9ee commit ba8e83b
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 31 deletions.
21 changes: 21 additions & 0 deletions jest.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"testEnvironment": "node",
"extensionsToTreatAsEsm": [".ts"],
"transform": {
"^.+\\.tsx?$": [
"ts-jest",
{
"useESM": true,
"tsconfig": {
"module": "esnext",
"target": "esnext",
"moduleResolution": "bundler"
}
}
]
},
"moduleNameMapper": {
"^@elizaos/core$": "<rootDir>/packages/core/src/index.ts",
"^@elizaos/(.*)$": "<rootDir>/packages/$1/src/index.ts"
}
}
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@
"typescript": "5.6.3",
"vite": "5.4.11",
"vitest": "2.1.5",
"viem": "2.21.58"
"viem": "2.21.58",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.11",
"jest": "^29.7.0"
},
"pnpm": {
"overrides": {
Expand Down
67 changes: 67 additions & 0 deletions packages/client-twitter/src/__tests__/environment.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { twitterEnvSchema } from "../environment";

describe("Twitter Environment Configuration", () => {
describe("Username Validation", () => {
const validateUsername = (username: string) => {
return twitterEnvSchema.parse({
TWITTER_DRY_RUN: false,
TWITTER_USERNAME: "test_user",
TWITTER_PASSWORD: "password",
TWITTER_EMAIL: "[email protected]",
TWITTER_2FA_SECRET: "",
TWITTER_RETRY_LIMIT: 5,
TWITTER_POLL_INTERVAL: 120,
POST_INTERVAL_MIN: 90,
POST_INTERVAL_MAX: 180,
ENABLE_ACTION_PROCESSING: false,
ACTION_INTERVAL: 5,
POST_IMMEDIATELY: false,
TWITTER_TARGET_USERS: [username],
});
};

it("should allow valid traditional usernames", () => {
expect(() => validateUsername("normal_user")).not.toThrow();
expect(() => validateUsername("user123")).not.toThrow();
expect(() => validateUsername("a_1_b_2")).not.toThrow();
});

it("should allow usernames starting with digits", () => {
expect(() => validateUsername("123user")).not.toThrow();
expect(() => validateUsername("42_test")).not.toThrow();
expect(() => validateUsername("007james")).not.toThrow();
});

it("should allow wildcard", () => {
expect(() => validateUsername("*")).not.toThrow();
});

it("should reject invalid usernames", () => {
expect(() => validateUsername("")).toThrow();
expect(() => validateUsername("user@123")).toThrow();
expect(() => validateUsername("user-123")).toThrow();
expect(() => validateUsername("user.123")).toThrow();
expect(() => validateUsername("a".repeat(16))).toThrow();
});

it("should handle array of usernames", () => {
const config = {
TWITTER_DRY_RUN: false,
TWITTER_USERNAME: "test_user",
TWITTER_PASSWORD: "password",
TWITTER_EMAIL: "[email protected]",
TWITTER_2FA_SECRET: "",
TWITTER_RETRY_LIMIT: 5,
TWITTER_POLL_INTERVAL: 120,
POST_INTERVAL_MIN: 90,
POST_INTERVAL_MAX: 180,
ENABLE_ACTION_PROCESSING: false,
ACTION_INTERVAL: 5,
POST_IMMEDIATELY: false,
TWITTER_TARGET_USERS: ["normal_user", "123digit", "*"],
};

expect(() => twitterEnvSchema.parse(config)).not.toThrow();
});
});
});
16 changes: 11 additions & 5 deletions packages/client-twitter/src/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ export const DEFAULT_MAX_TWEET_LENGTH = 280;

const twitterUsernameSchema = z
.string()
.min(1, "An X/Twitter Username must be at least 1 characters long")
.min(1, "An X/Twitter Username must be at least 1 character long")
.max(15, "An X/Twitter Username cannot exceed 15 characters")
.regex(
/^[A-Za-z0-9_]*$/,
"An X Username can only contain letters, numbers, and underscores"
);
.refine((username) => {
// Allow wildcard '*' as a special case
if (username === "*") return true;

// Twitter usernames can:
// - Start with digits now
// - Contain letters, numbers, underscores
// - Must not be empty
return /^[A-Za-z0-9_]+$/.test(username);
}, "An X Username can only contain letters, numbers, and underscores");

/**
* This schema defines all required/optional environment settings,
Expand Down
54 changes: 41 additions & 13 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 35 additions & 5 deletions scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,57 @@
REQUIRED_NODE_VERSION=22
CURRENT_NODE_VERSION=$(node -v | cut -d'.' -f1 | sed 's/v//')

if (( CURRENT_NODE_VERSION < REQUIRED_NODE_VERSION )); then
if ((CURRENT_NODE_VERSION < REQUIRED_NODE_VERSION)); then
echo "Error: Node.js version must be $REQUIRED_NODE_VERSION or higher. Current version is $CURRENT_NODE_VERSION."
exit 1
fi

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

# If specific test file provided, run just that
if [[ "$1" == *".ts" ]]; then
echo -e "\033[1mRunning specific test: $1\033[0m"
node --experimental-vm-modules $(which jest) "$1"
exit $?
fi

# If package name provided, run just that package
if [ ! -z "$1" ]; then
package="$1"
package_path="packages/$package"

if [ ! -d "$package_path" ]; then
echo -e "\033[1mPackage directory '$package' not found\033[0m"
exit 1
fi

echo -e "\033[1mTesting package: $package\033[0m"
# Use find to get all test files and pass them explicitly to jest
test_files=$(find "packages/$package/src" -name "*.test.ts" -type f)
if [ -z "$test_files" ]; then
echo "No test files found"
exit 1
fi
echo "Found test files:"
echo "$test_files"
node --experimental-vm-modules $(which jest) $test_files
exit $?
fi

# Check if the packages directory exists
if [ ! -d "packages" ]; then
echo "Error: 'packages' directory not found."
exit 1
fi

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

# Test packages in specified order
for package in "${PACKAGES[@]}"; do
package_path="packages/$package"

if [ ! -d "$package_path" ]; then
echo -e "\033[1mPackage directory '$package' not found, skipping...\033[0m"
continue
Expand Down Expand Up @@ -57,7 +87,7 @@ for package in "${PACKAGES[@]}"; do
echo "No package.json found in $package, skipping..."
fi

cd - > /dev/null || exit
cd - >/dev/null || exit
done

echo -e "\033[1mTest process completed.😎\033[0m"
echo -e "\033[1mTest process completed.😎\033[0m"
20 changes: 13 additions & 7 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"moduleResolution": "node",
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
"forceConsistentCasingInFileNames": true,
"allowImportingTsExtensions": true,
"noEmit": true
},
"files": [],
"references": [
{ "path": "packages/core" },
{ "path": "packages/client-slack" }
{
"path": "packages/core"
},
{
"path": "packages/client-slack"
}
]
}
}

0 comments on commit ba8e83b

Please sign in to comment.