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