Skip to content

Commit

Permalink
update authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
ileana-pr committed Jan 14, 2025
1 parent d55c86c commit e7c19f0
Showing 1 changed file with 60 additions and 169 deletions.
229 changes: 60 additions & 169 deletions packages/client-twitter/src/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import {
parseBooleanFromText,
IAgentRuntime,
ActionTimelineType,
elizaLogger,
} from "@elizaos/core";
import { z, ZodError } from "zod";
import { z } from "zod";

export const DEFAULT_MAX_TWEET_LENGTH = 280;

Expand All @@ -30,41 +31,15 @@ export const twitterEnvSchema = z.object({
TWITTER_DRY_RUN: z.boolean(),
TWITTER_USERNAME: z.string().min(1, "X/Twitter username is required"),
TWITTER_PASSWORD: z.string().min(1, "X/Twitter password is required"),
TWITTER_EMAIL: z.string().email("Valid X/Twitter email is required"),
TWITTER_EMAIL: z.string()
.min(1, "X/Twitter email is required")
.email("Invalid email format - must be a valid email address"),
MAX_TWEET_LENGTH: z.number().int().default(DEFAULT_MAX_TWEET_LENGTH),
TWITTER_SEARCH_ENABLE: z.boolean().default(false),
TWITTER_2FA_SECRET: z.string(),
TWITTER_RETRY_LIMIT: z.number().int(),
TWITTER_POLL_INTERVAL: z.number().int(),
TWITTER_TARGET_USERS: z.array(twitterUsernameSchema).default([]),
// I guess it's possible to do the transformation with zod
// not sure it's preferable, maybe a readability issue
// since more people will know js/ts than zod
/*
z
.string()
.transform((val) => val.trim())
.pipe(
z.string()
.transform((val) =>
val ? val.split(',').map((u) => u.trim()).filter(Boolean) : []
)
.pipe(
z.array(
z.string()
.min(1)
.max(15)
.regex(
/^[A-Za-z][A-Za-z0-9_]*[A-Za-z0-9]$|^[A-Za-z]$/,
'Invalid Twitter username format'
)
)
)
.transform((users) => users.join(','))
)
.optional()
.default(''),
*/
POST_INTERVAL_MIN: z.number().int(),
POST_INTERVAL_MAX: z.number().int(),
ENABLE_ACTION_PROCESSING: z.boolean(),
Expand All @@ -79,20 +54,6 @@ export const twitterEnvSchema = z.object({

export type TwitterConfig = z.infer<typeof twitterEnvSchema>;

/**
* Helper to parse a comma-separated list of Twitter usernames
* (already present in your code).
*/
function parseTargetUsers(targetUsersStr?: string | null): string[] {
if (!targetUsersStr?.trim()) {
return [];
}
return targetUsersStr
.split(",")
.map((user) => user.trim())
.filter(Boolean);
}

function safeParseInt(
value: string | undefined | null,
defaultValue: number
Expand All @@ -106,135 +67,65 @@ function safeParseInt(
* Validates or constructs a TwitterConfig object using zod,
* taking values from the IAgentRuntime or process.env as needed.
*/
// This also is organized to serve as a point of documentation for the client
// most of the inputs from the framework (env/character)

// we also do a lot of typing/parsing here
// so we can do it once and only once per character
export async function validateTwitterConfig(
runtime: IAgentRuntime
): Promise<TwitterConfig> {
try {
const twitterConfig = {
TWITTER_DRY_RUN:
parseBooleanFromText(
runtime.getSetting("TWITTER_DRY_RUN") ||
process.env.TWITTER_DRY_RUN
) ?? false, // parseBooleanFromText return null if "", map "" to false

TWITTER_USERNAME:
runtime.getSetting("TWITTER_USERNAME") ||
process.env.TWITTER_USERNAME,

TWITTER_PASSWORD:
runtime.getSetting("TWITTER_PASSWORD") ||
process.env.TWITTER_PASSWORD,

TWITTER_EMAIL:
runtime.getSetting("TWITTER_EMAIL") ||
process.env.TWITTER_EMAIL,

// number as string?
MAX_TWEET_LENGTH: safeParseInt(
runtime.getSetting("MAX_TWEET_LENGTH") ||
process.env.MAX_TWEET_LENGTH,
DEFAULT_MAX_TWEET_LENGTH
),

TWITTER_SEARCH_ENABLE:
parseBooleanFromText(
runtime.getSetting("TWITTER_SEARCH_ENABLE") ||
process.env.TWITTER_SEARCH_ENABLE
) ?? false,

// string passthru
TWITTER_2FA_SECRET:
runtime.getSetting("TWITTER_2FA_SECRET") ||
process.env.TWITTER_2FA_SECRET ||
"",

// int
TWITTER_RETRY_LIMIT: safeParseInt(
runtime.getSetting("TWITTER_RETRY_LIMIT") ||
process.env.TWITTER_RETRY_LIMIT,
5
),

// int in seconds
TWITTER_POLL_INTERVAL: safeParseInt(
runtime.getSetting("TWITTER_POLL_INTERVAL") ||
process.env.TWITTER_POLL_INTERVAL,
120 // 2m
),

// comma separated string
TWITTER_TARGET_USERS: parseTargetUsers(
runtime.getSetting("TWITTER_TARGET_USERS") ||
process.env.TWITTER_TARGET_USERS
),

// int in minutes
POST_INTERVAL_MIN: safeParseInt(
runtime.getSetting("POST_INTERVAL_MIN") ||
process.env.POST_INTERVAL_MIN,
90 // 1.5 hours
),

// int in minutes
POST_INTERVAL_MAX: safeParseInt(
runtime.getSetting("POST_INTERVAL_MAX") ||
process.env.POST_INTERVAL_MAX,
180 // 3 hours
),

// bool
ENABLE_ACTION_PROCESSING:
parseBooleanFromText(
runtime.getSetting("ENABLE_ACTION_PROCESSING") ||
process.env.ENABLE_ACTION_PROCESSING
) ?? false,

// init in minutes (min 1m)
ACTION_INTERVAL: safeParseInt(
runtime.getSetting("ACTION_INTERVAL") ||
process.env.ACTION_INTERVAL,
5 // 5 minutes
),

// bool
POST_IMMEDIATELY:
parseBooleanFromText(
runtime.getSetting("POST_IMMEDIATELY") ||
process.env.POST_IMMEDIATELY
) ?? false,

TWITTER_SPACES_ENABLE:
parseBooleanFromText(
runtime.getSetting("TWITTER_SPACES_ENABLE") ||
process.env.TWITTER_SPACES_ENABLE
) ?? false,

MAX_ACTIONS_PROCESSING: safeParseInt(
runtime.getSetting("MAX_ACTIONS_PROCESSING") ||
process.env.MAX_ACTIONS_PROCESSING,
1
),
// Helper to handle $ prefixed values
const getConfigValue = (key: string) => {
const value = runtime.getSetting(key);
return value?.startsWith('$') ? process.env[key] : value || process.env[key];
};

const config = {
TWITTER_DRY_RUN: parseBooleanFromText(getConfigValue("TWITTER_DRY_RUN") || "false"),
TWITTER_USERNAME: getConfigValue("TWITTER_USERNAME"),
TWITTER_PASSWORD: getConfigValue("TWITTER_PASSWORD"),
TWITTER_EMAIL: getConfigValue("TWITTER_EMAIL"),
MAX_TWEET_LENGTH: safeParseInt(getConfigValue("MAX_TWEET_LENGTH"), DEFAULT_MAX_TWEET_LENGTH),
TWITTER_SEARCH_ENABLE: parseBooleanFromText(getConfigValue("TWITTER_SEARCH_ENABLE") || "false"),
TWITTER_2FA_SECRET: getConfigValue("TWITTER_2FA_SECRET") || "",
TWITTER_RETRY_LIMIT: safeParseInt(getConfigValue("TWITTER_RETRY_LIMIT"), 5),
TWITTER_POLL_INTERVAL: safeParseInt(getConfigValue("TWITTER_POLL_INTERVAL"), 60),
TWITTER_TARGET_USERS: [],
POST_INTERVAL_MIN: safeParseInt(getConfigValue("POST_INTERVAL_MIN"), 60),
POST_INTERVAL_MAX: safeParseInt(getConfigValue("POST_INTERVAL_MAX"), 120),
ENABLE_ACTION_PROCESSING: parseBooleanFromText(getConfigValue("ENABLE_ACTION_PROCESSING") || "false"),
ACTION_INTERVAL: safeParseInt(getConfigValue("ACTION_INTERVAL"), 60),
POST_IMMEDIATELY: parseBooleanFromText(getConfigValue("POST_IMMEDIATELY") || "false"),
TWITTER_SPACES_ENABLE: parseBooleanFromText(getConfigValue("TWITTER_SPACES_ENABLE") || "false"),
MAX_ACTIONS_PROCESSING: safeParseInt(getConfigValue("MAX_ACTIONS_PROCESSING"), 10),
ACTION_TIMELINE_TYPE: ActionTimelineType.ForYou,
};

elizaLogger.error("Raw Twitter config values:", {
runtimeUsername: runtime.getSetting("TWITTER_USERNAME"),
envUsername: process.env.TWITTER_USERNAME,
runtimeEmail: runtime.getSetting("TWITTER_EMAIL"),
envEmail: process.env.TWITTER_EMAIL,
finalEmail: config.TWITTER_EMAIL
});

elizaLogger.error("Twitter config validation:", {
username: config.TWITTER_USERNAME,
email: config.TWITTER_EMAIL,
hasPassword: !!config.TWITTER_PASSWORD,
has2FA: !!config.TWITTER_2FA_SECRET,
searchEnabled: config.TWITTER_SEARCH_ENABLE,
spacesEnabled: config.TWITTER_SPACES_ENABLE
});

// Validation
if (!config.TWITTER_EMAIL?.includes("@")) {
throw new Error("X/Twitter configuration validation failed:\nTWITTER_EMAIL: Email must contain @ and a domain (e.g. [email protected])");
}

ACTION_TIMELINE_TYPE:
runtime.getSetting("ACTION_TIMELINE_TYPE") ||
process.env.ACTION_TIMELINE_TYPE,
};
if (!config.TWITTER_USERNAME) {
throw new Error("X/Twitter configuration validation failed:\nTWITTER_USERNAME is required");
}

return twitterEnvSchema.parse(twitterConfig);
} catch (error) {
if (error instanceof ZodError) {
const errorMessages = error.errors
.map((err) => `${err.path.join(".")}: ${err.message}`)
.join("\n");
throw new Error(
`X/Twitter configuration validation failed:\n${errorMessages}`
);
}
throw error;
if (!config.TWITTER_PASSWORD) {
throw new Error("X/Twitter configuration validation failed:\nTWITTER_PASSWORD is required");
}

return config;
}

0 comments on commit e7c19f0

Please sign in to comment.