UNPKG

4.39 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.slackDefaultMessagePrefixHook = exports.SlackService = void 0;
4const time_lib_1 = require("@naturalcycles/time-lib");
5const got_1 = require("got");
6const __1 = require("..");
7const GAE = !!process.env['GAE_INSTANCE'];
8const DEFAULTS = () => ({
9 username: 'bot',
10 channel: '#log',
11 icon_emoji: ':spider_web:',
12 items: 'no text',
13});
14const INSPECT_OPT = {
15 colors: false,
16 includeErrorData: true,
17 includeErrorStack: true,
18};
19const log = (0, __1.Debug)('nc:nodejs-lib:slack');
20/**
21 * Has 2 main methods:
22 *
23 * 1. .send({ items: any, channel: ..., ... })
24 * Low-level method with all possible options available.
25 *
26 * 2. .log(...items: any[])
27 * Shortcut method to "just log a bunch of things", everything is "by default" there.
28 *
29 * .send method has a shortcut:
30 * .send(string, ctx?: CTX)
31 */
32class SlackService {
33 constructor(cfg) {
34 this.cfg = {
35 messagePrefixHook: slackDefaultMessagePrefixHook,
36 ...cfg,
37 };
38 }
39 /**
40 * Allows to "log" many things at once, similar to `console.log(one, two, three).
41 */
42 async log(...items) {
43 await this.send({
44 // If it's an Array of just 1 item - treat it as non-array
45 items: items.length === 1 ? items[0] : items,
46 });
47 }
48 async send(msg, ctx) {
49 const { webhookUrl, messagePrefixHook } = this.cfg;
50 // If String is passed as first argument - just transform it to a full SlackMessage
51 if (typeof msg === 'string') {
52 msg = {
53 items: msg,
54 };
55 }
56 if (ctx !== undefined) {
57 Object.assign(msg, { ctx });
58 }
59 if (!msg.noLog) {
60 log[msg.level || __1.DebugLogLevel.info](...[msg.items, msg.kv, msg.attachments, msg.mentions].filter(Boolean));
61 }
62 if (!webhookUrl)
63 return;
64 // Transform msg.kv into msg.attachments
65 if (msg.kv) {
66 msg.attachments = [...(msg.attachments || []), { fields: this.kvToFields(msg.kv) }];
67 delete msg.kv; // to not pass it all the way to Slack Api
68 }
69 let text;
70 const inspectOpt = {
71 ...INSPECT_OPT,
72 ...msg.inspectOptions,
73 };
74 // Array has a special treatment here
75 if (Array.isArray(msg.items)) {
76 text = msg.items.map(t => (0, __1.inspectAny)(t, inspectOpt)).join('\n');
77 }
78 else {
79 text = (0, __1.inspectAny)(msg.items, inspectOpt);
80 }
81 // Wrap in markdown-text-block if it's anything but plain String
82 if (typeof msg.items !== 'string') {
83 text = '```' + text + '```';
84 }
85 if (msg.mentions?.length) {
86 text += '\n' + msg.mentions.map(s => `<@${s}>`).join(' ');
87 }
88 const prefix = await messagePrefixHook(msg);
89 if (prefix === null)
90 return; // filtered out!
91 const json = {
92 ...DEFAULTS(),
93 ...this.cfg.defaults,
94 ...msg,
95 // Text with Prefix
96 text: [prefix.join(': '), text].filter(Boolean).join('\n'),
97 };
98 // they're not needed in the json payload
99 delete json['items'];
100 delete json['ctx'];
101 delete json['noLog'];
102 json.channel = (this.cfg.channelByLevel || {})[msg.level] || json.channel;
103 await got_1.default
104 .post(webhookUrl, {
105 json,
106 responseType: 'text',
107 })
108 .catch(err => {
109 // ignore (unless throwOnError is set)
110 if (msg.throwOnError)
111 throw err;
112 });
113 }
114 kvToFields(kv) {
115 return Object.entries(kv).map(([k, v]) => ({
116 title: k,
117 value: String(v),
118 short: String(v).length < 80,
119 }));
120 }
121}
122exports.SlackService = SlackService;
123function slackDefaultMessagePrefixHook(msg) {
124 const tokens = [(0, time_lib_1.dayjs)().toPretty()];
125 const { ctx } = msg;
126 // AppEngine-specific decoration
127 if (GAE && ctx && typeof ctx === 'object' && typeof ctx.header === 'function') {
128 tokens.push(ctx.header('x-appengine-country'), ctx.header('x-appengine-city'));
129 }
130 return tokens.filter(Boolean);
131}
132exports.slackDefaultMessagePrefixHook = slackDefaultMessagePrefixHook;