@facteurjs/core 1.0.0-beta.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/api/handlers/notifications.js +77 -0
- package/dist/api/handlers/preferences.js +43 -0
- package/dist/api/index.d.ts +16 -0
- package/dist/api/index.js +21 -0
- package/dist/api/types.d.ts +22 -0
- package/dist/api/types.js +0 -0
- package/dist/channels/discord/channel.d.ts +18 -0
- package/dist/channels/discord/channel.js +15 -0
- package/dist/channels/discord/index.d.ts +3 -0
- package/dist/channels/discord/index.js +4 -0
- package/dist/channels/discord/message.d.ts +147 -0
- package/dist/channels/discord/message.js +176 -0
- package/dist/channels/discord/types.d.ts +52 -0
- package/dist/channels/discord/types.js +0 -0
- package/dist/channels/fcm/channel.d.ts +22 -0
- package/dist/channels/fcm/channel.js +44 -0
- package/dist/channels/fcm/index.d.ts +3 -0
- package/dist/channels/fcm/index.js +4 -0
- package/dist/channels/fcm/message.d.ts +64 -0
- package/dist/channels/fcm/message.js +122 -0
- package/dist/channels/fcm/types.d.ts +29 -0
- package/dist/channels/fcm/types.js +0 -0
- package/dist/channels/slack/channel.d.ts +18 -0
- package/dist/channels/slack/channel.js +15 -0
- package/dist/channels/slack/index.d.ts +3 -0
- package/dist/channels/slack/index.js +4 -0
- package/dist/channels/slack/message.d.ts +209 -0
- package/dist/channels/slack/message.js +390 -0
- package/dist/channels/slack/types.d.ts +7 -0
- package/dist/channels/slack/types.js +0 -0
- package/dist/channels/transmit/channel.d.ts +21 -0
- package/dist/channels/transmit/channel.js +27 -0
- package/dist/channels/transmit/index.d.ts +3 -0
- package/dist/channels/transmit/index.js +4 -0
- package/dist/channels/transmit/message.d.ts +11 -0
- package/dist/channels/transmit/message.js +17 -0
- package/dist/channels/transmit/types.d.ts +11 -0
- package/dist/channels/transmit/types.js +0 -0
- package/dist/channels/twilio/channel.d.ts +21 -0
- package/dist/channels/twilio/channel.js +56 -0
- package/dist/channels/twilio/index.d.ts +4 -0
- package/dist/channels/twilio/index.js +4 -0
- package/dist/channels/twilio/message.d.ts +86 -0
- package/dist/channels/twilio/message.js +152 -0
- package/dist/channels/twilio/types.d.ts +51 -0
- package/dist/channels/twilio/types.js +0 -0
- package/dist/channels/webhook/exceptions.d.ts +18 -0
- package/dist/channels/webhook/exceptions.js +24 -0
- package/dist/channels/webhook/index.d.ts +4 -0
- package/dist/channels/webhook/index.js +5 -0
- package/dist/channels/webhook/message.d.ts +24 -0
- package/dist/channels/webhook/message.js +40 -0
- package/dist/channels/webhook/provider.d.ts +19 -0
- package/dist/channels/webhook/provider.js +63 -0
- package/dist/channels/webhook/types.d.ts +15 -0
- package/dist/channels/webhook/types.js +0 -0
- package/dist/channels/webpush/channel.d.ts +26 -0
- package/dist/channels/webpush/channel.js +55 -0
- package/dist/channels/webpush/index.d.ts +3 -0
- package/dist/channels/webpush/index.js +4 -0
- package/dist/channels/webpush/message.d.ts +90 -0
- package/dist/channels/webpush/message.js +174 -0
- package/dist/channels/webpush/types.d.ts +50 -0
- package/dist/channels/webpush/types.js +0 -0
- package/dist/database/adapters/knex.d.ts +6 -0
- package/dist/database/adapters/knex.js +116 -0
- package/dist/database/adapters/kysely.d.ts +6 -0
- package/dist/database/adapters/kysely.js +101 -0
- package/dist/database/channel.d.ts +24 -0
- package/dist/database/channel.js +42 -0
- package/dist/database/database.d.ts +18 -0
- package/dist/database/database.js +89 -0
- package/dist/database/index.d.ts +3 -0
- package/dist/database/index.js +4 -0
- package/dist/database/message.d.ts +26 -0
- package/dist/database/message.js +51 -0
- package/dist/database/types.d.ts +147 -0
- package/dist/database/types.js +0 -0
- package/dist/debug.js +7 -0
- package/dist/errors/duplicate_notification_exception.d.ts +13 -0
- package/dist/errors/duplicate_notification_exception.js +21 -0
- package/dist/errors/http_error.d.ts +16 -0
- package/dist/errors/http_error.js +30 -0
- package/dist/errors/index.d.ts +38 -0
- package/dist/errors/index.js +39 -0
- package/dist/events/events.d.ts +90 -0
- package/dist/events/events.js +83 -0
- package/dist/facteur.d.ts +37 -0
- package/dist/facteur.js +83 -0
- package/dist/fake.d.ts +47 -0
- package/dist/fake.js +85 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +16 -0
- package/dist/notifications/channel_resolver.js +91 -0
- package/dist/notifications/notification_discoverer.d.ts +40 -0
- package/dist/notifications/notification_discoverer.js +113 -0
- package/dist/notifications/notification_sender.js +210 -0
- package/dist/options.d.ts +22 -0
- package/dist/options.js +57 -0
- package/dist/types/channel.d.ts +18 -0
- package/dist/types/channel.js +5 -0
- package/dist/types/events.d.ts +27 -0
- package/dist/types/extend.d.ts +25 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.js +4 -0
- package/dist/types/notifications.d.ts +60 -0
- package/dist/types/notifications.js +38 -0
- package/dist/types/options.d.ts +157 -0
- package/dist/types/preferences.d.ts +40 -0
- package/dist/types/queue.d.ts +11 -0
- package/package.json +65 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { defineRoute } from "../index.js";
|
|
2
|
+
|
|
3
|
+
//#region src/api/handlers/notifications.ts
|
|
4
|
+
/**
|
|
5
|
+
* Get all notifications for a given user
|
|
6
|
+
*/
|
|
7
|
+
const getNotificationRoute = defineRoute(({ facteur }) => ({
|
|
8
|
+
method: "get",
|
|
9
|
+
route: "/notifications/notifiable/:notifiableId/notifications",
|
|
10
|
+
handler: async (request) => {
|
|
11
|
+
const userId = request.params.notifiableId;
|
|
12
|
+
const notifications = await facteur.db.getNotifications({
|
|
13
|
+
notifiableId: userId,
|
|
14
|
+
tenantId: request.query.tenantId,
|
|
15
|
+
page: request.query.page,
|
|
16
|
+
limit: request.query.limit,
|
|
17
|
+
status: request.query.status
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
status: 200,
|
|
21
|
+
body: notifications || []
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
}));
|
|
25
|
+
/**
|
|
26
|
+
* Mark a specific notification as read or seen
|
|
27
|
+
*/
|
|
28
|
+
const markNotificationAsRoute = defineRoute(({ facteur }) => ({
|
|
29
|
+
method: "post",
|
|
30
|
+
route: "/notifications/notifiable/:notifiableId/mark-as",
|
|
31
|
+
handler: async (request) => {
|
|
32
|
+
const notificationId = request.body.notificationId;
|
|
33
|
+
const status = request.body.status;
|
|
34
|
+
if (!status) return {
|
|
35
|
+
status: 400,
|
|
36
|
+
body: { error: "Status is required" }
|
|
37
|
+
};
|
|
38
|
+
if (!notificationId) return {
|
|
39
|
+
status: 400,
|
|
40
|
+
body: { error: "Notification ID is required" }
|
|
41
|
+
};
|
|
42
|
+
await facteur.db.updateNotification({
|
|
43
|
+
id: notificationId,
|
|
44
|
+
status
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
status: 204,
|
|
48
|
+
body: {}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}));
|
|
52
|
+
/**
|
|
53
|
+
* Mark all notifications for a user as read or seen
|
|
54
|
+
*/
|
|
55
|
+
const markAllNotificationsAsRoute = defineRoute(({ facteur }) => ({
|
|
56
|
+
method: "post",
|
|
57
|
+
route: "/notifications/notifiable/:notifiableId/mark-all",
|
|
58
|
+
handler: async (request) => {
|
|
59
|
+
const status = request.body.status;
|
|
60
|
+
if (!status) return {
|
|
61
|
+
status: 400,
|
|
62
|
+
body: { error: "Status is required" }
|
|
63
|
+
};
|
|
64
|
+
await facteur.db.updateAllNotifications({
|
|
65
|
+
notifiableId: request.params.notifiableId,
|
|
66
|
+
tenantId: request.body.tenantId,
|
|
67
|
+
status
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
status: 204,
|
|
71
|
+
body: {}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}));
|
|
75
|
+
|
|
76
|
+
//#endregion
|
|
77
|
+
export { getNotificationRoute, markAllNotificationsAsRoute, markNotificationAsRoute };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { defineRoute } from "../index.js";
|
|
2
|
+
|
|
3
|
+
//#region src/api/handlers/preferences.ts
|
|
4
|
+
const getPreferencesRoute = defineRoute(({ facteur }) => ({
|
|
5
|
+
method: "get",
|
|
6
|
+
route: "/notifications/notifiable/:notifiableId/preferences",
|
|
7
|
+
handler: async (request) => {
|
|
8
|
+
const userId = request.params.notifiableId;
|
|
9
|
+
const preferences = await facteur.db.getPreferences({
|
|
10
|
+
notifiableId: userId,
|
|
11
|
+
tenantId: request.query.tenantId
|
|
12
|
+
});
|
|
13
|
+
return {
|
|
14
|
+
status: 200,
|
|
15
|
+
body: preferences || {}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
}));
|
|
19
|
+
const updatePreferencesRoute = defineRoute(({ facteur }) => ({
|
|
20
|
+
method: "post",
|
|
21
|
+
route: "/notifications/notifiable/:notifiableId/preferences",
|
|
22
|
+
handler: async (request) => {
|
|
23
|
+
const userId = request.params.notifiableId;
|
|
24
|
+
const preferences = request.body.preferences;
|
|
25
|
+
if (!preferences) return {
|
|
26
|
+
status: 400,
|
|
27
|
+
body: { error: "Preferences are required" }
|
|
28
|
+
};
|
|
29
|
+
await facteur.db.updatePreferences({
|
|
30
|
+
notifiableId: userId,
|
|
31
|
+
tenantId: request.body.tenantId,
|
|
32
|
+
notificationName: request.body.notificationName,
|
|
33
|
+
channelPreferences: preferences
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
status: 204,
|
|
37
|
+
body: {}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
export { getPreferencesRoute, updatePreferencesRoute };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Facteur } from "../facteur.js";
|
|
2
|
+
import { RouteDefinition, ServerAdapter } from "./types.js";
|
|
3
|
+
|
|
4
|
+
//#region src/api/index.d.ts
|
|
5
|
+
declare function defineRoute(routeDefinition: (options: {
|
|
6
|
+
facteur: Facteur<any, any>;
|
|
7
|
+
}) => RouteDefinition): (options: {
|
|
8
|
+
facteur: Facteur<any, any>;
|
|
9
|
+
}) => RouteDefinition;
|
|
10
|
+
declare const routes: (facteur: Facteur<any, any>) => RouteDefinition[];
|
|
11
|
+
declare function createFacteurServer(options: {
|
|
12
|
+
adapter: ServerAdapter;
|
|
13
|
+
facteur: Facteur<any, any>;
|
|
14
|
+
}): void;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { createFacteurServer, defineRoute, routes };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { getPreferencesRoute, updatePreferencesRoute } from "./handlers/preferences.js";
|
|
2
|
+
import { getNotificationRoute, markAllNotificationsAsRoute, markNotificationAsRoute } from "./handlers/notifications.js";
|
|
3
|
+
|
|
4
|
+
//#region src/api/index.ts
|
|
5
|
+
function defineRoute(routeDefinition) {
|
|
6
|
+
return routeDefinition;
|
|
7
|
+
}
|
|
8
|
+
const routes = (facteur) => [
|
|
9
|
+
getNotificationRoute({ facteur }),
|
|
10
|
+
markNotificationAsRoute({ facteur }),
|
|
11
|
+
markAllNotificationsAsRoute({ facteur }),
|
|
12
|
+
getPreferencesRoute({ facteur }),
|
|
13
|
+
updatePreferencesRoute({ facteur })
|
|
14
|
+
];
|
|
15
|
+
function createFacteurServer(options) {
|
|
16
|
+
const { adapter, facteur } = options;
|
|
17
|
+
adapter.setRoutes(routes(facteur));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
21
|
+
export { createFacteurServer, defineRoute, routes };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/api/types.d.ts
|
|
2
|
+
type HTTPMethod = 'get' | 'post' | 'put' | 'patch';
|
|
3
|
+
interface HTTPRequest {
|
|
4
|
+
body: Record<string, any>;
|
|
5
|
+
params: Record<string, any>;
|
|
6
|
+
query: Record<string, any>;
|
|
7
|
+
headers: Record<string, string | undefined>;
|
|
8
|
+
}
|
|
9
|
+
interface HTTPResponse {
|
|
10
|
+
status: number;
|
|
11
|
+
body: Record<string, any>;
|
|
12
|
+
}
|
|
13
|
+
interface RouteDefinition {
|
|
14
|
+
method: HTTPMethod;
|
|
15
|
+
route: string;
|
|
16
|
+
handler: (request: HTTPRequest) => Promise<HTTPResponse>;
|
|
17
|
+
}
|
|
18
|
+
interface ServerAdapter {
|
|
19
|
+
setRoutes(routes: RouteDefinition[]): void;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { HTTPMethod, HTTPRequest, HTTPResponse, RouteDefinition, ServerAdapter };
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Channel } from "../../types/channel.js";
|
|
2
|
+
import { WebhookOptions, WebhookTargets } from "../webhook/types.js";
|
|
3
|
+
import { DiscordResponse } from "./types.js";
|
|
4
|
+
import { DiscordMessage } from "./message.js";
|
|
5
|
+
import { WebhookChannel } from "../webhook/provider.js";
|
|
6
|
+
|
|
7
|
+
//#region src/channels/discord/channel.d.ts
|
|
8
|
+
declare function discordWebhookChannel<Options extends WebhookOptions<any>>(options: Options): DiscordProvider<Options>;
|
|
9
|
+
declare class DiscordProvider<T extends WebhookOptions<any>> extends WebhookChannel<T> implements Channel<T, DiscordMessage, DiscordResponse, WebhookTargets<T>> {
|
|
10
|
+
name: any;
|
|
11
|
+
}
|
|
12
|
+
declare module '@facteurjs/core/types' {
|
|
13
|
+
interface Notification {
|
|
14
|
+
asDiscordMessage(): DiscordMessage;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { DiscordProvider, discordWebhookChannel };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { WebhookChannel } from "../webhook/provider.js";
|
|
2
|
+
|
|
3
|
+
//#region src/channels/discord/channel.ts
|
|
4
|
+
function discordWebhookChannel(options) {
|
|
5
|
+
return new DiscordProvider({
|
|
6
|
+
name: "discord",
|
|
7
|
+
...options
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
var DiscordProvider = class extends WebhookChannel {
|
|
11
|
+
name = "discord";
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
export { DiscordProvider, discordWebhookChannel };
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { WebhookMessage } from "../webhook/message.js";
|
|
2
|
+
import { DiscordEmbedField, HexadecimalColor } from "./types.js";
|
|
3
|
+
|
|
4
|
+
//#region src/channels/discord/message.d.ts
|
|
5
|
+
declare class DiscordMessage extends WebhookMessage {
|
|
6
|
+
#private;
|
|
7
|
+
/**
|
|
8
|
+
* Creates a new instance of DiscordMessage.
|
|
9
|
+
*/
|
|
10
|
+
static create(): DiscordMessage;
|
|
11
|
+
/**
|
|
12
|
+
* Sets the content of the message.
|
|
13
|
+
*/
|
|
14
|
+
setBody(body: string): this;
|
|
15
|
+
/**
|
|
16
|
+
* Sets the username of the bot that will send the message.
|
|
17
|
+
*/
|
|
18
|
+
setBotUsername(username: string): this;
|
|
19
|
+
/**
|
|
20
|
+
* Sets the avatar URL for the bot that will send the message.
|
|
21
|
+
*/
|
|
22
|
+
setBotAvatar(avatarUrl: string): this;
|
|
23
|
+
/**
|
|
24
|
+
* Adds an embed to the message using a callback function to configure it.
|
|
25
|
+
*/
|
|
26
|
+
addEmbed(callback: (embed: DiscordEmbed) => void): this;
|
|
27
|
+
/**
|
|
28
|
+
* Whether or not this notification should be read as text to speech.
|
|
29
|
+
*/
|
|
30
|
+
setTTS(flag: boolean): this;
|
|
31
|
+
serialize(): {
|
|
32
|
+
body: {
|
|
33
|
+
embeds: {
|
|
34
|
+
title: string | undefined;
|
|
35
|
+
description: string | undefined;
|
|
36
|
+
url: string | undefined;
|
|
37
|
+
color: number | undefined;
|
|
38
|
+
fields: DiscordEmbedField[] | undefined;
|
|
39
|
+
image: {
|
|
40
|
+
url: string;
|
|
41
|
+
} | undefined;
|
|
42
|
+
thumbnail: {
|
|
43
|
+
url: string;
|
|
44
|
+
} | undefined;
|
|
45
|
+
author: {
|
|
46
|
+
name: string | undefined;
|
|
47
|
+
url: string | undefined;
|
|
48
|
+
icon_url: string | undefined;
|
|
49
|
+
} | undefined;
|
|
50
|
+
footer: {
|
|
51
|
+
text: string;
|
|
52
|
+
icon_url: string | undefined;
|
|
53
|
+
} | undefined;
|
|
54
|
+
timestamp: string | undefined;
|
|
55
|
+
}[];
|
|
56
|
+
avatar_url?: string;
|
|
57
|
+
username?: string;
|
|
58
|
+
tts: boolean;
|
|
59
|
+
content: string;
|
|
60
|
+
};
|
|
61
|
+
headers: Record<string, string>;
|
|
62
|
+
queryParameters: Record<string, string>;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
declare class DiscordEmbed {
|
|
66
|
+
#private;
|
|
67
|
+
/**
|
|
68
|
+
* Sets the author information for the embed.
|
|
69
|
+
*/
|
|
70
|
+
setAuthor(options: {
|
|
71
|
+
name?: string;
|
|
72
|
+
url?: string;
|
|
73
|
+
iconUrl?: string;
|
|
74
|
+
}): this;
|
|
75
|
+
/**
|
|
76
|
+
* Sets the title of the embed.
|
|
77
|
+
*/
|
|
78
|
+
setTitle(title: string): this;
|
|
79
|
+
/**
|
|
80
|
+
* Sets the description text for the embed.
|
|
81
|
+
*/
|
|
82
|
+
setDescription(description: string): this;
|
|
83
|
+
/**
|
|
84
|
+
* Sets the URL for the embed title to link to.
|
|
85
|
+
*/
|
|
86
|
+
setUrl(url: string): this;
|
|
87
|
+
/**
|
|
88
|
+
* Sets the color of the embed's left border. Accepts a hexadecimal color code.
|
|
89
|
+
*/
|
|
90
|
+
setColor(color: HexadecimalColor): this;
|
|
91
|
+
/**
|
|
92
|
+
* Adds a field to the embed with a name and value.
|
|
93
|
+
*
|
|
94
|
+
* Inline fields will be displayed next to each other, rather than each on their own line.
|
|
95
|
+
*/
|
|
96
|
+
addField(options: {
|
|
97
|
+
name: string;
|
|
98
|
+
value: string;
|
|
99
|
+
inline?: boolean;
|
|
100
|
+
}): this;
|
|
101
|
+
/**
|
|
102
|
+
* Sets the footer information for the embed, including optional timestamp.
|
|
103
|
+
*/
|
|
104
|
+
setFooter(options: {
|
|
105
|
+
text: string;
|
|
106
|
+
iconUrl?: string;
|
|
107
|
+
}): this;
|
|
108
|
+
/**
|
|
109
|
+
* Set a date that will be displayed in the footer.
|
|
110
|
+
*/
|
|
111
|
+
setTimestamp(timestamp: Date): this;
|
|
112
|
+
/**
|
|
113
|
+
* Adds an image to the embed.
|
|
114
|
+
* You can add up to 4 images when you have an URL in the Embed body. ( `embed.setUrl()` )
|
|
115
|
+
* Otherwise, only one image will be displayed.
|
|
116
|
+
*/
|
|
117
|
+
setImage(image: string): this;
|
|
118
|
+
/**
|
|
119
|
+
* Sets the thumbnail image for the embed.
|
|
120
|
+
*/
|
|
121
|
+
setThumbnail(url: string): this;
|
|
122
|
+
serialize(): {
|
|
123
|
+
title: string | undefined;
|
|
124
|
+
description: string | undefined;
|
|
125
|
+
url: string | undefined;
|
|
126
|
+
color: number | undefined;
|
|
127
|
+
fields: DiscordEmbedField[] | undefined;
|
|
128
|
+
image: {
|
|
129
|
+
url: string;
|
|
130
|
+
} | undefined;
|
|
131
|
+
thumbnail: {
|
|
132
|
+
url: string;
|
|
133
|
+
} | undefined;
|
|
134
|
+
author: {
|
|
135
|
+
name: string | undefined;
|
|
136
|
+
url: string | undefined;
|
|
137
|
+
icon_url: string | undefined;
|
|
138
|
+
} | undefined;
|
|
139
|
+
footer: {
|
|
140
|
+
text: string;
|
|
141
|
+
icon_url: string | undefined;
|
|
142
|
+
} | undefined;
|
|
143
|
+
timestamp: string | undefined;
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
export { DiscordMessage };
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { WebhookMessage } from "../webhook/message.js";
|
|
2
|
+
|
|
3
|
+
//#region src/channels/discord/message.ts
|
|
4
|
+
var DiscordMessage = class DiscordMessage extends WebhookMessage {
|
|
5
|
+
#body = "";
|
|
6
|
+
#username = "";
|
|
7
|
+
#avatarUrl = "";
|
|
8
|
+
#tts = false;
|
|
9
|
+
#embeds = [];
|
|
10
|
+
/**
|
|
11
|
+
* Creates a new instance of DiscordMessage.
|
|
12
|
+
*/
|
|
13
|
+
static create() {
|
|
14
|
+
return new DiscordMessage().setQueryParameters({ wait: "true" });
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Sets the content of the message.
|
|
18
|
+
*/
|
|
19
|
+
setBody(body) {
|
|
20
|
+
this.#body = body;
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Sets the username of the bot that will send the message.
|
|
25
|
+
*/
|
|
26
|
+
setBotUsername(username) {
|
|
27
|
+
this.#username = username;
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Sets the avatar URL for the bot that will send the message.
|
|
32
|
+
*/
|
|
33
|
+
setBotAvatar(avatarUrl) {
|
|
34
|
+
this.#avatarUrl = avatarUrl;
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Adds an embed to the message using a callback function to configure it.
|
|
39
|
+
*/
|
|
40
|
+
addEmbed(callback) {
|
|
41
|
+
const embed = new DiscordEmbed();
|
|
42
|
+
callback(embed);
|
|
43
|
+
this.#embeds.push(embed);
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Whether or not this notification should be read as text to speech.
|
|
48
|
+
*/
|
|
49
|
+
setTTS(flag) {
|
|
50
|
+
this.#tts = flag;
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
serialize() {
|
|
54
|
+
return {
|
|
55
|
+
...super.serialize(),
|
|
56
|
+
body: {
|
|
57
|
+
tts: this.#tts,
|
|
58
|
+
content: this.#body,
|
|
59
|
+
...this.#username ? { username: this.#username } : {},
|
|
60
|
+
...this.#avatarUrl ? { avatar_url: this.#avatarUrl } : {},
|
|
61
|
+
embeds: this.#embeds.map((embed) => embed.serialize())
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var DiscordEmbed = class {
|
|
67
|
+
#title;
|
|
68
|
+
#description;
|
|
69
|
+
#url;
|
|
70
|
+
#color;
|
|
71
|
+
#fields;
|
|
72
|
+
#author;
|
|
73
|
+
#footer;
|
|
74
|
+
#image;
|
|
75
|
+
#timestamp;
|
|
76
|
+
#thumbnail;
|
|
77
|
+
/**
|
|
78
|
+
* Sets the author information for the embed.
|
|
79
|
+
*/
|
|
80
|
+
setAuthor(options) {
|
|
81
|
+
this.#author = options;
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Sets the title of the embed.
|
|
86
|
+
*/
|
|
87
|
+
setTitle(title) {
|
|
88
|
+
this.#title = title;
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Sets the description text for the embed.
|
|
93
|
+
*/
|
|
94
|
+
setDescription(description) {
|
|
95
|
+
this.#description = description;
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Sets the URL for the embed title to link to.
|
|
100
|
+
*/
|
|
101
|
+
setUrl(url) {
|
|
102
|
+
this.#url = url;
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Sets the color of the embed's left border. Accepts a hexadecimal color code.
|
|
107
|
+
*/
|
|
108
|
+
setColor(color) {
|
|
109
|
+
this.#color = Number.parseInt(color.slice(1), 16);
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Adds a field to the embed with a name and value.
|
|
114
|
+
*
|
|
115
|
+
* Inline fields will be displayed next to each other, rather than each on their own line.
|
|
116
|
+
*/
|
|
117
|
+
addField(options) {
|
|
118
|
+
this.#fields = this.#fields || [];
|
|
119
|
+
this.#fields.push(options);
|
|
120
|
+
return this;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Sets the footer information for the embed, including optional timestamp.
|
|
124
|
+
*/
|
|
125
|
+
setFooter(options) {
|
|
126
|
+
this.#footer = options;
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Set a date that will be displayed in the footer.
|
|
131
|
+
*/
|
|
132
|
+
setTimestamp(timestamp) {
|
|
133
|
+
this.#timestamp = timestamp;
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Adds an image to the embed.
|
|
138
|
+
* You can add up to 4 images when you have an URL in the Embed body. ( `embed.setUrl()` )
|
|
139
|
+
* Otherwise, only one image will be displayed.
|
|
140
|
+
*/
|
|
141
|
+
setImage(image) {
|
|
142
|
+
this.#image = image;
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Sets the thumbnail image for the embed.
|
|
147
|
+
*/
|
|
148
|
+
setThumbnail(url) {
|
|
149
|
+
this.#thumbnail = url;
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
serialize() {
|
|
153
|
+
return {
|
|
154
|
+
title: this.#title,
|
|
155
|
+
description: this.#description,
|
|
156
|
+
url: this.#url,
|
|
157
|
+
color: this.#color,
|
|
158
|
+
fields: this.#fields,
|
|
159
|
+
image: this.#image ? { url: this.#image } : void 0,
|
|
160
|
+
thumbnail: this.#thumbnail ? { url: this.#thumbnail } : void 0,
|
|
161
|
+
author: this.#author ? {
|
|
162
|
+
name: this.#author.name,
|
|
163
|
+
url: this.#author.url,
|
|
164
|
+
icon_url: this.#author.iconUrl
|
|
165
|
+
} : void 0,
|
|
166
|
+
footer: this.#footer ? {
|
|
167
|
+
text: this.#footer.text,
|
|
168
|
+
icon_url: this.#footer.iconUrl
|
|
169
|
+
} : void 0,
|
|
170
|
+
timestamp: this.#timestamp?.toISOString()
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
//#endregion
|
|
176
|
+
export { DiscordMessage };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { WebhookOptions, WebhookTargets } from "../webhook/types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/channels/discord/types.d.ts
|
|
4
|
+
type DiscordOptions<WebhooksNames extends string> = WebhookOptions<WebhooksNames>;
|
|
5
|
+
type DiscordTargets<Options extends DiscordOptions<any>> = WebhookTargets<Options>;
|
|
6
|
+
type HexadecimalColor = `#${string}`;
|
|
7
|
+
interface DiscordResponse {
|
|
8
|
+
type: number;
|
|
9
|
+
content: string;
|
|
10
|
+
mentions: string[];
|
|
11
|
+
mention_roles: string[];
|
|
12
|
+
attachments: string[];
|
|
13
|
+
embeds: string[];
|
|
14
|
+
timestamp: string;
|
|
15
|
+
edited_timestamp: string | null;
|
|
16
|
+
flags: number;
|
|
17
|
+
components: string[];
|
|
18
|
+
id: string;
|
|
19
|
+
channel_id: string;
|
|
20
|
+
author: {
|
|
21
|
+
id: string;
|
|
22
|
+
username: string;
|
|
23
|
+
avatar: string | null;
|
|
24
|
+
discriminator: string;
|
|
25
|
+
public_flags: number;
|
|
26
|
+
flags: number;
|
|
27
|
+
bot: boolean;
|
|
28
|
+
global_name: string | null;
|
|
29
|
+
clan: string | null;
|
|
30
|
+
primary_guild: string | null;
|
|
31
|
+
};
|
|
32
|
+
pinned: boolean;
|
|
33
|
+
mention_everyone: boolean;
|
|
34
|
+
tts: boolean;
|
|
35
|
+
webhook_id: string;
|
|
36
|
+
}
|
|
37
|
+
interface DiscordEmbedField {
|
|
38
|
+
name: string;
|
|
39
|
+
value: string;
|
|
40
|
+
inline?: boolean;
|
|
41
|
+
}
|
|
42
|
+
interface DiscordEmbedAuthor {
|
|
43
|
+
name?: string;
|
|
44
|
+
url?: string;
|
|
45
|
+
iconUrl?: string;
|
|
46
|
+
}
|
|
47
|
+
interface DiscordEmbedFooter {
|
|
48
|
+
text: string;
|
|
49
|
+
iconUrl?: string;
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
export { DiscordEmbedAuthor, DiscordEmbedField, DiscordEmbedFooter, DiscordOptions, DiscordResponse, DiscordTargets, HexadecimalColor };
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Channel, ChannelSendParams, kTargetSymbol } from "../../types/channel.js";
|
|
2
|
+
import { FcmMessage } from "./message.js";
|
|
3
|
+
import { FcmConfig, FcmTargets } from "./types.js";
|
|
4
|
+
|
|
5
|
+
//#region src/channels/fcm/channel.d.ts
|
|
6
|
+
declare function fcmChannel(config: FcmConfig): FcmChannel;
|
|
7
|
+
declare class FcmChannel implements Channel<FcmConfig, FcmMessage, any, FcmTargets> {
|
|
8
|
+
#private;
|
|
9
|
+
private config;
|
|
10
|
+
name: "fcm";
|
|
11
|
+
[kTargetSymbol]: FcmTargets;
|
|
12
|
+
private messaging;
|
|
13
|
+
constructor(config: FcmConfig);
|
|
14
|
+
send(options: ChannelSendParams<FcmMessage, FcmTargets>): Promise<string>;
|
|
15
|
+
}
|
|
16
|
+
declare module '@facteurjs/core/types' {
|
|
17
|
+
interface Notification {
|
|
18
|
+
asFcmMessage(): FcmMessage;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { FcmChannel, fcmChannel };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { errors } from "../../errors/index.js";
|
|
2
|
+
import { kTargetSymbol } from "../../types/channel.js";
|
|
3
|
+
import "../../types/index.js";
|
|
4
|
+
import { cert, initializeApp } from "firebase-admin/app";
|
|
5
|
+
import { getMessaging } from "firebase-admin/messaging";
|
|
6
|
+
|
|
7
|
+
//#region src/channels/fcm/channel.ts
|
|
8
|
+
function fcmChannel(config) {
|
|
9
|
+
return new FcmChannel(config);
|
|
10
|
+
}
|
|
11
|
+
var FcmChannel = class {
|
|
12
|
+
name = "fcm";
|
|
13
|
+
[kTargetSymbol] = null;
|
|
14
|
+
messaging;
|
|
15
|
+
constructor(config) {
|
|
16
|
+
this.config = config;
|
|
17
|
+
const app = initializeApp({
|
|
18
|
+
...config,
|
|
19
|
+
...config.serviceAccountKeyPath ? { credential: cert(config.serviceAccountKeyPath) } : {}
|
|
20
|
+
});
|
|
21
|
+
this.messaging = getMessaging(app);
|
|
22
|
+
}
|
|
23
|
+
#resolveTargets(options) {
|
|
24
|
+
if (options.targets) return options.targets;
|
|
25
|
+
throw new errors.E_UNAVAILABLE_TARGETS(["FCM"]);
|
|
26
|
+
}
|
|
27
|
+
#handleError(error) {
|
|
28
|
+
if (error.code === "messaging/registration-token-not-registered") throw new Error(`FCM token is no longer valid: ${error.message}`);
|
|
29
|
+
if (error.code === "messaging/invalid-argument") throw new Error(`Invalid FCM message format: ${error.message}`);
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
async send(options) {
|
|
33
|
+
const message = options.message.serialize();
|
|
34
|
+
const targets = this.#resolveTargets(options);
|
|
35
|
+
if (this.config.debugToken) message.token = this.config.debugToken;
|
|
36
|
+
if (targets.token) message.token = this.config.debugToken || targets.token;
|
|
37
|
+
else if (targets.topic) message.topic = targets.topic;
|
|
38
|
+
else if (targets.condition) message.condition = targets.condition;
|
|
39
|
+
return await this.messaging.send(message).catch((error) => this.#handleError(error));
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { FcmChannel, fcmChannel };
|