Skip to content

Commit

Permalink
Merge pull request #1137 from blackflux/dev
Browse files Browse the repository at this point in the history
[Gally]: master <- dev
  • Loading branch information
simlu committed Aug 1, 2021
2 parents 092b33d + dc45c9c commit 55d7479
Show file tree
Hide file tree
Showing 28 changed files with 606 additions and 244 deletions.
12 changes: 12 additions & 0 deletions README.md
Expand Up @@ -53,6 +53,10 @@ Maximum number of entries in cache at any given time. Optional, defaults to `100

Send `message` to self.

### self.shareFiles(files: array<string>)

Share `files` to self.

### channel.meta(channel: string)

Get meta information about channel `channel`
Expand All @@ -69,10 +73,18 @@ Set `topic` of channel `channel`

Set `purpose` of channel `channel`

### channel.shareFiles(channel: string, files: array<string>)

Share `files` to a channel `channel`.

### workspace.details(cache: boolean = true)

Obtain details for workspace. Should usually be cached as it is easy to run into rate limits.

### files.upload(filepath: string, title: string = null, filename: string = null)

Upload file. Title and filename default to basename of filepath. Returns file id.

## Internal functions

### call(endpoint: string, params: object, cache: boolean = false)
Expand Down
8 changes: 4 additions & 4 deletions package.json
Expand Up @@ -11,16 +11,16 @@
},
"peerDependencies": {},
"devDependencies": {
"@babel/cli": "7.14.5",
"@babel/core": "7.14.3",
"@babel/cli": "7.14.8",
"@babel/core": "7.14.8",
"@babel/register": "7.14.5",
"@blackflux/eslint-plugin-rules": "2.0.3",
"@blackflux/robo-config-plugin": "5.2.4",
"@blackflux/robo-config-plugin": "5.2.5",
"babel-eslint": "10.1.0",
"babel-preset-latest-node": "5.5.1",
"chai": "4.3.4",
"coveralls": "3.1.1",
"eslint": "7.30.0",
"eslint": "7.32.0",
"eslint-config-airbnb-base": "14.2.1",
"eslint-plugin-import": "2.23.4",
"eslint-plugin-json": "3.0.0",
Expand Down
56 changes: 9 additions & 47 deletions src/index.js
Expand Up @@ -3,6 +3,11 @@ const FormData = require('form-data');
const LRU = require('lru-cache-ext');
const objectHash = require('object-hash-strict');

const workspace = require('./index/workspace');
const self = require('./index/self');
const channel = require('./index/channel');
const files = require('./index/files');

module.exports = (workspaceUrl, token, { cacheTtl = 60, cacheMaxEntries = 100 } = {}) => {
const lru = new LRU({ maxAge: cacheTtl * 1000, max: cacheMaxEntries });

Expand Down Expand Up @@ -33,55 +38,12 @@ module.exports = (workspaceUrl, token, { cacheTtl = 60, cacheMaxEntries = 100 }
});
return lru.memoize(signature, () => req());
};
const channelMeta = async (name) => {
const rtmStart = await call('rtm.start', {}, true);
const channel = rtmStart.channels.find((chan) => chan.name === name);
if (!channel) {
throw new Error(`Channel "${name}" not found.`);
}
return channel;
};

return {
call,
workspace: {
details: (cache = true) => call('rtm.start', {}, cache)
},
self: {
message: async (msg) => {
const rtmStart = await call('rtm.start', {}, true);
const user = rtmStart.self;
return call('chat.command', {
disp: '/me',
command: '/msg',
text: `${user.name} ${msg}`,
channel: rtmStart.ims.find((im) => im.user === user.id).id
});
}
},
channel: {
meta: channelMeta,
message: async (name, msg) => {
const channel = await channelMeta(name);
return call('chat.postMessage', {
text: msg,
channel: channel.id
});
},
setTopic: async (name, topic) => {
const channel = await channelMeta(name);
return call('conversations.setTopic', {
topic,
channel: channel.id
});
},
setPurpose: async (name, purpose) => {
const channel = await channelMeta(name);
return call('conversations.setPurpose', {
purpose,
channel: channel.id
});
}
}
workspace: workspace(call),
self: self(call),
channel: channel(call),
files: files(call)
};
};
33 changes: 33 additions & 0 deletions src/index/channel.js
@@ -0,0 +1,33 @@
const getChannelMeta = require('./util/get-channel-meta');

module.exports = (call) => ({
meta: (name) => getChannelMeta(call, name),
message: async (name, msg) => {
const channel = await getChannelMeta(call, name);
return call('chat.postMessage', {
text: msg,
channel: channel.id
});
},
setTopic: async (name, topic) => {
const channel = await getChannelMeta(call, name);
return call('conversations.setTopic', {
topic,
channel: channel.id
});
},
setPurpose: async (name, purpose) => {
const channel = await getChannelMeta(call, name);
return call('conversations.setPurpose', {
purpose,
channel: channel.id
});
},
shareFiles: async (name, files) => {
const channel = await getChannelMeta(call, name);
return call('files.share', {
files: files.join(','),
channel: channel.id
});
}
});
43 changes: 43 additions & 0 deletions src/index/files.js
@@ -0,0 +1,43 @@
const fs = require('fs');
const path = require('path');
const assert = require('assert');
const FormData = require('form-data');
const axios = require('axios');

module.exports = (call) => ({
upload: async (filepath, title_ = null, filename_ = null) => {
// prepare
const basename = path.basename(filepath);
const title = title_ === null ? basename : title_;
const filename = filename_ === null ? basename : filename_;
const stat = fs.statSync(filepath);

// get upload information
const getUploadUrlResponse = await call('files.getUploadURL', { filename, length: stat.size });
assert(getUploadUrlResponse.ok === true, getUploadUrlResponse);

// upload
const formData = new FormData();
formData.append('file', fs.createReadStream(filepath));
const requestParams = {
method: 'post',
url: getUploadUrlResponse.upload_url,
data: formData,
headers: {
// eslint-disable-next-line no-underscore-dangle
'Content-Type': `multipart/form-data; boundary=${formData._boundary}`,
...formData.getHeaders()
}
};
const { data } = await axios(requestParams);
assert(data === `OK - ${stat.size}`);

// complete upload
const completeUploadResponse = await call('files.completeUpload', {
files: JSON.stringify([{ id: getUploadUrlResponse.file, title }])
});
assert(completeUploadResponse.ok === true, completeUploadResponse);
assert(completeUploadResponse.files.length === 1, completeUploadResponse);
return completeUploadResponse.files[0].id;
}
});
20 changes: 20 additions & 0 deletions src/index/self.js
@@ -0,0 +1,20 @@
const getSelf = require('./util/get-self');

module.exports = (call) => ({
message: async (msg) => {
const self = await getSelf(call);
return call('chat.command', {
disp: '/me',
command: '/msg',
text: `${self.name} ${msg}`,
channel: self.channel
});
},
shareFiles: async (files) => {
const self = await getSelf(call);
return call('files.share', {
files: files.join(','),
channel: self.channel
});
}
});
8 changes: 8 additions & 0 deletions src/index/util/get-channel-meta.js
@@ -0,0 +1,8 @@
module.exports = async (call, name) => {
const rtmStart = await call('rtm.start', {}, true);
const channel = rtmStart.channels.find((chan) => chan.name === name);
if (!channel) {
throw new Error(`Channel "${name}" not found.`);
}
return channel;
};
9 changes: 9 additions & 0 deletions src/index/util/get-self.js
@@ -0,0 +1,9 @@
module.exports = async (call) => {
const rtmStart = await call('rtm.start', {}, true);
const user = rtmStart.self;
return {
id: user.id,
name: user.name,
channel: rtmStart.ims.find((im) => im.user === user.id).id
};
};
3 changes: 3 additions & 0 deletions src/index/workspace.js
@@ -0,0 +1,3 @@
module.exports = (call) => ({
details: (cache = true) => call('rtm.start', {}, cache)
});
30 changes: 0 additions & 30 deletions test/index.spec.js
Expand Up @@ -12,36 +12,6 @@ describe('Testing Slack SDK', {
slack = Slack('workspace', 'SLACK-SESSION-TOKEN');
});

it('Testing workspace.details', async () => {
const r = await slack.workspace.details();
expect(r).to.deep.contain({ ok: true });
});

it('Testing channel.message', async () => {
const r = await slack.channel.message('channel', 'message');
expect(r).to.deep.contain({ ok: true });
});

it('Testing channel.message-unknown', async ({ capture }) => {
const e = await capture(() => slack.channel.message('unknown', 'message'));
expect(e.message).to.equal('Channel "unknown" not found.');
});

it('Testing channel.setTopic', async () => {
const r = await slack.channel.setTopic('channel', 'topic');
expect(r).to.deep.contain({ ok: true });
});

it('Testing channel.setPurpose', async () => {
const r = await slack.channel.setPurpose('channel', 'purpose');
expect(r).to.deep.contain({ ok: true });
});

it('Testing self.message', async () => {
const r = await slack.self.message('message');
expect(r).to.deep.contain({ ok: true });
});

it('Testing call-cached', async () => {
const r1 = await slack.call('rtm.start', {}, true);
expect(r1).to.deep.contain({ ok: true });
Expand Down
Expand Up @@ -3,7 +3,7 @@
"scope": "https://workspace.slack.com:443",
"method": "POST",
"path": "/api/rtm.start",
"body": "----------------------------437991048648655338960848\r\nContent-Disposition: form-data; name=\"token\"\r\n\r\nSLACK-SESSION-TOKEN\r\n----------------------------437991048648655338960848--\r\n",
"body": "----------------------------143695343986972598734041\r\nContent-Disposition: form-data; name=\"token\"\r\n\r\nSLACK-SESSION-TOKEN\r\n----------------------------143695343986972598734041--\r\n",
"status": 200,
"response": {
"ok": true
Expand Down
47 changes: 47 additions & 0 deletions test/index/channel.spec.js
@@ -0,0 +1,47 @@
const expect = require('chai').expect;
const { describe } = require('node-tdd');
const Slack = require('../../src/index');

describe('Testing Slack SDK', {
useNock: true,
cryptoSeed: '3eced0c2-38ea-4bc4-9045-ee0cb68bd688',
timestamp: '2020-07-13T16:14:25.488Z'
}, () => {
let slack;
beforeEach(() => {
slack = Slack('workspace', 'SLACK-SESSION-TOKEN');
});

it('Testing channel.meta', async () => {
const r = await slack.channel.meta('channel');
expect(r).to.deep.equal({
id: 'C3Y9NQTQG',
name: 'channel'
});
});

it('Testing channel.message', async () => {
const r = await slack.channel.message('channel', 'message');
expect(r).to.deep.contain({ ok: true });
});

it('Testing channel.message-unknown', async ({ capture }) => {
const e = await capture(() => slack.channel.message('unknown', 'message'));
expect(e.message).to.equal('Channel "unknown" not found.');
});

it('Testing channel.setTopic', async () => {
const r = await slack.channel.setTopic('channel', 'topic');
expect(r).to.deep.contain({ ok: true });
});

it('Testing channel.setPurpose', async () => {
const r = await slack.channel.setPurpose('channel', 'purpose');
expect(r).to.deep.contain({ ok: true });
});

it('Testing channel.shareFiles', async () => {
const r = await slack.channel.shareFiles('channel', ['F029QHN0AAZ', 'F02AH8J752L']);
expect(r).to.deep.equal({ ok: true });
});
});
@@ -0,0 +1,18 @@
[
{
"scope": "https://workspace.slack.com:443",
"method": "POST",
"path": "/api/rtm.start",
"body": "----------------------------143695343986972598734041\r\nContent-Disposition: form-data; name=\"token\"\r\n\r\nSLACK-SESSION-TOKEN\r\n----------------------------143695343986972598734041--\r\n",
"status": 200,
"response": {
"ok": true,
"channels": [
{
"id": "C3Y9NQTQG",
"name": "channel"
}
]
}
}
]
@@ -0,0 +1,29 @@
[
{
"scope": "https://workspace.slack.com:443",
"method": "POST",
"path": "/api/rtm.start",
"body": "----------------------------240931670220439824763760\r\nContent-Disposition: form-data; name=\"token\"\r\n\r\nSLACK-SESSION-TOKEN\r\n----------------------------240931670220439824763760--\r\n",
"status": 200,
"response": {
"ok": true,
"channels": [
{
"id": "C3Y9NQTQG",
"name": "channel"
}
]
}
},
{
"scope": "https://workspace.slack.com:443",
"method": "POST",
"path": "/api/files.share",
"body": "----------------------------898525272439246643720391\r\nContent-Disposition: form-data; name=\"token\"\r\n\r\nSLACK-SESSION-TOKEN\r\n----------------------------898525272439246643720391\r\nContent-Disposition: form-data; name=\"files\"\r\n\r\nF029QHN0AAZ,F02AH8J752L\r\n----------------------------898525272439246643720391\r\nContent-Disposition: form-data; name=\"channel\"\r\n\r\nC3Y9NQTQG\r\n----------------------------898525272439246643720391--\r\n",
"status": 200,
"response": {
"ok": true
},
"responseIsBinary": false
}
]

0 comments on commit 55d7479

Please sign in to comment.