diff --git a/doc/rate-limiting.md b/doc/rate-limiting.md index 47843f6b..559c5d2e 100644 --- a/doc/rate-limiting.md +++ b/doc/rate-limiting.md @@ -32,6 +32,9 @@ const manualFullResponse = await client.v1.get('statuses/ manualFullResponse.data; // TweetV1TimelineResult // Rate limit information manualFullResponse.rateLimit; // { limit: number, remaining: number, reset: number } +// Optional daily limits +manualFullResponse.rateLimit.day; // Application 24h limit (if provided) +manualFullResponse.rateLimit.userDay; // User 24h limit (if provided) ``` ## Handle errors - Everywhere in this library diff --git a/src/client-mixins/request-handler.helper.ts b/src/client-mixins/request-handler.helper.ts index 5d59f9dc..bb157aa3 100644 --- a/src/client-mixins/request-handler.helper.ts +++ b/src/client-mixins/request-handler.helper.ts @@ -213,6 +213,14 @@ export class RequestHandlerHelper { }; } + if (res.headers['x-user-limit-24hour-limit']) { + rateLimit.userDay = { + limit: Number(res.headers['x-user-limit-24hour-limit']), + remaining: Number(res.headers['x-user-limit-24hour-remaining']), + reset: Number(res.headers['x-user-limit-24hour-reset']), + }; + } + if (this.requestData.rateLimitSaver) { this.requestData.rateLimitSaver(rateLimit); } diff --git a/src/types/responses.types.ts b/src/types/responses.types.ts index e9231e41..f147dc8d 100644 --- a/src/types/responses.types.ts +++ b/src/types/responses.types.ts @@ -14,4 +14,5 @@ export interface SingleTwitterRateLimit { export interface TwitterRateLimit extends SingleTwitterRateLimit { day?: SingleTwitterRateLimit; + userDay?: SingleTwitterRateLimit; } diff --git a/test/rate-limit.test.ts b/test/rate-limit.test.ts new file mode 100644 index 00000000..2dce4e08 --- /dev/null +++ b/test/rate-limit.test.ts @@ -0,0 +1,39 @@ +import 'mocha'; +import { expect } from 'chai'; +import { RequestHandlerHelper } from '../src/client-mixins/request-handler.helper'; +import type { IncomingMessage } from 'http'; + +class TestRequestHandlerHelper extends RequestHandlerHelper { + public parse(res: IncomingMessage) { + return this.getRateLimitFromResponse(res); + } +} + +describe('Rate limit parsing', () => { + it('includes user 24-hour limits when present', () => { + const helper = new TestRequestHandlerHelper({ + url: new URL('https://example.com'), + options: {}, + } as any); + + const res = { + headers: { + 'x-rate-limit-limit': '15', + 'x-rate-limit-remaining': '14', + 'x-rate-limit-reset': '100', + 'x-app-limit-24hour-limit': '5000', + 'x-app-limit-24hour-remaining': '4999', + 'x-app-limit-24hour-reset': '200', + 'x-user-limit-24hour-limit': '1000', + 'x-user-limit-24hour-remaining': '999', + 'x-user-limit-24hour-reset': '300', + }, + } as unknown as IncomingMessage; + + const rateLimit = helper.parse(res)!; + expect(rateLimit.limit).to.equal(15); + expect(rateLimit.day).to.deep.equal({ limit: 5000, remaining: 4999, reset: 200 }); + expect(rateLimit.userDay).to.deep.equal({ limit: 1000, remaining: 999, reset: 300 }); + }); +}); +