@nu-art/slack-backend 0.401.8 → 0.500.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/ModuleBE_Slack.d.ts +7 -3
- package/ModuleBE_Slack.js +167 -113
- package/SlackBuilderBE.js +2 -4
- package/Slack_ServerApiError.js +1 -1
- package/package.json +6 -6
package/ModuleBE_Slack.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { Module } from '@nu-art/ts-common';
|
|
5
5
|
import { WebClientOptions } from '@slack/web-api';
|
|
6
|
-
import { PreSendSlackStructuredMessage } from '@nu-art/slack-shared';
|
|
6
|
+
import { API_Slack, PreSendSlackStructuredMessage } from '@nu-art/slack-shared';
|
|
7
7
|
import { Stream } from 'stream';
|
|
8
8
|
export type ConfigType_ModuleBE_Slack = {
|
|
9
9
|
token: string;
|
|
@@ -28,9 +28,13 @@ export declare class ModuleBE_Slack_Class extends Module<ConfigType_ModuleBE_Sla
|
|
|
28
28
|
private messageMap;
|
|
29
29
|
constructor();
|
|
30
30
|
protected init(): void;
|
|
31
|
-
postMessage(
|
|
31
|
+
postMessage(request: API_Slack['postMessage']['Body']): Promise<void>;
|
|
32
|
+
postStructuredMessage(request: API_Slack['postStructuredMessage']['Body']): Promise<API_Slack['postStructuredMessage']['Response']>;
|
|
33
|
+
sendFEMessage(request: API_Slack['sendFEMessage']['Body']): Promise<void>;
|
|
34
|
+
postFiles(request: API_Slack['postFiles']['Body']): Promise<API_Slack['postFiles']['Response']>;
|
|
35
|
+
sendText(text: string, channel?: string, thread?: ThreadPointer): Promise<ThreadPointer | undefined>;
|
|
32
36
|
postFile(file: Buffer, name: string, thread?: ThreadPointer): Promise<import("@slack/web-api").FilesCompleteUploadExternalResponse>;
|
|
33
|
-
|
|
37
|
+
sendStructured(message: PreSendSlackStructuredMessage, thread?: ThreadPointer): Promise<ThreadPointer>;
|
|
34
38
|
private postMessageImpl;
|
|
35
39
|
uploadFile: (file: Buffer | Stream, name: string, tp?: ThreadPointer) => Promise<import("@slack/web-api").FilesUploadResponse>;
|
|
36
40
|
getUserIdByEmail(email: string): Promise<string | undefined>;
|
package/ModuleBE_Slack.js
CHANGED
|
@@ -16,134 +16,188 @@
|
|
|
16
16
|
* See the License for the specific language governing permissions and
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
20
|
+
var useValue = arguments.length > 2;
|
|
21
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
22
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
23
|
+
}
|
|
24
|
+
return useValue ? value : void 0;
|
|
25
|
+
};
|
|
26
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
27
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
28
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
29
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
30
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
31
|
+
var _, done = false;
|
|
32
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
33
|
+
var context = {};
|
|
34
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
35
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
36
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
37
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
38
|
+
if (kind === "accessor") {
|
|
39
|
+
if (result === void 0) continue;
|
|
40
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
41
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
42
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
43
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
44
|
+
}
|
|
45
|
+
else if (_ = accept(result)) {
|
|
46
|
+
if (kind === "field") initializers.unshift(_);
|
|
47
|
+
else descriptor[key] = _;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
51
|
+
done = true;
|
|
52
|
+
};
|
|
19
53
|
/**
|
|
20
54
|
* Created by AlanBen on 29/08/2019.
|
|
21
55
|
*/
|
|
22
56
|
import { currentTimeMillis, ImplementationMissingException, md5, Minute, Module } from '@nu-art/ts-common';
|
|
23
57
|
import { WebClient, } from '@slack/web-api';
|
|
24
|
-
import {
|
|
58
|
+
import { ApiHandler } from '@nu-art/http-server';
|
|
25
59
|
import { ApiDef_Slack } from '@nu-art/slack-shared';
|
|
26
60
|
import { postSlackMessageErrorHandler } from './utils.js';
|
|
27
61
|
import { HttpCodes } from '@nu-art/ts-common/core/exceptions/http-codes';
|
|
28
62
|
import { SlackBuilderBE } from './SlackBuilderBE.js';
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
})
|
|
48
|
-
createBodyServerApi(ApiDef_Slack.vv1.postStructuredMessage, async (request) => {
|
|
49
|
-
return { threadPointer: await this.postStructuredMessage(request.message, request.thread) };
|
|
50
|
-
}),
|
|
51
|
-
createBodyServerApi(ApiDef_Slack.vv1.sendFEMessage, async (request) => {
|
|
52
|
-
const slackMessage = new SlackBuilderBE(request.channel, request.messageBlocks, request.messageReplies);
|
|
53
|
-
await slackMessage.send();
|
|
54
|
-
}),
|
|
55
|
-
createBodyServerApi(ApiDef_Slack.vv1.postFiles, async (request) => this.postFile(request.file, request.name, request.thread))
|
|
56
|
-
]);
|
|
57
|
-
}
|
|
58
|
-
async postMessage(text, channel, thread) {
|
|
59
|
-
const message = {
|
|
60
|
-
channel: channel ?? this.config.defaultChannel,
|
|
61
|
-
};
|
|
62
|
-
// @ts-ignore - no clue why, their api requires text but it is not in the te
|
|
63
|
-
message.text = text;
|
|
64
|
-
//Block same message on throttling time
|
|
65
|
-
const time = this.messageMap[md5(text)];
|
|
66
|
-
if (time && currentTimeMillis() - time < (this.config.throttlingTime || Minute))
|
|
67
|
-
return;
|
|
68
|
-
//Post and return thread
|
|
69
|
-
return await this.postMessageImpl(message, thread);
|
|
70
|
-
}
|
|
71
|
-
async postFile(file, name, thread) {
|
|
72
|
-
// Get a URL to upload
|
|
73
|
-
const uploadUrlResponse = await this.web.files.getUploadURLExternal({
|
|
74
|
-
filename: name,
|
|
75
|
-
length: file.length
|
|
76
|
-
});
|
|
77
|
-
if (!uploadUrlResponse.ok)
|
|
78
|
-
throw HttpCodes._5XX.INTERNAL_SERVER_ERROR(`Failed at getting a URL from slack: ${uploadUrlResponse.error}`);
|
|
79
|
-
const { upload_url, file_id } = uploadUrlResponse;
|
|
80
|
-
try {
|
|
81
|
-
await AxiosHttpModule.createRequest({
|
|
82
|
-
fullUrl: upload_url,
|
|
83
|
-
path: '',
|
|
84
|
-
method: HttpMethod.POST
|
|
85
|
-
})
|
|
86
|
-
.setBody(file)
|
|
87
|
-
.executeSync();
|
|
63
|
+
let ModuleBE_Slack_Class = (() => {
|
|
64
|
+
let _classSuper = Module;
|
|
65
|
+
let _instanceExtraInitializers = [];
|
|
66
|
+
let _postMessage_decorators;
|
|
67
|
+
let _postStructuredMessage_decorators;
|
|
68
|
+
let _sendFEMessage_decorators;
|
|
69
|
+
let _postFiles_decorators;
|
|
70
|
+
return class ModuleBE_Slack_Class extends _classSuper {
|
|
71
|
+
static {
|
|
72
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
73
|
+
_postMessage_decorators = [ApiHandler(ApiDef_Slack.postMessage)];
|
|
74
|
+
_postStructuredMessage_decorators = [ApiHandler(ApiDef_Slack.postStructuredMessage)];
|
|
75
|
+
_sendFEMessage_decorators = [ApiHandler(ApiDef_Slack.sendFEMessage)];
|
|
76
|
+
_postFiles_decorators = [ApiHandler(ApiDef_Slack.postFiles)];
|
|
77
|
+
__esDecorate(this, null, _postMessage_decorators, { kind: "method", name: "postMessage", static: false, private: false, access: { has: obj => "postMessage" in obj, get: obj => obj.postMessage }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
78
|
+
__esDecorate(this, null, _postStructuredMessage_decorators, { kind: "method", name: "postStructuredMessage", static: false, private: false, access: { has: obj => "postStructuredMessage" in obj, get: obj => obj.postStructuredMessage }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
79
|
+
__esDecorate(this, null, _sendFEMessage_decorators, { kind: "method", name: "sendFEMessage", static: false, private: false, access: { has: obj => "sendFEMessage" in obj, get: obj => obj.sendFEMessage }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
80
|
+
__esDecorate(this, null, _postFiles_decorators, { kind: "method", name: "postFiles", static: false, private: false, access: { has: obj => "postFiles" in obj, get: obj => obj.postFiles }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
81
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
88
82
|
}
|
|
89
|
-
|
|
90
|
-
|
|
83
|
+
web = __runInitializers(this, _instanceExtraInitializers);
|
|
84
|
+
messageMap = {};
|
|
85
|
+
constructor() {
|
|
86
|
+
super('slack');
|
|
87
|
+
this.setDefaultConfig({ unfurl_links: false, unfurl_media: false });
|
|
91
88
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
89
|
+
init() {
|
|
90
|
+
if (!this.config.token)
|
|
91
|
+
throw new ImplementationMissingException('Missing config token for ModuleBE_Slack. Please add it');
|
|
92
|
+
this.web = new WebClient(this.config.token, {
|
|
93
|
+
rejectRateLimitedCalls: true,
|
|
94
|
+
...this.config.slackConfig
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
async postMessage(request) {
|
|
98
|
+
await this.sendText(request.message, request.channel);
|
|
99
|
+
}
|
|
100
|
+
async postStructuredMessage(request) {
|
|
101
|
+
return { threadPointer: await this.sendStructured(request.message, request.thread) };
|
|
102
|
+
}
|
|
103
|
+
async sendFEMessage(request) {
|
|
104
|
+
const slackMessage = new SlackBuilderBE(request.channel, request.messageBlocks, request.messageReplies);
|
|
105
|
+
await slackMessage.send();
|
|
106
|
+
}
|
|
107
|
+
async postFiles(request) {
|
|
108
|
+
return this.postFile(request.file, request.name, request.thread);
|
|
109
|
+
}
|
|
110
|
+
async sendText(text, channel, thread) {
|
|
111
|
+
const message = {
|
|
112
|
+
channel: channel ?? this.config.defaultChannel,
|
|
113
|
+
};
|
|
114
|
+
// @ts-ignore - no clue why, their api requires text but it is not in the te
|
|
115
|
+
message.text = text;
|
|
116
|
+
//Block same message on throttling time
|
|
117
|
+
const time = this.messageMap[md5(text)];
|
|
118
|
+
if (time && currentTimeMillis() - time < (this.config.throttlingTime || Minute))
|
|
119
|
+
return;
|
|
120
|
+
//Post and return thread
|
|
121
|
+
return await this.postMessageImpl(message, thread);
|
|
122
|
+
}
|
|
123
|
+
async postFile(file, name, thread) {
|
|
124
|
+
// Get a URL to upload
|
|
125
|
+
const uploadUrlResponse = await this.web.files.getUploadURLExternal({
|
|
126
|
+
filename: name,
|
|
127
|
+
length: file.length
|
|
128
|
+
});
|
|
129
|
+
if (!uploadUrlResponse.ok)
|
|
130
|
+
throw HttpCodes._5XX.INTERNAL_SERVER_ERROR(`Failed at getting a URL from slack: ${uploadUrlResponse.error}`);
|
|
131
|
+
const { upload_url, file_id } = uploadUrlResponse;
|
|
132
|
+
try {
|
|
133
|
+
const res = await fetch(upload_url, {
|
|
134
|
+
method: 'POST',
|
|
135
|
+
body: file,
|
|
136
|
+
});
|
|
137
|
+
if (!res.ok)
|
|
138
|
+
throw new Error(`Upload failed: ${res.status} ${res.statusText}`);
|
|
111
139
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
140
|
+
catch (e) {
|
|
141
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
142
|
+
throw HttpCodes._5XX.INTERNAL_SERVER_ERROR(`Failed at uploading file to url: ${message}`);
|
|
143
|
+
}
|
|
144
|
+
// Complete the upload - post the file to slack message
|
|
145
|
+
const completeResponse = await this.web.files.completeUploadExternal({
|
|
146
|
+
files: [{ id: file_id }],
|
|
147
|
+
channel_id: thread ? thread.channel : this.config.defaultChannel,
|
|
148
|
+
thread_ts: thread?.ts,
|
|
149
|
+
});
|
|
150
|
+
if (!completeResponse.ok)
|
|
151
|
+
throw HttpCodes._5XX.INTERNAL_SERVER_ERROR(`Failed at complete uploading: ${completeResponse.error}`);
|
|
152
|
+
return completeResponse;
|
|
117
153
|
}
|
|
118
|
-
|
|
119
|
-
|
|
154
|
+
async sendStructured(message, thread) {
|
|
155
|
+
message.channel ??= this.config.defaultChannel;
|
|
156
|
+
return await this.postMessageImpl(message, thread);
|
|
120
157
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
158
|
+
async postMessageImpl(message, threadPointer) {
|
|
159
|
+
try {
|
|
160
|
+
if (threadPointer) {
|
|
161
|
+
message.thread_ts = threadPointer.ts;
|
|
162
|
+
message.channel = threadPointer.channel;
|
|
163
|
+
}
|
|
164
|
+
message.unfurl_links = this.config.unfurl_links;
|
|
165
|
+
message.unfurl_media = this.config.unfurl_media;
|
|
166
|
+
this.logDebug(`Sending message in ${threadPointer ? 'thread' : 'channel'}`, message);
|
|
167
|
+
const res = await this.web.chat.postMessage(message);
|
|
168
|
+
return { ts: res.ts, channel: res.channel };
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
throw HttpCodes._5XX.INTERNAL_SERVER_ERROR(postSlackMessageErrorHandler(err, message.channel));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
uploadFile = async (file, name, tp) => {
|
|
175
|
+
const channel = tp?.channel || this.config.defaultChannel;
|
|
176
|
+
const fileUploadBlob = {
|
|
177
|
+
channels: channel,
|
|
178
|
+
file: file,
|
|
179
|
+
filename: name,
|
|
180
|
+
};
|
|
181
|
+
return await this.web.files.upload(fileUploadBlob);
|
|
128
182
|
};
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
return result.user.id;
|
|
136
|
-
return undefined;
|
|
137
|
-
}
|
|
138
|
-
async openDM(userIds) {
|
|
139
|
-
const users = userIds.join(',');
|
|
140
|
-
const result = await this.web.conversations.open({ users });
|
|
141
|
-
if (result.ok) { // @ts-ignore
|
|
142
|
-
return result.channel.id;
|
|
183
|
+
async getUserIdByEmail(email) {
|
|
184
|
+
const result = await this.web.users.lookupByEmail({ email });
|
|
185
|
+
if (result.ok)
|
|
186
|
+
// @ts-ignore
|
|
187
|
+
return result.user.id;
|
|
188
|
+
return undefined;
|
|
143
189
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
190
|
+
async openDM(userIds) {
|
|
191
|
+
const users = userIds.join(',');
|
|
192
|
+
const result = await this.web.conversations.open({ users });
|
|
193
|
+
if (result.ok) { // @ts-ignore
|
|
194
|
+
return result.channel.id;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
getDefaultChannel = () => {
|
|
198
|
+
return this.config.defaultChannel;
|
|
199
|
+
};
|
|
147
200
|
};
|
|
148
|
-
}
|
|
201
|
+
})();
|
|
202
|
+
export { ModuleBE_Slack_Class };
|
|
149
203
|
export const ModuleBE_Slack = new ModuleBE_Slack_Class();
|
package/SlackBuilderBE.js
CHANGED
|
@@ -2,11 +2,9 @@ import { ModuleBE_Slack } from './ModuleBE_Slack.js';
|
|
|
2
2
|
import { BaseSlackBuilder } from '@nu-art/slack-shared';
|
|
3
3
|
import { __stringify, currentTimeMillis, formatTimestamp, generateHex } from '@nu-art/ts-common';
|
|
4
4
|
export class SlackBuilderBE extends BaseSlackBuilder {
|
|
5
|
-
// ######################## Builder Steps ########################
|
|
6
5
|
constructor(channel, blocks, replies) {
|
|
7
6
|
super(channel, blocks, replies);
|
|
8
7
|
}
|
|
9
|
-
// ######################## Internal Logic ########################
|
|
10
8
|
convertLongSectionBlocks = () => {
|
|
11
9
|
const convertBlock = (block) => {
|
|
12
10
|
if (block.type !== 'section')
|
|
@@ -31,7 +29,7 @@ export class SlackBuilderBE extends BaseSlackBuilder {
|
|
|
31
29
|
};
|
|
32
30
|
sendMessage = async () => {
|
|
33
31
|
this.convertLongSectionBlocks();
|
|
34
|
-
return ModuleBE_Slack.
|
|
32
|
+
return ModuleBE_Slack.sendStructured({
|
|
35
33
|
channel: this.channel,
|
|
36
34
|
blocks: this.blocks
|
|
37
35
|
});
|
|
@@ -45,7 +43,7 @@ export class SlackBuilderBE extends BaseSlackBuilder {
|
|
|
45
43
|
};
|
|
46
44
|
sendReplies = async (tp) => {
|
|
47
45
|
for (const reply of this.replies) {
|
|
48
|
-
await ModuleBE_Slack.
|
|
46
|
+
await ModuleBE_Slack.sendStructured({
|
|
49
47
|
blocks: reply
|
|
50
48
|
}, tp);
|
|
51
49
|
}
|
package/Slack_ServerApiError.js
CHANGED
|
@@ -74,7 +74,7 @@ export class Slack_ServerApiError_Class extends Module {
|
|
|
74
74
|
if (message.includes(key))
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
|
-
return ModuleBE_Slack.
|
|
77
|
+
return ModuleBE_Slack.sendText(message, undefined, threadPointer);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
export const Slack_ServerApiError = new Slack_ServerApiError_Class();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nu-art/slack-backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.500.0",
|
|
4
4
|
"description": "Storm - Express & Typescript based backend framework Backend",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"TacB0sS",
|
|
@@ -31,10 +31,8 @@
|
|
|
31
31
|
"build": "tsc"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@nu-art/slack-shared": "0.
|
|
35
|
-
"@nu-art/
|
|
36
|
-
"@nu-art/thunderstorm-shared": "0.401.8",
|
|
37
|
-
"@nu-art/ts-common": "0.401.8",
|
|
34
|
+
"@nu-art/slack-shared": "0.500.0",
|
|
35
|
+
"@nu-art/ts-common": "0.500.0",
|
|
38
36
|
"@slack/web-api": "7.13.0",
|
|
39
37
|
"firebase": "^11.9.0",
|
|
40
38
|
"firebase-admin": "13.4.0",
|
|
@@ -43,7 +41,9 @@
|
|
|
43
41
|
"moment": "^2.29.4",
|
|
44
42
|
"react": "^18.0.0",
|
|
45
43
|
"react-dom": "^18.0.0",
|
|
46
|
-
"react-router-dom": "^6.9.0"
|
|
44
|
+
"react-router-dom": "^6.9.0",
|
|
45
|
+
"@nu-art/http-server": "{{THUNDERSTORM_VERSION}}",
|
|
46
|
+
"@nu-art/api-types": "{{THUNDERSTORM_VERSION}}"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/react": "^18.0.0",
|