@manaobot/kick 1.0.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.
Files changed (47) hide show
  1. package/README.md +163 -0
  2. package/biome.json +44 -0
  3. package/bun.lock +78 -0
  4. package/example/01-authorize-bot/.env.example +7 -0
  5. package/example/01-authorize-bot/index.ts +60 -0
  6. package/example/02-webhook/.env.example +7 -0
  7. package/example/02-webhook/index.ts +80 -0
  8. package/example/03-ngrok/.env.example +7 -0
  9. package/example/03-ngrok/index.ts +92 -0
  10. package/example/04-categories-api/.env.example +7 -0
  11. package/example/04-categories-api/index.ts +77 -0
  12. package/example/05-users-api/.env.example +7 -0
  13. package/example/05-users-api/index.ts +60 -0
  14. package/example/06-channels-api/.env.example +7 -0
  15. package/example/06-channels-api/index.ts +60 -0
  16. package/example/07-channel-rewards-api/.env.example +7 -0
  17. package/example/07-channel-rewards-api/index.ts +60 -0
  18. package/example/08-basic-chat-bot/.env.example +7 -0
  19. package/example/08-basic-chat-bot/index.ts +102 -0
  20. package/package.json +23 -0
  21. package/qodana.yaml +31 -0
  22. package/src/KickClient.ts +172 -0
  23. package/src/Logger.ts +25 -0
  24. package/src/api/CategoriesAPI.ts +45 -0
  25. package/src/api/ChannelRewardsAPI.ts +121 -0
  26. package/src/api/ChannelsAPI.ts +63 -0
  27. package/src/api/KicksAPI.ts +37 -0
  28. package/src/api/LivestreamsAPI.ts +65 -0
  29. package/src/api/ModerationAPI.ts +60 -0
  30. package/src/api/UsersAPI.ts +72 -0
  31. package/src/auth/AuthManager.ts +64 -0
  32. package/src/auth/CallbackServer.ts +57 -0
  33. package/src/auth/OAuth.ts +55 -0
  34. package/src/auth/PKCE.ts +13 -0
  35. package/src/auth/TokenManager.ts +53 -0
  36. package/src/chat/ChatClient.ts +48 -0
  37. package/src/rest/RestClient.ts +39 -0
  38. package/src/webhooks/NgrokAdapter.ts +46 -0
  39. package/src/webhooks/WebhookRouter.ts +135 -0
  40. package/src/webhooks/WebhookServer.ts +41 -0
  41. package/tsconfig.json +29 -0
  42. package/types/api.d.ts +158 -0
  43. package/types/auth.d.ts +38 -0
  44. package/types/chat.d.ts +14 -0
  45. package/types/client.d.ts +67 -0
  46. package/types/index.d.ts +4 -0
  47. package/types/webhooks.d.ts +35 -0
@@ -0,0 +1,121 @@
1
+ import type { RestClient } from "../rest/RestClient";
2
+ import type {
3
+ KickChannelReward,
4
+ ChannelRewardBase,
5
+ GetChannelRewardsResponse,
6
+ GetRedemptionsParams,
7
+ GetRedemptionsResponse,
8
+ RedemptionActionResponse,
9
+ } from "../../types/api";
10
+
11
+ /**
12
+ * ChannelRewardsAPI provides access to Kick Channel Rewards APIs.
13
+ *
14
+ * Requires scopes:
15
+ * - channel:rewards:read
16
+ * - channel:rewards:write
17
+ *
18
+ * @example
19
+ * const rewards = await kick.api.channelRewards.get();
20
+ */
21
+ export class ChannelRewardsAPI {
22
+ constructor(private readonly rest: RestClient) {}
23
+
24
+ /**
25
+ * Get all channel rewards for the authorized broadcaster.
26
+ */
27
+ async get(): Promise<GetChannelRewardsResponse> {
28
+ return this.rest.fetch("/public/v1/channels/rewards", {
29
+ method: "GET",
30
+ });
31
+ }
32
+
33
+ /**
34
+ * Create a new channel reward.
35
+ */
36
+ async create(
37
+ body: ChannelRewardBase,
38
+ ): Promise<{ data: KickChannelReward; message: string }> {
39
+ return this.rest.fetch("/public/v1/channels/rewards", {
40
+ method: "POST",
41
+ headers: {
42
+ "Content-Type": "application/json",
43
+ },
44
+ body: JSON.stringify(body),
45
+ });
46
+ }
47
+
48
+ /**
49
+ * Update an existing reward.
50
+ *
51
+ * Only the creating app can update a reward.
52
+ */
53
+ async update(
54
+ id: string,
55
+ body: Partial<ChannelRewardBase>,
56
+ ): Promise<{ data: KickChannelReward; message: string }> {
57
+ return this.rest.fetch(`/public/v1/channels/rewards/${id}`, {
58
+ method: "PATCH",
59
+ headers: {
60
+ "Content-Type": "application/json",
61
+ },
62
+ body: JSON.stringify(body),
63
+ });
64
+ }
65
+
66
+ /**
67
+ * Delete a channel reward.
68
+ */
69
+ async delete(id: string): Promise<void> {
70
+ await this.rest.fetch(`/public/v1/channels/rewards/${id}`, {
71
+ method: "DELETE",
72
+ });
73
+ }
74
+
75
+ /**
76
+ * Retrieve reward redemptions.
77
+ */
78
+ async getRedemptions(
79
+ params: GetRedemptionsParams = {},
80
+ ): Promise<GetRedemptionsResponse> {
81
+ const search = new URLSearchParams();
82
+
83
+ if (params.reward_id) search.append("reward_id", params.reward_id);
84
+ if (params.status) search.append("status", params.status);
85
+ if (params.cursor) search.append("cursor", params.cursor);
86
+ params.id?.forEach((v) => search.append("id", v));
87
+
88
+ const query = search.toString();
89
+
90
+ return this.rest.fetch(
91
+ `/public/v1/channels/rewards/redemptions${query ? `?${query}` : ""}`,
92
+ { method: "GET" },
93
+ );
94
+ }
95
+
96
+ /**
97
+ * Accept redemptions (max 25).
98
+ */
99
+ async accept(ids: string[]): Promise<RedemptionActionResponse> {
100
+ return this.rest.fetch("/public/v1/channels/rewards/redemptions/accept", {
101
+ method: "POST",
102
+ headers: {
103
+ "Content-Type": "application/json",
104
+ },
105
+ body: JSON.stringify({ ids }),
106
+ });
107
+ }
108
+
109
+ /**
110
+ * Reject redemptions (max 25).
111
+ */
112
+ async reject(ids: string[]): Promise<RedemptionActionResponse> {
113
+ return this.rest.fetch("/public/v1/channels/rewards/redemptions/reject", {
114
+ method: "POST",
115
+ headers: {
116
+ "Content-Type": "application/json",
117
+ },
118
+ body: JSON.stringify({ ids }),
119
+ });
120
+ }
121
+ }
@@ -0,0 +1,63 @@
1
+ import type { RestClient } from "../rest/RestClient";
2
+ import type { KickChannel } from "../../types/api";
3
+
4
+ /**
5
+ * ChannelsAPI provides access to Kick Channel APIs.
6
+ *
7
+ * @example
8
+ * const channels = await kick.api.channels.get();
9
+ * const channels = await kick.api.channels.get({ slug: ["manaobot"] });
10
+ */
11
+ export class ChannelsAPI {
12
+ constructor(private readonly rest: RestClient) {}
13
+
14
+ /**
15
+ * Retrieve channel information.
16
+ *
17
+ * - Provided no params, returns current authorized channel
18
+ * - Provide broadcaster_user_id[] OR slug[] only, not both
19
+ */
20
+ async get(params?: {
21
+ broadcaster_user_id?: number[];
22
+ slug?: string[];
23
+ }): Promise<{ data: KickChannel[] }> {
24
+ const search = new URLSearchParams();
25
+
26
+ if (params?.broadcaster_user_id && params?.slug) {
27
+ throw new Error(
28
+ "ChannelsClient.get(): cannot mix broadcaster_user_id and slug",
29
+ );
30
+ }
31
+
32
+ params?.broadcaster_user_id?.forEach((id) =>
33
+ search.append("broadcaster_user_id", String(id)),
34
+ );
35
+
36
+ params?.slug?.forEach((s) => search.append("slug", s));
37
+
38
+ const query = search.toString();
39
+
40
+ return this.rest.fetch(`/public/v1/channels${query ? `?${query}` : ""}`, {
41
+ method: "GET",
42
+ });
43
+ }
44
+
45
+ /**
46
+ * Update livestream metadata for the authorized channel.
47
+ *
48
+ * Requires scope: channel:write
49
+ */
50
+ async update(body: {
51
+ category_id?: number;
52
+ custom_tags?: string[];
53
+ stream_title?: string;
54
+ }): Promise<void> {
55
+ await this.rest.fetch(`/public/v1/channels`, {
56
+ method: "PATCH",
57
+ headers: {
58
+ "Content-Type": "application/json",
59
+ },
60
+ body: JSON.stringify(body),
61
+ });
62
+ }
63
+ }
@@ -0,0 +1,37 @@
1
+ import type { RestClient } from "../rest/RestClient";
2
+ import type { KickLeaderboardResponse } from "../../types/api";
3
+
4
+ /**
5
+ * KicksAPI provides access to Kick KICKs APIs.
6
+ *
7
+ * Required scope:
8
+ * - kicks:read
9
+ *
10
+ * @example
11
+ * const leaderboard = await kick.api.kicks.getLeaderboard({ top: 10 });
12
+ */
13
+ export class KicksAPI {
14
+ constructor(private readonly rest: RestClient) {}
15
+
16
+ /**
17
+ * Get KICKs leaderboard for the authenticated broadcaster.
18
+ *
19
+ * @param params Optional query params
20
+ */
21
+ async getLeaderboard(params?: {
22
+ top?: number;
23
+ }): Promise<KickLeaderboardResponse> {
24
+ const search = new URLSearchParams();
25
+
26
+ if (params?.top) {
27
+ search.set("top", String(params.top));
28
+ }
29
+
30
+ const query = search.toString();
31
+
32
+ return this.rest.fetch(
33
+ `/public/v1/kicks/leaderboard${query ? `?${query}` : ""}`,
34
+ { method: "GET" },
35
+ );
36
+ }
37
+ }
@@ -0,0 +1,65 @@
1
+ import type { RestClient } from "../rest/RestClient";
2
+ import type {
3
+ KickLivestream,
4
+ KickLivestreamStatsResponse,
5
+ KickLivestreamsResponse,
6
+ } from "../../types/api";
7
+
8
+ /**
9
+ * LivestreamsAPI provides access to Kick Livestream APIs.
10
+ *
11
+ * @example
12
+ * const streams = await kick.api.livestreams.get({ limit: 10 });
13
+ */
14
+ export class LivestreamsAPI {
15
+ constructor(private readonly rest: RestClient) {}
16
+
17
+ /**
18
+ * Get livestreams with optional filters.
19
+ *
20
+ * Supported filters:
21
+ * - broadcaster_user_id[]
22
+ * - category_id
23
+ * - language
24
+ * - limit
25
+ * - sort ("viewer_count" | "started_at")
26
+ */
27
+ async get(params?: {
28
+ broadcaster_user_id?: number[];
29
+ category_id?: number;
30
+ language?: string;
31
+ limit?: number;
32
+ sort?: "viewer_count" | "started_at";
33
+ }): Promise<KickLivestreamsResponse> {
34
+ const search = new URLSearchParams();
35
+
36
+ params?.broadcaster_user_id?.forEach((id) =>
37
+ search.append("broadcaster_user_id", String(id)),
38
+ );
39
+
40
+ if (params?.category_id)
41
+ search.set("category_id", String(params.category_id));
42
+ if (params?.language) search.set("language", params.language);
43
+ if (params?.limit) search.set("limit", String(params.limit));
44
+ if (params?.sort) search.set("sort", params.sort);
45
+
46
+ const query = search.toString();
47
+
48
+ return this.rest.fetch(
49
+ `/public/v1/livestreams${query ? `?${query}` : ""}`,
50
+ { method: "GET" },
51
+ );
52
+ }
53
+
54
+ /**
55
+ * Get livestream statistics.
56
+ *
57
+ * @example
58
+ * const stats = await kick.api.livestreams.getStats();
59
+ */
60
+ async getStats(): Promise<KickLivestreamStatsResponse> {
61
+ return this.rest.fetch("/public/v1/livestreams/stats", {
62
+ method: "GET",
63
+ });
64
+ }
65
+ }
@@ -0,0 +1,60 @@
1
+ import type { RestClient } from "../rest/RestClient";
2
+ import type {
3
+ ModerationBanRequest,
4
+ ModerationUnbanRequest,
5
+ KickOkResponse,
6
+ } from "../../types/api";
7
+
8
+ /**
9
+ * ModerationAPI provides access to Kick Moderation APIs.
10
+ *
11
+ * Required scope:
12
+ * - moderation:ban
13
+ *
14
+ * @example
15
+ * await kick.api.moderation.ban({ broadcaster_user_id: 123, user_id: 333, reason: "Not vibing" });
16
+ * await kick.api.moderation.timeout({ broadcaster_user_id: 123, user_id: 333, duration: 10 });
17
+ * await kick.api.moderation.unban({ broadcaster_user_id: 123, user_id: 333 });
18
+ */
19
+ export class ModerationAPI {
20
+ constructor(private readonly rest: RestClient) {}
21
+
22
+ /**
23
+ * Ban a user (permanent ban).
24
+ * Omit `duration (minutes)` to ban permanently.
25
+ */
26
+ async ban(
27
+ body: Omit<ModerationBanRequest, "duration">,
28
+ ): Promise<KickOkResponse> {
29
+ return this.rest.fetch("/public/v1/moderation/bans", {
30
+ method: "POST",
31
+ body: JSON.stringify(body),
32
+ });
33
+ }
34
+
35
+ /**
36
+ * Timeout a user for `duration` minutes.
37
+ */
38
+ async timeout(body: ModerationBanRequest): Promise<KickOkResponse> {
39
+ if (typeof body.duration !== "number") {
40
+ throw new Error(
41
+ "ModerationAPI.timeout(): duration is required (minutes)",
42
+ );
43
+ }
44
+
45
+ return this.rest.fetch("/public/v1/moderation/bans", {
46
+ method: "POST",
47
+ body: JSON.stringify(body),
48
+ });
49
+ }
50
+
51
+ /**
52
+ * Unban a user OR remove an active timeout.
53
+ */
54
+ async unban(body: ModerationUnbanRequest): Promise<KickOkResponse> {
55
+ return this.rest.fetch("/public/v1/moderation/bans", {
56
+ method: "DELETE",
57
+ body: JSON.stringify(body),
58
+ });
59
+ }
60
+ }
@@ -0,0 +1,72 @@
1
+ import type { RestClient } from "../rest/RestClient";
2
+
3
+ /**
4
+ * Parameters for retrieving users.
5
+ */
6
+ export interface GetUsersParams {
7
+ /**
8
+ * User IDs to retrieve.
9
+ * If omitted, returns the currently authorized user.
10
+ */
11
+ id?: number[];
12
+ }
13
+
14
+ /**
15
+ * Kick User object returned by the API.
16
+ */
17
+ export interface KickUser {
18
+ email?: string;
19
+ name: string;
20
+ profile_picture: string;
21
+ user_id: number;
22
+ }
23
+
24
+ /**
25
+ * Response returned from GET /public/v1/users
26
+ */
27
+ export interface GetUsersResponse {
28
+ data: KickUser[];
29
+ message: string;
30
+ }
31
+
32
+ /**
33
+ * UsersAPI provides access to Kick User APIs.
34
+ *
35
+ * @example
36
+ * const users = await kick.api.users.get();
37
+ * console.log(users.data);
38
+ */
39
+ export class UsersAPI {
40
+ constructor(private readonly rest: RestClient) {}
41
+
42
+ /**
43
+ * Retrieve user information.
44
+ *
45
+ * If no IDs are provided, the currently authorized user is returned.
46
+ *
47
+ * Requires scope:
48
+ * - user:read
49
+ *
50
+ * @param params Query parameters
51
+ * @returns Kick user data
52
+ *
53
+ * @example
54
+ * // Get current user
55
+ * const me = await kick.api.users.get();
56
+ *
57
+ * @example
58
+ * // Get specific users
59
+ * const users = await kick.api.users.get({ id: [123, 456] });
60
+ */
61
+ async get(params: GetUsersParams = {}): Promise<GetUsersResponse> {
62
+ const search = new URLSearchParams();
63
+
64
+ params.id?.forEach((v) => search.append("id", String(v)));
65
+
66
+ const query = search.toString();
67
+
68
+ return this.rest.fetch(`/public/v1/users${query ? `?${query}` : ""}`, {
69
+ method: "GET",
70
+ });
71
+ }
72
+ }
@@ -0,0 +1,64 @@
1
+ import { callbackServer } from "./CallbackServer.ts";
2
+ import type { KickClient } from "../KickClient";
3
+
4
+ type AuthorizedHandler = () => void | Promise<void>;
5
+
6
+ /**
7
+ * AuthManager handles authentication lifecycle events and local callback servers.
8
+ * @example
9
+ * ```ts
10
+ * kick.auth.onAuthorized(() => {
11
+ * console.log("Authenticated and ready!");
12
+ * });
13
+ * ```
14
+ */
15
+ export class AuthManager {
16
+ private handlers = new Set<AuthorizedHandler>();
17
+ private readonly client: KickClient;
18
+
19
+ constructor(client: KickClient) {
20
+ this.client = client;
21
+ }
22
+
23
+ /**
24
+ * Register a callback to be executed when the client is authorized.
25
+ * @param handler Function to run on authorization
26
+ */
27
+ onAuthorized(handler: AuthorizedHandler) {
28
+ this.handlers.add(handler);
29
+ }
30
+
31
+ /**
32
+ * Trigger all registered authorization handlers.
33
+ * Internal use only.
34
+ */
35
+ async emitAuthorized() {
36
+ for (const handler of this.handlers) {
37
+ await handler();
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Returns a promise that resolves once the client is authorized.
43
+ * @example
44
+ * await kick.auth.waitForAuthorization();
45
+ * console.log("Authorized!");
46
+ */
47
+ waitForAuthorization(): Promise<void> {
48
+ return new Promise((resolve) => {
49
+ this.onAuthorized(() => resolve());
50
+ });
51
+ }
52
+
53
+ /**
54
+ * Creates a temporary local server to handle OAuth2 redirects.
55
+ * @param options Server configuration
56
+ * @returns {Bun.Server<undefined>} Callback server instance
57
+ */
58
+ createCallbackServer(options?: {
59
+ port?: number;
60
+ path?: string;
61
+ }): Bun.Server<undefined> {
62
+ return callbackServer(this.client, options);
63
+ }
64
+ }
@@ -0,0 +1,57 @@
1
+ import type { KickClient } from "../KickClient";
2
+ import { logger } from "../Logger.ts";
3
+
4
+ /**
5
+ * Configuration options for the local callback server.
6
+ */
7
+ interface CallbackServerOptions {
8
+ /** @default 3000 */
9
+ port?: number;
10
+ /** @default "/callback" */
11
+ path?: string;
12
+ }
13
+
14
+ export function callbackServer(
15
+ client: KickClient,
16
+ options: CallbackServerOptions = {},
17
+ ): Bun.Server<undefined> {
18
+ const port = options.port ?? 3000;
19
+ const path = options.path ?? "/callback";
20
+
21
+ const server = Bun.serve({
22
+ port,
23
+ reusePort: true,
24
+ async fetch(req) {
25
+ const url = new URL(req.url);
26
+
27
+ if (req.method !== "GET" || url.pathname !== path) {
28
+ return new Response(null, { status: 404 });
29
+ }
30
+
31
+ const code = url.searchParams.get("code");
32
+ const state = url.searchParams.get("state");
33
+
34
+ if (!code || state !== client.getState()) {
35
+ logger.error("Invalid OAuth callback: state mismatch or missing code");
36
+ return new Response("Invalid OAuth callback", { status: 400 });
37
+ }
38
+
39
+ try {
40
+ await client.exchangeCode(code);
41
+ await client.auth.emitAuthorized();
42
+
43
+ return new Response("Authorization success, you can close this tab.", {
44
+ status: 200,
45
+ headers: { "Content-Type": "text/plain" },
46
+ });
47
+ } catch (err) {
48
+ logger.error("OAuth exchange failed", err);
49
+ return new Response("Authorization failed", { status: 500 });
50
+ }
51
+ },
52
+ });
53
+
54
+ logger.success("Callback server started", { port, path });
55
+
56
+ return server;
57
+ }
@@ -0,0 +1,55 @@
1
+ import type { KickTokenResponse } from "../../types";
2
+
3
+ export async function exchangeCode(
4
+ code: string,
5
+ verifier: string,
6
+ clientId: string,
7
+ clientSecret: string,
8
+ redirectUri: string,
9
+ ): Promise<KickTokenResponse> {
10
+ const params = new URLSearchParams({
11
+ grant_type: "authorization_code",
12
+ code,
13
+ redirect_uri: redirectUri,
14
+ client_id: clientId,
15
+ client_secret: clientSecret,
16
+ code_verifier: verifier,
17
+ });
18
+
19
+ const res = await fetch("https://id.kick.com/oauth/token", {
20
+ method: "POST",
21
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
22
+ body: params.toString(),
23
+ });
24
+
25
+ if (!res.ok) {
26
+ throw new Error(`Code exchange failed (${res.status})`);
27
+ }
28
+
29
+ return res.json() as Promise<KickTokenResponse>;
30
+ }
31
+
32
+ export async function refreshToken(
33
+ refreshToken: string,
34
+ clientId: string,
35
+ clientSecret: string,
36
+ ): Promise<KickTokenResponse> {
37
+ const params = new URLSearchParams({
38
+ grant_type: "refresh_token",
39
+ refresh_token: refreshToken,
40
+ client_id: clientId,
41
+ client_secret: clientSecret,
42
+ });
43
+
44
+ const res = await fetch("https://id.kick.com/oauth/token", {
45
+ method: "POST",
46
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
47
+ body: params.toString(),
48
+ });
49
+
50
+ if (!res.ok) {
51
+ throw new Error(`Token refresh failed (${res.status})`);
52
+ }
53
+
54
+ return res.json() as Promise<KickTokenResponse>;
55
+ }
@@ -0,0 +1,13 @@
1
+ export function createPkce(): {
2
+ verifier: string;
3
+ challenge: string;
4
+ } {
5
+ const verifier = Buffer.from(
6
+ crypto.getRandomValues(new Uint8Array(32)),
7
+ ).toString("base64url");
8
+ const challenge = new Bun.CryptoHasher("sha256")
9
+ .update(verifier)
10
+ .digest("base64url");
11
+
12
+ return { verifier, challenge };
13
+ }
@@ -0,0 +1,53 @@
1
+ import type { KickTokenResponse } from "../../types";
2
+
3
+ type RefreshFn = (refreshToken: string) => Promise<KickTokenResponse>;
4
+ type TokenUpdateCallback = (tokens: KickTokenResponse) => void | Promise<void>;
5
+
6
+ export class TokenManager {
7
+ private accessToken?: string;
8
+ private refreshToken?: string;
9
+ private expiresAt?: number;
10
+ private readonly onUpdate?: TokenUpdateCallback;
11
+
12
+ private readonly refreshFn: RefreshFn;
13
+ private refreshThresholdMs = 60_000; // refresh 60s before expiry
14
+
15
+ constructor(refreshFn: RefreshFn, onUpdate?: TokenUpdateCallback) {
16
+ this.refreshFn = refreshFn;
17
+ this.onUpdate = onUpdate;
18
+ }
19
+
20
+ setTokens(token: KickTokenResponse) {
21
+ this.accessToken = token.access_token;
22
+ this.refreshToken = token.refresh_token;
23
+
24
+ this.onUpdate?.(token);
25
+ }
26
+
27
+ async getAccessToken(): Promise<string> {
28
+ if (!this.accessToken) {
29
+ throw new Error("TokenManager not initialized");
30
+ }
31
+
32
+ if (!this.expiresAt) {
33
+ return this.accessToken;
34
+ }
35
+
36
+ const now = Date.now();
37
+
38
+ if (now >= this.expiresAt - this.refreshThresholdMs) {
39
+ await this.refresh();
40
+ }
41
+
42
+ return this.accessToken;
43
+ }
44
+
45
+ private async refresh() {
46
+ if (!this.refreshToken) {
47
+ throw new Error("No refresh token available");
48
+ }
49
+
50
+ const newToken = await this.refreshFn(this.refreshToken);
51
+ this.setTokens(newToken);
52
+ }
53
+ }