scrapebadger 0.1.9 → 0.3.0

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.
@@ -1,3 +1,5 @@
1
+ import { EventEmitter } from 'node:events';
2
+
1
3
  /**
2
4
  * Configuration management for the ScrapeBadger SDK.
3
5
  */
@@ -35,7 +37,7 @@ interface RequestOptions {
35
37
  * Base HTTP client for making API requests.
36
38
  */
37
39
  declare class BaseClient {
38
- private readonly config;
40
+ readonly config: ResolvedConfig;
39
41
  constructor(config: ResolvedConfig);
40
42
  /**
41
43
  * Make an HTTP request to the API.
@@ -254,6 +256,26 @@ interface Tweet {
254
256
  username?: string;
255
257
  /** Author's display name */
256
258
  user_name?: string;
259
+ /** Author's profile image URL */
260
+ user_profile_image_url?: string;
261
+ /** Author's bio */
262
+ user_description?: string;
263
+ /** Author's location */
264
+ user_location?: string;
265
+ /** Author's website URL */
266
+ user_url?: string;
267
+ /** Author's follower count */
268
+ user_followers_count?: number;
269
+ /** Author's following count */
270
+ user_following_count?: number;
271
+ /** Author's tweet count */
272
+ user_tweet_count?: number;
273
+ /** Author's legacy verification status */
274
+ user_verified?: boolean;
275
+ /** Author's Twitter Blue verification status */
276
+ user_is_blue_verified?: boolean;
277
+ /** Author's account creation date */
278
+ user_created_at?: string;
257
279
  /** Number of likes */
258
280
  favorite_count: number;
259
281
  /** Number of retweets */
@@ -928,6 +950,7 @@ declare class TweetsClient {
928
950
  */
929
951
  search(query: string, options?: PaginationOptions & {
930
952
  queryType?: QueryType;
953
+ count?: number;
931
954
  }): Promise<PaginatedResponse<Tweet>>;
932
955
  /**
933
956
  * Iterate through all search results with automatic pagination.
@@ -957,6 +980,7 @@ declare class TweetsClient {
957
980
  */
958
981
  searchAll(query: string, options?: IteratorOptions & {
959
982
  queryType?: QueryType;
983
+ count?: number;
960
984
  }): AsyncGenerator<Tweet, void, undefined>;
961
985
  /**
962
986
  * Get tweets from a user's timeline.
@@ -1741,6 +1765,948 @@ declare class GeoClient {
1741
1765
  search(options?: GeoSearchOptions): Promise<PaginatedResponse<Place>>;
1742
1766
  }
1743
1767
 
1768
+ /**
1769
+ * Custom exceptions for the ScrapeBadger SDK.
1770
+ */
1771
+ /**
1772
+ * Base error class for all ScrapeBadger errors.
1773
+ */
1774
+ declare class ScrapeBadgerError extends Error {
1775
+ constructor(message: string);
1776
+ }
1777
+ /**
1778
+ * Raised when authentication fails (invalid or missing API key).
1779
+ */
1780
+ declare class AuthenticationError extends ScrapeBadgerError {
1781
+ constructor(message?: string);
1782
+ }
1783
+ /**
1784
+ * Raised when rate limit is exceeded.
1785
+ */
1786
+ declare class RateLimitError extends ScrapeBadgerError {
1787
+ /** Unix timestamp when the rate limit resets */
1788
+ readonly retryAfter: number | undefined;
1789
+ /** Maximum requests per minute for this tier */
1790
+ readonly limit: number | undefined;
1791
+ /** Remaining requests in the current window */
1792
+ readonly remaining: number | undefined;
1793
+ constructor(message?: string, options?: {
1794
+ retryAfter?: number;
1795
+ limit?: number;
1796
+ remaining?: number;
1797
+ });
1798
+ }
1799
+ /**
1800
+ * Raised when the requested resource is not found.
1801
+ */
1802
+ declare class NotFoundError extends ScrapeBadgerError {
1803
+ /** The resource type that was not found */
1804
+ readonly resourceType: string | undefined;
1805
+ /** The resource ID that was not found */
1806
+ readonly resourceId: string | undefined;
1807
+ constructor(message?: string, resourceType?: string, resourceId?: string);
1808
+ }
1809
+ /**
1810
+ * Raised when the request is invalid.
1811
+ */
1812
+ declare class ValidationError extends ScrapeBadgerError {
1813
+ /** Validation errors by field */
1814
+ readonly errors: Record<string, string[]> | undefined;
1815
+ constructor(message?: string, errors?: Record<string, string[]>);
1816
+ }
1817
+ /**
1818
+ * Raised when an internal server error occurs.
1819
+ */
1820
+ declare class ServerError extends ScrapeBadgerError {
1821
+ /** HTTP status code */
1822
+ readonly statusCode: number;
1823
+ constructor(message?: string, statusCode?: number);
1824
+ }
1825
+ /**
1826
+ * Raised when the request times out.
1827
+ */
1828
+ declare class TimeoutError extends ScrapeBadgerError {
1829
+ /** Timeout duration in milliseconds */
1830
+ readonly timeout: number;
1831
+ constructor(message: string | undefined, timeout: number);
1832
+ }
1833
+ /**
1834
+ * Raised when the account has insufficient credits.
1835
+ */
1836
+ declare class InsufficientCreditsError extends ScrapeBadgerError {
1837
+ /** Current credit balance */
1838
+ readonly creditsBalance: number | undefined;
1839
+ constructor(message?: string, creditsBalance?: number);
1840
+ }
1841
+ /**
1842
+ * Raised when the account is restricted.
1843
+ */
1844
+ declare class AccountRestrictedError extends ScrapeBadgerError {
1845
+ /** Reason for the restriction */
1846
+ readonly reason: string | undefined;
1847
+ constructor(message?: string, reason?: string);
1848
+ }
1849
+ /**
1850
+ * Raised when a resource conflict occurs (e.g. duplicate monitor name).
1851
+ *
1852
+ * Maps to HTTP 409 Conflict.
1853
+ *
1854
+ * @example
1855
+ * ```typescript
1856
+ * import { ConflictError } from "scrapebadger";
1857
+ *
1858
+ * try {
1859
+ * await client.twitter.stream.createMonitor({
1860
+ * name: "Existing Monitor",
1861
+ * usernames: ["elonmusk"],
1862
+ * pollIntervalSeconds: 10,
1863
+ * });
1864
+ * } catch (err) {
1865
+ * if (err instanceof ConflictError) {
1866
+ * console.error("Monitor name already exists:", err.message);
1867
+ * }
1868
+ * }
1869
+ * ```
1870
+ */
1871
+ declare class ConflictError extends ScrapeBadgerError {
1872
+ constructor(message?: string);
1873
+ }
1874
+ /**
1875
+ * Raised when the WebSocket stream connection fails or is terminated.
1876
+ *
1877
+ * Common codes:
1878
+ * - 4001 -- Invalid or missing API key (auth failure)
1879
+ * - 4003 -- Connection limit exceeded (max 5 per API key)
1880
+ * - 1001 -- Server closed due to pong timeout
1881
+ * - 0 -- Unknown/parse error
1882
+ *
1883
+ * @example
1884
+ * ```typescript
1885
+ * import { WebSocketStreamError } from "scrapebadger";
1886
+ *
1887
+ * try {
1888
+ * for await (const event of client.twitter.stream.connectIter()) {
1889
+ * // ...
1890
+ * }
1891
+ * } catch (err) {
1892
+ * if (err instanceof WebSocketStreamError && err.code === 4001) {
1893
+ * console.error("API key rejected -- check your key");
1894
+ * }
1895
+ * }
1896
+ * ```
1897
+ */
1898
+ declare class WebSocketStreamError extends ScrapeBadgerError {
1899
+ /** WebSocket close code or server error code */
1900
+ readonly code: number | undefined;
1901
+ constructor(message?: string, code?: number);
1902
+ }
1903
+
1904
+ /**
1905
+ * TypeScript types for Twitter Streams API.
1906
+ */
1907
+ /**
1908
+ * Lifecycle status of a stream monitor.
1909
+ */
1910
+ type MonitorStatus = "active" | "paused" | "error";
1911
+ /**
1912
+ * A stream monitor watching a set of Twitter accounts.
1913
+ */
1914
+ interface StreamMonitor {
1915
+ /** UUID of the monitor */
1916
+ id: string;
1917
+ /** Human-readable label */
1918
+ name: string;
1919
+ /** Lowercased Twitter handles being monitored */
1920
+ usernames: string[];
1921
+ /** How often the monitor polls in seconds */
1922
+ poll_interval_seconds: number;
1923
+ /** Current lifecycle state */
1924
+ status: MonitorStatus;
1925
+ /** Human-readable reason for non-active status */
1926
+ status_reason: string | null;
1927
+ /** HTTPS delivery URL, or null */
1928
+ webhook_url: string | null;
1929
+ /** Whether a signing secret is configured */
1930
+ webhook_secret_set: boolean;
1931
+ /** Projected credit burn rate */
1932
+ estimated_credits_per_hour: number;
1933
+ /** Tier label (Ultra, High, Standard, Low, Minimal) */
1934
+ pricing_tier: string;
1935
+ /** ISO timestamp of creation */
1936
+ created_at: string;
1937
+ /** ISO timestamp of last modification */
1938
+ updated_at: string;
1939
+ }
1940
+ /**
1941
+ * Paginated list of stream monitors.
1942
+ */
1943
+ interface StreamMonitorList {
1944
+ /** The monitors on this page */
1945
+ monitors: StreamMonitor[];
1946
+ /** Total count across all pages */
1947
+ total: number;
1948
+ /** Current page number (1-indexed) */
1949
+ page: number;
1950
+ /** Number of items per page */
1951
+ page_size: number;
1952
+ }
1953
+ /**
1954
+ * Create monitor request parameters.
1955
+ */
1956
+ interface CreateMonitorParams {
1957
+ /** Display name (1-100 chars) */
1958
+ name: string;
1959
+ /** Twitter handles to monitor (1-100 items) */
1960
+ usernames: string[];
1961
+ /** Polling interval in seconds (>= 0.1) */
1962
+ pollIntervalSeconds: number;
1963
+ /** Optional HTTPS webhook URL */
1964
+ webhookUrl?: string;
1965
+ /** Optional signing secret */
1966
+ webhookSecret?: string;
1967
+ }
1968
+ /**
1969
+ * Update monitor request parameters (all fields optional).
1970
+ */
1971
+ interface UpdateMonitorParams {
1972
+ /** New display name */
1973
+ name?: string;
1974
+ /** Replacement list of Twitter handles */
1975
+ usernames?: string[];
1976
+ /** New polling interval */
1977
+ pollIntervalSeconds?: number;
1978
+ /** New status ("active" or "paused") */
1979
+ status?: "active" | "paused";
1980
+ /** New webhook URL (empty string clears it) */
1981
+ webhookUrl?: string;
1982
+ /** New signing secret */
1983
+ webhookSecret?: string;
1984
+ }
1985
+ /**
1986
+ * Type discriminator for WebSocket events.
1987
+ */
1988
+ type StreamEventType = "connected" | "ping" | "tweet" | "error";
1989
+ /**
1990
+ * A tweet payload embedded in a TweetEvent.
1991
+ */
1992
+ interface StreamTweet {
1993
+ /** Snowflake tweet ID */
1994
+ id: string;
1995
+ /** Tweet text content */
1996
+ text: string;
1997
+ /** Raw created_at string from Twikit */
1998
+ created_at?: string;
1999
+ /** Author numeric ID as string */
2000
+ user_id?: string;
2001
+ /** Author handle (without @) */
2002
+ username?: string;
2003
+ /** Author display name */
2004
+ user_name?: string;
2005
+ /** Like count at detection time */
2006
+ favorite_count: number;
2007
+ /** Retweet count at detection time */
2008
+ retweet_count: number;
2009
+ /** Reply count at detection time */
2010
+ reply_count: number;
2011
+ /** Attached media */
2012
+ media: Record<string, unknown>[];
2013
+ /** URL entities */
2014
+ urls: Record<string, unknown>[];
2015
+ /** Hashtag entities */
2016
+ hashtags: Record<string, unknown>[];
2017
+ }
2018
+ /**
2019
+ * Emitted immediately after a successful WebSocket upgrade.
2020
+ */
2021
+ interface ConnectedEvent {
2022
+ type: "connected";
2023
+ /** Server-assigned UUID for this connection */
2024
+ connectionId: string;
2025
+ /** The API key identifier used */
2026
+ apiKeyId: string;
2027
+ }
2028
+ /**
2029
+ * Server keepalive ping. SDK responds with pong automatically.
2030
+ */
2031
+ interface PingEvent {
2032
+ type: "ping";
2033
+ /** Server timestamp in ISO format */
2034
+ timestamp: string;
2035
+ }
2036
+ /**
2037
+ * A new tweet detected by one of the caller's monitors.
2038
+ */
2039
+ interface TweetEvent {
2040
+ type: "tweet";
2041
+ /** UUID of the monitor that detected this tweet */
2042
+ monitorId: string;
2043
+ /** Snowflake tweet ID as string */
2044
+ tweetId: string;
2045
+ /** Lowercased Twitter handle of the author */
2046
+ authorUsername: string;
2047
+ /** ISO timestamp decoded from Snowflake */
2048
+ tweetPublishedAt: string;
2049
+ /** ISO timestamp of server-side detection */
2050
+ detectedAt: string;
2051
+ /** Milliseconds between tweet publish and detection */
2052
+ latencyMs: number;
2053
+ /** Full tweet data snapshot */
2054
+ tweet: StreamTweet;
2055
+ }
2056
+ /**
2057
+ * Server-side error event (sent before connection close on auth failure).
2058
+ */
2059
+ interface ErrorEvent {
2060
+ type: "error";
2061
+ /** Numeric error code (4001 = auth, 4003 = connection limit) */
2062
+ code: number;
2063
+ /** Human-readable description */
2064
+ message: string;
2065
+ }
2066
+ /**
2067
+ * Union of all possible WebSocket event types.
2068
+ */
2069
+ type StreamEvent = ConnectedEvent | PingEvent | TweetEvent | ErrorEvent;
2070
+ /**
2071
+ * A tweet delivery log entry.
2072
+ *
2073
+ * Field names match the JSON wire format (snake_case) from the server.
2074
+ */
2075
+ interface DeliveryLog {
2076
+ id: string;
2077
+ monitor_id: string;
2078
+ monitor_name: string;
2079
+ tweet_id: string;
2080
+ author_username: string;
2081
+ tweet_text_preview: string | null;
2082
+ tweet_url: string;
2083
+ tweet_published_at: string;
2084
+ detected_at: string;
2085
+ /** Detection latency in milliseconds */
2086
+ latency_ms: number;
2087
+ /** "green" | "yellow" | "red" */
2088
+ latency_badge: string;
2089
+ /** "websocket_delivered" | "webhook_delivered" | "webhook_failed" */
2090
+ delivery_status: string;
2091
+ webhook_status_code: number | null;
2092
+ webhook_attempts: number;
2093
+ }
2094
+ /**
2095
+ * Paginated tweet delivery logs.
2096
+ */
2097
+ interface DeliveryLogList {
2098
+ logs: DeliveryLog[];
2099
+ total: number;
2100
+ page: number;
2101
+ page_size: number;
2102
+ }
2103
+ /**
2104
+ * A billing activity log entry.
2105
+ */
2106
+ interface BillingLog {
2107
+ id: string;
2108
+ monitor_id: string;
2109
+ monitor_name: string;
2110
+ billed_at: string;
2111
+ num_accounts: number;
2112
+ credits_deducted: number;
2113
+ tier_label: string;
2114
+ rate_applied: number;
2115
+ }
2116
+ /**
2117
+ * Paginated billing activity logs.
2118
+ */
2119
+ interface BillingLogList {
2120
+ logs: BillingLog[];
2121
+ total: number;
2122
+ page: number;
2123
+ page_size: number;
2124
+ }
2125
+ /**
2126
+ * Lifecycle status of a filter rule.
2127
+ */
2128
+ type FilterRuleStatus = "active" | "paused" | "error" | "inactive";
2129
+ /**
2130
+ * A filter rule that watches Twitter for tweets matching a search query.
2131
+ */
2132
+ interface FilterRuleResponse {
2133
+ /** UUID of the filter rule */
2134
+ id: string;
2135
+ /** Short human-readable tag for the rule */
2136
+ tag: string;
2137
+ /** Twitter search query string */
2138
+ query: string;
2139
+ /** How often the rule polls in seconds */
2140
+ interval_seconds: number;
2141
+ /** Current lifecycle state */
2142
+ status: FilterRuleStatus;
2143
+ /** Human-readable reason for non-active status */
2144
+ status_reason: string | null;
2145
+ /** HTTPS delivery URL, or null */
2146
+ webhook_url: string | null;
2147
+ /** Whether a signing secret is configured */
2148
+ webhook_secret_set: boolean;
2149
+ /** Maximum tweet results fetched per poll */
2150
+ max_results_per_poll: number;
2151
+ /** Credits burned per rule per day */
2152
+ credits_per_rule_per_day: number;
2153
+ /** Tier label (e.g. Ultra, High, Standard) */
2154
+ pricing_tier: string;
2155
+ /** ISO timestamp of creation */
2156
+ created_at: string;
2157
+ /** ISO timestamp of last modification */
2158
+ updated_at: string;
2159
+ }
2160
+ /**
2161
+ * Request body for creating a filter rule.
2162
+ */
2163
+ interface FilterRuleCreate {
2164
+ /** Short tag identifying this rule */
2165
+ tag: string;
2166
+ /** Twitter search query string */
2167
+ query: string;
2168
+ /** Polling interval in seconds */
2169
+ interval_seconds: number;
2170
+ /** Optional HTTPS webhook URL */
2171
+ webhook_url?: string | null;
2172
+ /** Optional webhook signing secret */
2173
+ webhook_secret?: string | null;
2174
+ /** Maximum results per poll (server default applies if omitted) */
2175
+ max_results_per_poll?: number;
2176
+ }
2177
+ /**
2178
+ * Request body for updating a filter rule (all fields optional).
2179
+ */
2180
+ interface FilterRuleUpdate {
2181
+ /** New tag */
2182
+ tag?: string;
2183
+ /** New search query */
2184
+ query?: string;
2185
+ /** New polling interval in seconds */
2186
+ interval_seconds?: number;
2187
+ /** New lifecycle status */
2188
+ status?: "active" | "paused" | "inactive";
2189
+ /** New webhook URL (null clears it) */
2190
+ webhook_url?: string | null;
2191
+ /** New webhook signing secret */
2192
+ webhook_secret?: string | null;
2193
+ /** New max results per poll */
2194
+ max_results_per_poll?: number;
2195
+ }
2196
+ /**
2197
+ * A pricing tier for filter rules.
2198
+ */
2199
+ interface FilterRulePricingTier {
2200
+ /** UUID of the pricing tier */
2201
+ id: string;
2202
+ /** Human-readable tier label */
2203
+ tier_label: string;
2204
+ /** Maximum polling interval in seconds for this tier */
2205
+ max_interval_seconds: number;
2206
+ /** Credits deducted per rule per day at this tier */
2207
+ credits_per_rule_per_day: number;
2208
+ /** Ordering value for display */
2209
+ display_order: number;
2210
+ }
2211
+ /**
2212
+ * Paginated list of filter rules.
2213
+ */
2214
+ interface FilterRuleListResponse {
2215
+ /** The rules on this page */
2216
+ rules: FilterRuleResponse[];
2217
+ /** Total count across all pages */
2218
+ total: number;
2219
+ /** Requested limit */
2220
+ limit: number;
2221
+ /** Requested offset */
2222
+ offset: number;
2223
+ }
2224
+ /**
2225
+ * Response from the filter rule query validation endpoint.
2226
+ */
2227
+ interface FilterRuleValidateResponse {
2228
+ /** Whether the query is valid */
2229
+ valid: boolean;
2230
+ /** Human-readable error if query is invalid */
2231
+ error?: string;
2232
+ /** Number of sample results found */
2233
+ sample_results: number;
2234
+ }
2235
+ /**
2236
+ * A single tweet delivery log entry for a filter rule.
2237
+ *
2238
+ * Field names match the JSON wire format (snake_case) from the server.
2239
+ */
2240
+ interface FilterRuleDeliveryLog {
2241
+ id: string;
2242
+ rule_id: string;
2243
+ tweet_id: string;
2244
+ author_username: string;
2245
+ tweet_text: string | null;
2246
+ tweet_published_at: string;
2247
+ detected_at: string;
2248
+ /** Detection latency in milliseconds */
2249
+ latency_ms: number;
2250
+ delivery_status: string;
2251
+ webhook_status_code: number | null;
2252
+ webhook_attempts: number;
2253
+ tweet_type: string | null;
2254
+ }
2255
+ /**
2256
+ * Paginated filter rule delivery logs.
2257
+ */
2258
+ interface FilterRuleDeliveryLogListResponse {
2259
+ logs: FilterRuleDeliveryLog[];
2260
+ total: number;
2261
+ limit: number;
2262
+ offset: number;
2263
+ }
2264
+ /**
2265
+ * Response from the filter rule pricing tiers endpoint.
2266
+ */
2267
+ interface FilterRulePricingTiersResponse {
2268
+ tiers: FilterRulePricingTier[];
2269
+ }
2270
+ /**
2271
+ * Options for StreamClient.connect() and StreamClient.connectIter().
2272
+ */
2273
+ interface ConnectOptions {
2274
+ /**
2275
+ * Automatically reconnect on unexpected disconnect.
2276
+ * Default: false.
2277
+ */
2278
+ reconnect?: boolean;
2279
+ /**
2280
+ * Minimum seconds between reconnect attempts.
2281
+ * Enforced floor of 5 seconds. Default: 90.
2282
+ */
2283
+ reconnectDelaySeconds?: number;
2284
+ /**
2285
+ * Maximum reconnect attempts. undefined means unlimited.
2286
+ */
2287
+ maxReconnects?: number;
2288
+ }
2289
+
2290
+ /**
2291
+ * StreamClient -- stream monitor management and live WebSocket streaming.
2292
+ */
2293
+
2294
+ /**
2295
+ * Typed EventEmitter for stream events.
2296
+ *
2297
+ * @example
2298
+ * ```typescript
2299
+ * const stream = client.twitter.stream.connect();
2300
+ * stream.on("tweet", (event) => console.log(event.authorUsername));
2301
+ * stream.on("error", (err) => console.error(err));
2302
+ * ```
2303
+ */
2304
+ interface StreamEmitter extends EventEmitter {
2305
+ on(event: "connected", listener: (event: ConnectedEvent) => void): this;
2306
+ on(event: "ping", listener: (event: PingEvent) => void): this;
2307
+ on(event: "tweet", listener: (event: TweetEvent) => void): this;
2308
+ on(event: "error", listener: (error: WebSocketStreamError) => void): this;
2309
+ on(event: "close", listener: () => void): this;
2310
+ once(event: "connected", listener: (event: ConnectedEvent) => void): this;
2311
+ once(event: "tweet", listener: (event: TweetEvent) => void): this;
2312
+ once(event: "close", listener: () => void): this;
2313
+ /** Gracefully close the WebSocket connection. */
2314
+ close(): void;
2315
+ }
2316
+ /**
2317
+ * Client for Twitter Streams -- monitor CRUD and live WebSocket streaming.
2318
+ *
2319
+ * Accessed as `client.twitter.stream`.
2320
+ *
2321
+ * @example Monitor CRUD
2322
+ * ```typescript
2323
+ * const monitor = await client.twitter.stream.createMonitor({
2324
+ * name: "Tech Leaders",
2325
+ * usernames: ["elonmusk", "naval"],
2326
+ * pollIntervalSeconds: 10,
2327
+ * });
2328
+ * console.log(`Created: ${monitor.id}, tier: ${monitor.pricing_tier}`);
2329
+ * ```
2330
+ *
2331
+ * @example EventEmitter streaming
2332
+ * ```typescript
2333
+ * const stream = client.twitter.stream.connect();
2334
+ * stream.on("tweet", (event) => {
2335
+ * console.log(`@${event.authorUsername}: ${event.tweet.text}`);
2336
+ * console.log(` latency: ${event.latencyMs}ms`);
2337
+ * });
2338
+ * stream.on("error", (err) => console.error("Stream error:", err));
2339
+ * // Later:
2340
+ * stream.close();
2341
+ * ```
2342
+ *
2343
+ * @example AsyncIterator streaming
2344
+ * ```typescript
2345
+ * for await (const event of client.twitter.stream.connectIter()) {
2346
+ * if (event.type === "tweet") {
2347
+ * console.log(event.authorUsername, event.latencyMs);
2348
+ * }
2349
+ * }
2350
+ * ```
2351
+ */
2352
+ declare class StreamClient {
2353
+ private readonly client;
2354
+ constructor(client: BaseClient);
2355
+ /**
2356
+ * Create a new stream monitor.
2357
+ *
2358
+ * @param params - Monitor configuration.
2359
+ * @returns The created StreamMonitor.
2360
+ * @throws InsufficientCreditsError - Credit balance below tier threshold (402).
2361
+ * @throws ValidationError - Invalid username or interval (422).
2362
+ * @throws ScrapeBadgerError - Name conflict (409).
2363
+ * @throws AuthenticationError - Invalid API key (401).
2364
+ *
2365
+ * @example
2366
+ * ```typescript
2367
+ * const monitor = await client.twitter.stream.createMonitor({
2368
+ * name: "Breaking News",
2369
+ * usernames: ["cnnbrk", "bbcbreaking"],
2370
+ * pollIntervalSeconds: 5,
2371
+ * });
2372
+ * ```
2373
+ */
2374
+ createMonitor(params: CreateMonitorParams): Promise<StreamMonitor>;
2375
+ /**
2376
+ * List stream monitors for the authenticated API key.
2377
+ *
2378
+ * @param options - Filter and pagination options.
2379
+ * @returns StreamMonitorList with pagination metadata.
2380
+ *
2381
+ * @example
2382
+ * ```typescript
2383
+ * const { monitors, total } = await client.twitter.stream.listMonitors({
2384
+ * status: "active",
2385
+ * });
2386
+ * console.log(`${total} active monitors`);
2387
+ * ```
2388
+ */
2389
+ listMonitors(options?: {
2390
+ status?: "active" | "paused" | "error";
2391
+ page?: number;
2392
+ pageSize?: number;
2393
+ }): Promise<StreamMonitorList>;
2394
+ /**
2395
+ * Get a single stream monitor by ID.
2396
+ *
2397
+ * @param monitorId - UUID of the monitor.
2398
+ * @returns The StreamMonitor.
2399
+ * @throws NotFoundError - No monitor with that ID for this API key.
2400
+ *
2401
+ * @example
2402
+ * ```typescript
2403
+ * const monitor = await client.twitter.stream.getMonitor("550e8400-...");
2404
+ * console.log(`${monitor.name}: ${monitor.status}`);
2405
+ * ```
2406
+ */
2407
+ getMonitor(monitorId: string): Promise<StreamMonitor>;
2408
+ /**
2409
+ * Partially update a stream monitor.
2410
+ *
2411
+ * Only fields that are explicitly set in params are sent to the server.
2412
+ *
2413
+ * @param monitorId - UUID of the monitor.
2414
+ * @param params - Fields to update (all optional).
2415
+ * @returns The updated StreamMonitor.
2416
+ * @throws NotFoundError - Monitor not found for this API key.
2417
+ * @throws InsufficientCreditsError - When resuming with insufficient credits.
2418
+ *
2419
+ * @example
2420
+ * ```typescript
2421
+ * const monitor = await client.twitter.stream.updateMonitor("550e8400-...", {
2422
+ * pollIntervalSeconds: 60,
2423
+ * });
2424
+ * ```
2425
+ */
2426
+ updateMonitor(monitorId: string, params: UpdateMonitorParams): Promise<StreamMonitor>;
2427
+ /**
2428
+ * Pause an active stream monitor.
2429
+ *
2430
+ * Convenience wrapper around updateMonitor({ status: "paused" }).
2431
+ *
2432
+ * @param monitorId - UUID of the monitor.
2433
+ * @returns The updated StreamMonitor with status="paused".
2434
+ */
2435
+ pauseMonitor(monitorId: string): Promise<StreamMonitor>;
2436
+ /**
2437
+ * Resume a paused stream monitor.
2438
+ *
2439
+ * Convenience wrapper around updateMonitor({ status: "active" }).
2440
+ *
2441
+ * @param monitorId - UUID of the monitor.
2442
+ * @returns The updated StreamMonitor with status="active".
2443
+ * @throws InsufficientCreditsError - If credits are below the tier threshold.
2444
+ */
2445
+ resumeMonitor(monitorId: string): Promise<StreamMonitor>;
2446
+ /**
2447
+ * Delete a stream monitor and all its associated logs. Irreversible.
2448
+ *
2449
+ * @param monitorId - UUID of the monitor.
2450
+ * @throws NotFoundError - Monitor not found for this API key.
2451
+ *
2452
+ * @example
2453
+ * ```typescript
2454
+ * await client.twitter.stream.deleteMonitor("550e8400-...");
2455
+ * ```
2456
+ */
2457
+ deleteMonitor(monitorId: string): Promise<void>;
2458
+ /**
2459
+ * List tweet delivery logs.
2460
+ *
2461
+ * @param options - Filter and pagination options.
2462
+ * @returns DeliveryLogList with pagination metadata.
2463
+ */
2464
+ listDeliveryLogs(options?: {
2465
+ monitorId?: string;
2466
+ authorUsername?: string;
2467
+ deliveryStatus?: string;
2468
+ page?: number;
2469
+ pageSize?: number;
2470
+ sort?: "asc" | "desc";
2471
+ }): Promise<DeliveryLogList>;
2472
+ /**
2473
+ * List billing activity logs.
2474
+ *
2475
+ * @param options - Filter and pagination options.
2476
+ * @returns BillingLogList with pagination metadata.
2477
+ */
2478
+ listBillingLogs(options?: {
2479
+ monitorId?: string;
2480
+ page?: number;
2481
+ pageSize?: number;
2482
+ }): Promise<BillingLogList>;
2483
+ /**
2484
+ * Create a new tweet filter rule.
2485
+ *
2486
+ * @param params - Filter rule configuration.
2487
+ * @returns The created FilterRuleResponse.
2488
+ * @throws ValidationError - Invalid query or interval (422).
2489
+ * @throws InsufficientCreditsError - Credit balance below tier threshold (402).
2490
+ * @throws AuthenticationError - Invalid API key (401).
2491
+ *
2492
+ * @example
2493
+ * ```typescript
2494
+ * const rule = await client.twitter.stream.createFilterRule({
2495
+ * tag: "python news",
2496
+ * query: "#python lang:en -is:retweet",
2497
+ * interval_seconds: 60,
2498
+ * });
2499
+ * console.log(`Created: ${rule.id}, tier: ${rule.pricing_tier}`);
2500
+ * ```
2501
+ */
2502
+ createFilterRule(params: FilterRuleCreate): Promise<FilterRuleResponse>;
2503
+ /**
2504
+ * List filter rules for the authenticated API key.
2505
+ *
2506
+ * @param options - Filter and pagination options.
2507
+ * @returns FilterRuleListResponse with pagination metadata.
2508
+ *
2509
+ * @example
2510
+ * ```typescript
2511
+ * const { rules, total } = await client.twitter.stream.listFilterRules({
2512
+ * status: "active",
2513
+ * });
2514
+ * console.log(`${total} active rules`);
2515
+ * ```
2516
+ */
2517
+ listFilterRules(options?: {
2518
+ status?: FilterRuleResponse["status"];
2519
+ limit?: number;
2520
+ offset?: number;
2521
+ }): Promise<FilterRuleListResponse>;
2522
+ /**
2523
+ * Get a single filter rule by ID.
2524
+ *
2525
+ * @param ruleId - UUID of the filter rule.
2526
+ * @returns The FilterRuleResponse.
2527
+ * @throws NotFoundError - No rule with that ID for this API key.
2528
+ *
2529
+ * @example
2530
+ * ```typescript
2531
+ * const rule = await client.twitter.stream.getFilterRule("550e8400-...");
2532
+ * console.log(`${rule.tag}: ${rule.status}`);
2533
+ * ```
2534
+ */
2535
+ getFilterRule(ruleId: string): Promise<FilterRuleResponse>;
2536
+ /**
2537
+ * Partially update a filter rule.
2538
+ *
2539
+ * Only fields that are explicitly set in params are sent to the server.
2540
+ *
2541
+ * @param ruleId - UUID of the filter rule.
2542
+ * @param params - Fields to update (all optional).
2543
+ * @returns The updated FilterRuleResponse.
2544
+ * @throws NotFoundError - Rule not found for this API key.
2545
+ * @throws ValidationError - Invalid field values (422).
2546
+ *
2547
+ * @example
2548
+ * ```typescript
2549
+ * const rule = await client.twitter.stream.updateFilterRule("550e8400-...", {
2550
+ * interval_seconds: 120,
2551
+ * });
2552
+ * ```
2553
+ */
2554
+ updateFilterRule(ruleId: string, params: FilterRuleUpdate): Promise<FilterRuleResponse>;
2555
+ /**
2556
+ * Delete a filter rule and all its associated logs. Irreversible.
2557
+ *
2558
+ * @param ruleId - UUID of the filter rule.
2559
+ * @throws NotFoundError - Rule not found for this API key.
2560
+ *
2561
+ * @example
2562
+ * ```typescript
2563
+ * await client.twitter.stream.deleteFilterRule("550e8400-...");
2564
+ * ```
2565
+ */
2566
+ deleteFilterRule(ruleId: string): Promise<void>;
2567
+ /**
2568
+ * Validate a Twitter search query before creating a rule.
2569
+ *
2570
+ * @param query - The Twitter search query to validate.
2571
+ * @returns FilterRuleValidateResponse with validity and sample result count.
2572
+ *
2573
+ * @example
2574
+ * ```typescript
2575
+ * const result = await client.twitter.stream.validateFilterRuleQuery(
2576
+ * "#python lang:en -is:retweet"
2577
+ * );
2578
+ * if (!result.valid) {
2579
+ * console.error("Invalid query:", result.error);
2580
+ * }
2581
+ * ```
2582
+ */
2583
+ validateFilterRuleQuery(query: string): Promise<FilterRuleValidateResponse>;
2584
+ /**
2585
+ * List tweet delivery logs for a specific filter rule.
2586
+ *
2587
+ * @param ruleId - UUID of the filter rule.
2588
+ * @param options - Filter and pagination options.
2589
+ * @returns FilterRuleDeliveryLogListResponse with pagination metadata.
2590
+ *
2591
+ * @example
2592
+ * ```typescript
2593
+ * const { logs, total } = await client.twitter.stream.listFilterRuleLogs(
2594
+ * "550e8400-...",
2595
+ * { limit: 50, deliveryStatus: "webhook_delivered" }
2596
+ * );
2597
+ * ```
2598
+ */
2599
+ listFilterRuleLogs(ruleId: string, options?: {
2600
+ limit?: number;
2601
+ offset?: number;
2602
+ deliveryStatus?: string;
2603
+ sort?: "asc" | "desc";
2604
+ }): Promise<FilterRuleDeliveryLogListResponse>;
2605
+ /**
2606
+ * Get all available filter rule pricing tiers.
2607
+ *
2608
+ * @returns FilterRulePricingTiersResponse listing all tiers.
2609
+ *
2610
+ * @example
2611
+ * ```typescript
2612
+ * const { tiers } = await client.twitter.stream.getFilterRulePricingTiers();
2613
+ * tiers.forEach((t) => console.log(t.tier_label, t.credits_per_rule_per_day));
2614
+ * ```
2615
+ */
2616
+ getFilterRulePricingTiers(): Promise<FilterRulePricingTiersResponse>;
2617
+ /**
2618
+ * Connect to the WebSocket stream and return an EventEmitter.
2619
+ *
2620
+ * The caller subscribes to events via `.on("tweet", handler)`.
2621
+ * The SDK handles pong replies to server pings automatically.
2622
+ * Call `.close()` on the emitter to disconnect cleanly.
2623
+ *
2624
+ * If reconnect is true, the emitter automatically reconnects after
2625
+ * disconnects (other than auth failures). A new "connected" event is
2626
+ * emitted on each reconnect.
2627
+ *
2628
+ * @param options - Connection options (reconnect, delay, maxReconnects).
2629
+ * @returns StreamEmitter -- an EventEmitter subclass.
2630
+ *
2631
+ * @example
2632
+ * ```typescript
2633
+ * const stream = client.twitter.stream.connect();
2634
+ * stream.on("connected", (e) => console.log("Connected:", e.connectionId));
2635
+ * stream.on("tweet", (event) => {
2636
+ * console.log(`@${event.authorUsername}: ${event.tweet.text}`);
2637
+ * console.log(` latency: ${event.latencyMs}ms`);
2638
+ * });
2639
+ * stream.on("error", (err) => console.error("Stream error:", err));
2640
+ * stream.on("close", () => console.log("Stream closed"));
2641
+ *
2642
+ * // Later:
2643
+ * stream.close();
2644
+ * ```
2645
+ */
2646
+ connect(options?: ConnectOptions): StreamEmitter;
2647
+ /**
2648
+ * Connect to the WebSocket stream and return an AsyncIterator.
2649
+ *
2650
+ * Iterates over StreamEvent objects. The SDK handles pong replies
2651
+ * automatically but still yields PingEvent to the caller (the caller
2652
+ * may ignore ping events).
2653
+ *
2654
+ * @param options - Connection options (reconnect, delay, maxReconnects).
2655
+ * @yields StreamEvent
2656
+ *
2657
+ * @example
2658
+ * ```typescript
2659
+ * for await (const event of client.twitter.stream.connectIter()) {
2660
+ * if (event.type === "tweet") {
2661
+ * console.log(`@${event.authorUsername}: ${event.latencyMs}ms`);
2662
+ * }
2663
+ * }
2664
+ * ```
2665
+ *
2666
+ * @example With auto-reconnect
2667
+ * ```typescript
2668
+ * for await (const event of client.twitter.stream.connectIter({
2669
+ * reconnect: true,
2670
+ * reconnectDelaySeconds: 90,
2671
+ * })) {
2672
+ * if (event.type === "tweet") {
2673
+ * // process(event);
2674
+ * }
2675
+ * }
2676
+ * ```
2677
+ */
2678
+ connectIter(options?: ConnectOptions): AsyncIterableIterator<StreamEvent>;
2679
+ }
2680
+ /**
2681
+ * Verify the HMAC-SHA256 signature of an incoming webhook request.
2682
+ *
2683
+ * The server sets the header:
2684
+ * X-ScrapeBadger-Signature: sha256=<hex-digest>
2685
+ *
2686
+ * @param secret - The webhook secret configured on the monitor.
2687
+ * @param body - The raw request body string or Buffer.
2688
+ * @param signatureHeader - The full X-ScrapeBadger-Signature header value.
2689
+ * @returns true if the signature is valid.
2690
+ *
2691
+ * @example
2692
+ * ```typescript
2693
+ * // In an Express webhook receiver:
2694
+ * import { verifyWebhookSignature } from "scrapebadger/twitter";
2695
+ * import express from "express";
2696
+ *
2697
+ * app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
2698
+ * const sig = req.headers["x-scrapebadger-signature"] as string;
2699
+ * if (!verifyWebhookSignature("my-secret", req.body, sig)) {
2700
+ * return res.status(401).json({ error: "Invalid signature" });
2701
+ * }
2702
+ * const event = JSON.parse(req.body.toString());
2703
+ * // process event...
2704
+ * res.sendStatus(200);
2705
+ * });
2706
+ * ```
2707
+ */
2708
+ declare function verifyWebhookSignature(secret: string, body: string | Buffer, signatureHeader: string): boolean;
2709
+
1744
2710
  /**
1745
2711
  * Twitter API client.
1746
2712
  *
@@ -1757,6 +2723,7 @@ declare class GeoClient {
1757
2723
  * - `communities` - Community operations (details, members, tweets, search, etc.)
1758
2724
  * - `trends` - Trending topics and locations
1759
2725
  * - `geo` - Geographic place information
2726
+ * - `stream` - Real-time stream monitor management and WebSocket streaming
1760
2727
  *
1761
2728
  * @example
1762
2729
  * ```typescript
@@ -1775,6 +2742,10 @@ declare class GeoClient {
1775
2742
  * for await (const tweet of client.twitter.tweets.searchAll("python")) {
1776
2743
  * console.log(tweet.text);
1777
2744
  * }
2745
+ *
2746
+ * // Stream live tweets
2747
+ * const stream = client.twitter.stream.connect();
2748
+ * stream.on("tweet", (event) => console.log(event.authorUsername));
1778
2749
  * ```
1779
2750
  */
1780
2751
  declare class TwitterClient {
@@ -1790,6 +2761,8 @@ declare class TwitterClient {
1790
2761
  readonly trends: TrendsClient;
1791
2762
  /** Client for geo/places operations */
1792
2763
  readonly geo: GeoClient;
2764
+ /** Client for real-time stream monitor management and WebSocket streaming */
2765
+ readonly stream: StreamClient;
1793
2766
  /**
1794
2767
  * Create a new Twitter client.
1795
2768
  *
@@ -1798,4 +2771,4 @@ declare class TwitterClient {
1798
2771
  constructor(client: BaseClient);
1799
2772
  }
1800
2773
 
1801
- export { type ApiResponse as A, BaseClient as B, CommunitiesClient as C, GeoClient as G, type Hashtag as H, type IteratorOptions as I, ListsClient as L, type Media as M, type PaginatedResponse as P, type QueryType as Q, type ResolvedConfig as R, type ScrapeBadgerConfig as S, TwitterClient as T, UsersClient as U, type PaginationOptions as a, TweetsClient as b, collectAll as c, TrendsClient as d, type GeoSearchOptions as e, type TrendCategory as f, type CommunityTweetType as g, type PollOption as h, type Poll as i, type Url as j, type UserMention as k, type TweetPlace as l, type Tweet as m, type User as n, type UserAbout as o, type UserIds as p, type List as q, type CommunityBanner as r, type CommunityRule as s, type Community as t, type CommunityMember as u, type Trend as v, type Location as w, type PlaceTrends as x, type Place as y, type ListResponse as z };
2774
+ export { type ListResponse as $, AuthenticationError as A, BaseClient as B, ConflictError as C, type List as D, type CommunityBanner as E, type CommunityRule as F, GeoClient as G, type Hashtag as H, InsufficientCreditsError as I, type Community as J, type CommunityMember as K, ListsClient as L, type Media as M, NotFoundError as N, type Trend as O, type PaginatedResponse as P, type QueryType as Q, type ResolvedConfig as R, type ScrapeBadgerConfig as S, TwitterClient as T, UsersClient as U, ValidationError as V, WebSocketStreamError as W, type Location as X, type PlaceTrends as Y, type Place as Z, type ApiResponse as _, ScrapeBadgerError as a, type MonitorStatus as a0, type StreamMonitor as a1, type StreamMonitorList as a2, type CreateMonitorParams as a3, type UpdateMonitorParams as a4, type StreamTweet as a5, type ConnectedEvent as a6, type PingEvent as a7, type TweetEvent as a8, type ErrorEvent as a9, type StreamEvent as aa, type StreamEventType as ab, type DeliveryLog as ac, type DeliveryLogList as ad, type BillingLog as ae, type BillingLogList as af, type ConnectOptions as ag, type FilterRuleStatus as ah, type FilterRuleResponse as ai, type FilterRuleCreate as aj, type FilterRuleUpdate as ak, type FilterRulePricingTier as al, type FilterRuleListResponse as am, type FilterRuleValidateResponse as an, type FilterRuleDeliveryLog as ao, type FilterRuleDeliveryLogListResponse as ap, type FilterRulePricingTiersResponse as aq, RateLimitError as b, ServerError as c, TimeoutError as d, AccountRestrictedError as e, type PaginationOptions as f, type IteratorOptions as g, collectAll as h, TweetsClient as i, CommunitiesClient as j, TrendsClient as k, type GeoSearchOptions as l, StreamClient as m, type StreamEmitter as n, type TrendCategory as o, type CommunityTweetType as p, type PollOption as q, type Poll as r, type Url as s, type UserMention as t, type TweetPlace as u, verifyWebhookSignature as v, type Tweet as w, type User as x, type UserAbout as y, type UserIds as z };