@kash-88/alerts 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.
- package/dist/example/getAuthorizeLink.d.ts +1 -0
- package/dist/example/getAuthorizeLink.js +13 -0
- package/dist/example/getOauthToken.d.ts +1 -0
- package/dist/example/getOauthToken.js +16 -0
- package/dist/example/getUser.d.ts +1 -0
- package/dist/example/getUser.js +14 -0
- package/dist/example/getUserChannel.d.ts +1 -0
- package/dist/example/getUserChannel.js +12 -0
- package/dist/example/updateAccessToken.d.ts +1 -0
- package/dist/example/updateAccessToken.js +16 -0
- package/dist/example/wsExample.d.ts +1 -0
- package/dist/example/wsExample.js +42 -0
- package/dist/src/example/getAuthorizeLink.d.ts +1 -0
- package/dist/src/example/getAuthorizeLink.js +13 -0
- package/dist/src/example/getOauthToken.d.ts +1 -0
- package/dist/src/example/getOauthToken.js +16 -0
- package/dist/src/example/getUser.d.ts +1 -0
- package/dist/src/example/getUser.js +14 -0
- package/dist/src/example/getUserChannel.d.ts +1 -0
- package/dist/src/example/getUserChannel.js +12 -0
- package/dist/src/example/updateAccessToken.d.ts +1 -0
- package/dist/src/example/updateAccessToken.js +16 -0
- package/dist/src/example/wsExample.d.ts +1 -0
- package/dist/src/example/wsExample.js +42 -0
- package/dist/src/func/getAuthorizeLink.d.ts +18 -0
- package/dist/src/func/getAuthorizeLink.js +29 -0
- package/dist/src/func/getOauthToken.d.ts +27 -0
- package/dist/src/func/getOauthToken.js +42 -0
- package/dist/src/func/getPrivateToken.d.ts +29 -0
- package/dist/src/func/getPrivateToken.js +46 -0
- package/dist/src/func/getUser.d.ts +23 -0
- package/dist/src/func/getUser.js +38 -0
- package/dist/src/func/getUserChannel.d.ts +19 -0
- package/dist/src/func/getUserChannel.js +32 -0
- package/dist/src/func/updateAccessToken.d.ts +24 -0
- package/dist/src/func/updateAccessToken.js +39 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/index.js +9 -0
- package/dist/src/types.d.ts +33 -0
- package/dist/src/types.js +1 -0
- package/dist/src/utils.d.ts +21 -0
- package/dist/src/utils.js +48 -0
- package/dist/src/ws/CentrifugeClient.d.ts +31 -0
- package/dist/src/ws/CentrifugeClient.js +71 -0
- package/package.json +59 -0
- package/readme.md +82 -0
- package/src/example/getAuthorizeLink.ts +15 -0
- package/src/example/getOauthToken.ts +18 -0
- package/src/example/getUser.ts +16 -0
- package/src/example/getUserChannel.ts +14 -0
- package/src/example/updateAccessToken.ts +18 -0
- package/src/example/wsExample.ts +52 -0
- package/src/func/getAuthorizeLink.ts +33 -0
- package/src/func/getOauthToken.ts +46 -0
- package/src/func/getPrivateToken.ts +53 -0
- package/src/func/getUser.ts +42 -0
- package/src/func/getUserChannel.ts +34 -0
- package/src/func/updateAccessToken.ts +43 -0
- package/src/index.ts +19 -0
- package/src/types.ts +38 -0
- package/src/utils.ts +56 -0
- package/src/ws/CentrifugeClient.ts +108 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { GetPrivateToken } from "@type";
|
|
3
|
+
import { validateDataObject } from "@utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Gets a private token for subscribing to a DonationAlerts channel via Centrifuge.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { connectPrivateToken } from "@kash-88/alerts";
|
|
10
|
+
*
|
|
11
|
+
* // The "channel" can be obtained using the "getUserChannel" function,
|
|
12
|
+
* // and the "uuidv4_client_id" is provided during authentication in WebSocket (ws).
|
|
13
|
+
*
|
|
14
|
+
* try {
|
|
15
|
+
* const token = await connectPrivateToken({
|
|
16
|
+
* channel: "USER_CHANNEL",
|
|
17
|
+
* uuidv4_client_id: "USER_uuid_ID",
|
|
18
|
+
* access_token: "USER_ACCESS_TOKEN"
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* console.log(token);
|
|
22
|
+
* } catch (error) {
|
|
23
|
+
* console.error("Error getting Oauth token:", error.response.data);
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* @param {Object} params - Parameters for getting the token.
|
|
27
|
+
* @param {string} params.channel - Channel name to subscribe.
|
|
28
|
+
* @param {string} params.uuidv4_client_id - UUID v4 client ID.
|
|
29
|
+
* @param {string} params.access_token - OAuth access token of the user.
|
|
30
|
+
* @returns {Promise<string>} - Token for channel subscription.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export async function getPrivateToken(data: GetPrivateToken): Promise<string> {
|
|
34
|
+
try {
|
|
35
|
+
validateDataObject(data, ["channel", "uuidv4_client_id", "access_token"]);
|
|
36
|
+
|
|
37
|
+
const response = await axios.post(
|
|
38
|
+
"https://www.donationalerts.com/api/v1/centrifuge/subscribe",
|
|
39
|
+
{
|
|
40
|
+
channels: [data.channel],
|
|
41
|
+
client: data.uuidv4_client_id
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
headers: {
|
|
45
|
+
"Authorization": `Bearer ${data.access_token}`
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
return response.data.channels[0].token;
|
|
50
|
+
} catch (error: any) {
|
|
51
|
+
throw new Error(error?.response?.data?.error_description || error?.message || error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { User } from "@type";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Fetches the profile information for the authenticated user.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* import { getUser } from "@kash.88/alerts";
|
|
9
|
+
*
|
|
10
|
+
* // Get an access_token using getOauthToken or updateAccessToken
|
|
11
|
+
*
|
|
12
|
+
* try {
|
|
13
|
+
* const accessToken = "USER_ACCESS_TOKEN";
|
|
14
|
+
* const user = await getUser(accessToken);
|
|
15
|
+
*
|
|
16
|
+
* console.log(user);
|
|
17
|
+
* } catch (error) {
|
|
18
|
+
* console.error("Error fetching user:", error.response.data);
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* @param {string} access_token - The access token for authentication.
|
|
22
|
+
* @returns {Promise<User>} A promise that resolves to the user profile data.
|
|
23
|
+
* @see {@link https://www.donationalerts.com/apidoc#api_v1__users__user_profile_information}
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
export default async function getUser(access_token: string): Promise<User> {
|
|
27
|
+
try {
|
|
28
|
+
if (!access_token) {
|
|
29
|
+
throw new Error("You must provide \"access_token\" as a non-empty string.");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const response = await axios.get<{ data: User }>("https://www.donationalerts.com/api/v1/user/oauth", {
|
|
33
|
+
headers: {
|
|
34
|
+
Authorization: `Bearer ${access_token}`
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return response.data.data;
|
|
39
|
+
} catch (error: any) {
|
|
40
|
+
throw new Error(error?.response?.data?.error_description || error?.message || error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetches the profile information for the authenticated user.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* import { getUserChannel } from "@kash.88/alerts";
|
|
6
|
+
*
|
|
7
|
+
* // The "id" can be obtained using the "getUser" function,
|
|
8
|
+
*
|
|
9
|
+
* try {
|
|
10
|
+
* const userId = "USER_ID";
|
|
11
|
+
* const channelId = getUserChannel(userId);
|
|
12
|
+
* } catch (error) {
|
|
13
|
+
* console.error("Error generating channel ID:", error.message);
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* @param {string|number} id - The user ID for which to generate the channel identifier.
|
|
17
|
+
* @returns {string} The donation alert channel identifier in the format "$alerts:donation_{id}".
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export default function getUserChannel(id: string | number): string {
|
|
21
|
+
try {
|
|
22
|
+
if (typeof id !== "string" && typeof id !== "number") {
|
|
23
|
+
throw new Error("You must provide \"id\" as a string or number.");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (id === null || id === undefined || id === "") {
|
|
27
|
+
throw new Error("You must provide a non-empty \"id\".");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return `$alerts:donation_${id}`;
|
|
31
|
+
} catch (error: any) {
|
|
32
|
+
throw new Error(error?.response?.data?.error_description || error?.message || error);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { validateDataObject } from "@utils";
|
|
3
|
+
import { UpdateTokenData, OauthToken } from "@type";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Refreshes an access token using a refresh token.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { updateAccessToken } from "@kash.88/alerts";
|
|
10
|
+
*
|
|
11
|
+
* try {
|
|
12
|
+
* const tokenData = await updateAccessToken({
|
|
13
|
+
* client_id: "YOUR_CLIENT_ID",
|
|
14
|
+
* client_secret: "YOUR_CLIENT_SECRET",
|
|
15
|
+
* refresh_token: "USER_REFRESH_TOKEN"
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* console.log(tokenData.access_token);
|
|
19
|
+
* } catch (error) {
|
|
20
|
+
* console.error("Error updating access token:", error.response.data);
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* @param {UpdateTokenData} data - The data for the token refresh request.
|
|
24
|
+
* @returns {Promise<OauthToken>} A promise that resolves to the new token data from the API.
|
|
25
|
+
* @see {@link https://www.donationalerts.com/apidoc#authorization__authorization_code__getting_access_token}
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
export default async function updateAccessToken(data: UpdateTokenData): Promise<OauthToken> {
|
|
29
|
+
try {
|
|
30
|
+
validateDataObject(data, ["client_id", "client_secret", "refresh_token"]);
|
|
31
|
+
|
|
32
|
+
const response = await axios.post<OauthToken>("https://www.donationalerts.com/oauth/token", {
|
|
33
|
+
grant_type: "refresh_token",
|
|
34
|
+
client_id: data.client_id,
|
|
35
|
+
client_secret: data.client_secret,
|
|
36
|
+
refresh_token: data.refresh_token
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return response.data;
|
|
40
|
+
} catch (error: any) {
|
|
41
|
+
throw new Error(error?.response?.data?.error_description || error?.message || error);
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import getAuthorizeLink from "@function/getAuthorizeLink";
|
|
2
|
+
import getOauthToken from "@function/getOauthToken";
|
|
3
|
+
import getUser from "@function/getUser";
|
|
4
|
+
import updateAccessToken from "@function/updateAccessToken";
|
|
5
|
+
import getUserChannel from "@function/getUserChannel";
|
|
6
|
+
import { getPrivateToken } from "@function/getPrivateToken";
|
|
7
|
+
import CentrifugeClient from "@ws/CentrifugeClient";
|
|
8
|
+
|
|
9
|
+
export * from "@type";
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
getAuthorizeLink,
|
|
13
|
+
getOauthToken,
|
|
14
|
+
getUser,
|
|
15
|
+
updateAccessToken,
|
|
16
|
+
getUserChannel,
|
|
17
|
+
getPrivateToken,
|
|
18
|
+
CentrifugeClient
|
|
19
|
+
};
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface OauthToken {
|
|
2
|
+
token_type: string;
|
|
3
|
+
expires_in: number;
|
|
4
|
+
access_token: string;
|
|
5
|
+
refresh_token: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface GetPrivateToken {
|
|
9
|
+
channel: string;
|
|
10
|
+
uuidv4_client_id: string;
|
|
11
|
+
access_token: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface User {
|
|
15
|
+
id: number;
|
|
16
|
+
code: string;
|
|
17
|
+
name: string;
|
|
18
|
+
avatar: string;
|
|
19
|
+
email: string;
|
|
20
|
+
socket_connection_token: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface GetAuthorizeLinkData {
|
|
24
|
+
client_id: string | number;
|
|
25
|
+
scope: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface GetOauthData {
|
|
29
|
+
client_id: string | number;
|
|
30
|
+
client_secret: string;
|
|
31
|
+
code: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface UpdateTokenData {
|
|
35
|
+
client_id: string | number;
|
|
36
|
+
client_secret: string;
|
|
37
|
+
refresh_token: string;
|
|
38
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if a value is a plain object.
|
|
3
|
+
* @param {unknown} value The value to check.
|
|
4
|
+
* @returns {boolean} True if the value is a plain object, false otherwise.
|
|
5
|
+
* @private
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
function isObject(value: unknown): value is Record<string, unknown> {
|
|
9
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Validates that the provided data is an object and contains the required keys.
|
|
14
|
+
* For each required key, it also validates that the value is a string or a number.
|
|
15
|
+
* @param {unknown} data - The data object to validate.
|
|
16
|
+
* @param {string[]} requiredKeys - An array of keys that must be present in the data object.
|
|
17
|
+
* @throws {Error} If validation fails.
|
|
18
|
+
* @private
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export function validateDataObject(data: unknown, requiredKeys: string[]): asserts data is Record<string, string | number | string[]> {
|
|
22
|
+
if (!isObject(data)) {
|
|
23
|
+
throw new Error("You must provide data as an object.");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
for (const key of requiredKeys) {
|
|
27
|
+
if (!(key in data) || data[key] === undefined || data[key] === null) {
|
|
28
|
+
throw new Error(`You must provide "${key}" in the data object.`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const value = data[key];
|
|
32
|
+
|
|
33
|
+
if (key === "scope") {
|
|
34
|
+
if(!Array.isArray(value)) {
|
|
35
|
+
throw new Error(`"${key}" must be an array of strings.`);
|
|
36
|
+
}
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (typeof value !== "string" && typeof value !== "number") {
|
|
41
|
+
throw new Error(`"${key}" must be a string or a number.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Checks if required environment variables are present.
|
|
48
|
+
* Throws an error with a clear message if any variable is missing.
|
|
49
|
+
* @param {string[]} keys - List of required environment variable names.
|
|
50
|
+
*/
|
|
51
|
+
export function checkEnv(keys: string[]): void {
|
|
52
|
+
const missing = keys.filter((key) => !process.env[key]);
|
|
53
|
+
if (missing.length > 0) {
|
|
54
|
+
throw new Error(`Missing required environment variables: ${missing.join(", ")}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { getPrivateToken } from "@kash-88/alerts";
|
|
2
|
+
import { WebSocket } from "ws";
|
|
3
|
+
import { EventEmitter } from "events";
|
|
4
|
+
import TypedEmitter from "typed-emitter";
|
|
5
|
+
|
|
6
|
+
// --- Interfaces ---
|
|
7
|
+
interface CentrifugeConfiguration {
|
|
8
|
+
ws: {
|
|
9
|
+
url: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface WSClientOptions {
|
|
14
|
+
channel: string;
|
|
15
|
+
socket_connection_token: string;
|
|
16
|
+
access_token: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface CentrifugeMessage {
|
|
20
|
+
params: Record<string, unknown>;
|
|
21
|
+
id: number;
|
|
22
|
+
method?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// --- Configuration ---
|
|
26
|
+
const configuration: CentrifugeConfiguration = {
|
|
27
|
+
ws: {
|
|
28
|
+
url: "wss://centrifugo.donationalerts.com/connection/websocket"
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type MessageEvents = {
|
|
33
|
+
open: () => void;
|
|
34
|
+
message: (data: CentrifugeMessage) => void;
|
|
35
|
+
close: () => void;
|
|
36
|
+
error: (error: Error) => void;
|
|
37
|
+
reconnecting: (attempt: number) => void;
|
|
38
|
+
reconnect_failed: () => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// --- Class ---
|
|
42
|
+
export default class CentrifugeClient extends (EventEmitter as new () => TypedEmitter<MessageEvents>) {
|
|
43
|
+
private options: WSClientOptions;
|
|
44
|
+
private ws: WebSocket | null = null;
|
|
45
|
+
|
|
46
|
+
constructor(options: WSClientOptions) {
|
|
47
|
+
super();
|
|
48
|
+
this.options = options; this.ws;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public createConnection(): WebSocket {
|
|
52
|
+
if (this.ws) {
|
|
53
|
+
return this.ws;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
this.ws = new WebSocket(configuration.ws.url);
|
|
58
|
+
|
|
59
|
+
return this.ws;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error("[WS] Failed to create connection:", error);
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public confirmConnection(socket_connection_token: string = this.options.socket_connection_token): void {
|
|
67
|
+
const message: CentrifugeMessage = {
|
|
68
|
+
params: {
|
|
69
|
+
token: socket_connection_token
|
|
70
|
+
},
|
|
71
|
+
id: 1
|
|
72
|
+
};
|
|
73
|
+
this.sendMessage(JSON.stringify(message));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public async connectPrivateToken(channel: string, uuidv4_client_id: string, access_token: string = this.options.access_token): Promise<void> {
|
|
77
|
+
try {
|
|
78
|
+
const token = await getPrivateToken({
|
|
79
|
+
channel,
|
|
80
|
+
uuidv4_client_id,
|
|
81
|
+
access_token
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const subscribeMessage: CentrifugeMessage = {
|
|
85
|
+
params: {
|
|
86
|
+
channel: channel,
|
|
87
|
+
token
|
|
88
|
+
},
|
|
89
|
+
method: 1,
|
|
90
|
+
id: 2
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
this.sendMessage(JSON.stringify(subscribeMessage));
|
|
94
|
+
} catch (error: any) {
|
|
95
|
+
throw new Error(error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public sendMessage(message: string): void {
|
|
100
|
+
if(!this.ws) return;
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
this.ws.send(message);
|
|
104
|
+
} catch (error: any) {
|
|
105
|
+
throw new Error(error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|