basic-implementation #1
135
package-lock.json
generated
|
@ -11,7 +11,9 @@
|
|||
"dependencies": {
|
||||
"@types/chai": "^4.2.18",
|
||||
"@types/mocha": "^8.2.2",
|
||||
"@types/nock": "^11.1.0",
|
||||
"@types/node": "^15.3.0",
|
||||
"@types/node-fetch": "^2.5.10",
|
||||
"@types/sinon-chai": "^3.2.5",
|
||||
"@typescript-eslint/eslint-plugin": "^4.23.0",
|
||||
"@typescript-eslint/parser": "^4.23.0",
|
||||
|
@ -23,10 +25,14 @@
|
|||
"eslint-plugin-import": "^2.23.2",
|
||||
"eslint-plugin-mocha": "^8.1.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"form-data": "^3.0.1",
|
||||
"mocha": "^8.4.0",
|
||||
"nock": "^13.0.11",
|
||||
"node-fetch": "^2.6.1",
|
||||
"prettier": "^2.3.0",
|
||||
"sinon-chai": "^3.6.0",
|
||||
"tmp-promise": "^3.0.2",
|
||||
"ts-dedent": "^2.1.1",
|
||||
"ts-node": "^9.1.1",
|
||||
"ts-sinon": "^2.0.1",
|
||||
"tsc-watch": "^4.2.9",
|
||||
|
@ -265,11 +271,29 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz",
|
||||
"integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw=="
|
||||
},
|
||||
"node_modules/@types/nock": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/nock/-/nock-11.1.0.tgz",
|
||||
"integrity": "sha512-jI/ewavBQ7X5178262JQR0ewicPAcJhXS/iFaNJl0VHLfyosZ/kwSrsa6VNQNSO8i9d8SqdRgOtZSOKJ/+iNMw==",
|
||||
"deprecated": "This is a stub types definition. nock provides its own type definitions, so you do not need this installed.",
|
||||
"dependencies": {
|
||||
"nock": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "15.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.3.0.tgz",
|
||||
"integrity": "sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ=="
|
||||
},
|
||||
"node_modules/@types/node-fetch": {
|
||||
"version": "2.5.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.10.tgz",
|
||||
"integrity": "sha512-IpkX0AasN44hgEad0gEF/V6EgR5n69VEqPEgnmoM8GsIGro3PowbWs4tR6IhxUTyPLpOn+fiGG6nrQhcmoCuIQ==",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"form-data": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/sinon": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.0.tgz",
|
||||
|
@ -1512,6 +1536,19 @@
|
|||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz",
|
||||
"integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA=="
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
|
||||
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/from": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
|
||||
|
@ -1972,6 +2009,11 @@
|
|||
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
|
||||
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="
|
||||
},
|
||||
"node_modules/json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
|
@ -2041,6 +2083,11 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
|
||||
},
|
||||
"node_modules/lodash.set": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
|
||||
"integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM="
|
||||
},
|
||||
"node_modules/lodash.truncate": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
|
||||
|
@ -2317,6 +2364,20 @@
|
|||
"@sinonjs/commons": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nock": {
|
||||
"version": "13.0.11",
|
||||
"resolved": "https://registry.npmjs.org/nock/-/nock-13.0.11.tgz",
|
||||
"integrity": "sha512-sKZltNkkWblkqqPAsjYW0bm3s9DcHRPiMOyKO/PkfJ+ANHZ2+LA2PLe22r4lLrKgXaiSaDQwW3qGsJFtIpQIeQ==",
|
||||
"dependencies": {
|
||||
"debug": "^4.1.0",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"lodash.set": "^4.3.2",
|
||||
"propagate": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13"
|
||||
}
|
||||
},
|
||||
"node_modules/node-cleanup": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz",
|
||||
|
@ -2677,6 +2738,14 @@
|
|||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/propagate": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz",
|
||||
"integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/ps-tree": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz",
|
||||
|
@ -3240,6 +3309,14 @@
|
|||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-dedent": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.1.1.tgz",
|
||||
"integrity": "sha512-riHuwnzAUCfdIeTBNUq7+Yj+ANnrMXo/7+Z74dIdudS7ys2k8aSGMzpJRMFDF7CLwUTbtvi1ZZff/Wl+XxmqIA==",
|
||||
"engines": {
|
||||
"node": ">=6.10"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
|
||||
|
@ -3861,11 +3938,28 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz",
|
||||
"integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw=="
|
||||
},
|
||||
"@types/nock": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/nock/-/nock-11.1.0.tgz",
|
||||
"integrity": "sha512-jI/ewavBQ7X5178262JQR0ewicPAcJhXS/iFaNJl0VHLfyosZ/kwSrsa6VNQNSO8i9d8SqdRgOtZSOKJ/+iNMw==",
|
||||
"requires": {
|
||||
"nock": "*"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "15.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.3.0.tgz",
|
||||
"integrity": "sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ=="
|
||||
},
|
||||
"@types/node-fetch": {
|
||||
"version": "2.5.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.10.tgz",
|
||||
"integrity": "sha512-IpkX0AasN44hgEad0gEF/V6EgR5n69VEqPEgnmoM8GsIGro3PowbWs4tR6IhxUTyPLpOn+fiGG6nrQhcmoCuIQ==",
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"form-data": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"@types/sinon": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.0.tgz",
|
||||
|
@ -4771,6 +4865,16 @@
|
|||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz",
|
||||
"integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA=="
|
||||
},
|
||||
"form-data": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
|
||||
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"from": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
|
||||
|
@ -5085,6 +5189,11 @@
|
|||
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
|
||||
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||
},
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
|
@ -5142,6 +5251,11 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
|
||||
},
|
||||
"lodash.set": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
|
||||
"integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM="
|
||||
},
|
||||
"lodash.truncate": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
|
||||
|
@ -5348,6 +5462,17 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"nock": {
|
||||
"version": "13.0.11",
|
||||
"resolved": "https://registry.npmjs.org/nock/-/nock-13.0.11.tgz",
|
||||
"integrity": "sha512-sKZltNkkWblkqqPAsjYW0bm3s9DcHRPiMOyKO/PkfJ+ANHZ2+LA2PLe22r4lLrKgXaiSaDQwW3qGsJFtIpQIeQ==",
|
||||
"requires": {
|
||||
"debug": "^4.1.0",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"lodash.set": "^4.3.2",
|
||||
"propagate": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node-cleanup": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz",
|
||||
|
@ -5595,6 +5720,11 @@
|
|||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="
|
||||
},
|
||||
"propagate": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz",
|
||||
"integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag=="
|
||||
},
|
||||
"ps-tree": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz",
|
||||
|
@ -5996,6 +6126,11 @@
|
|||
"is-number": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"ts-dedent": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.1.1.tgz",
|
||||
"integrity": "sha512-riHuwnzAUCfdIeTBNUq7+Yj+ANnrMXo/7+Z74dIdudS7ys2k8aSGMzpJRMFDF7CLwUTbtvi1ZZff/Wl+XxmqIA=="
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
|
||||
|
|
|
@ -26,7 +26,9 @@
|
|||
"dependencies": {
|
||||
"@types/chai": "^4.2.18",
|
||||
"@types/mocha": "^8.2.2",
|
||||
"@types/nock": "^11.1.0",
|
||||
"@types/node": "^15.3.0",
|
||||
"@types/node-fetch": "^2.5.10",
|
||||
"@types/sinon-chai": "^3.2.5",
|
||||
"@typescript-eslint/eslint-plugin": "^4.23.0",
|
||||
"@typescript-eslint/parser": "^4.23.0",
|
||||
|
@ -38,10 +40,14 @@
|
|||
"eslint-plugin-import": "^2.23.2",
|
||||
"eslint-plugin-mocha": "^8.1.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"form-data": "^3.0.1",
|
||||
"mocha": "^8.4.0",
|
||||
"nock": "^13.0.11",
|
||||
"node-fetch": "^2.6.1",
|
||||
"prettier": "^2.3.0",
|
||||
"sinon-chai": "^3.6.0",
|
||||
"tmp-promise": "^3.0.2",
|
||||
"ts-dedent": "^2.1.1",
|
||||
"ts-node": "^9.1.1",
|
||||
"ts-sinon": "^2.0.1",
|
||||
"tsc-watch": "^4.2.9",
|
||||
|
|
|
@ -1,44 +1,113 @@
|
|||
import { Channel, Client } from 'discord.js';
|
||||
import { isBooleanObject } from 'util/types';
|
||||
import { Client, TextChannel } from 'discord.js';
|
||||
import dedent from 'ts-dedent';
|
||||
import { ImplementableApi } from './implementableApi';
|
||||
|
||||
namespace DiscordParser {
|
||||
export type Config = ImplementableApi.Config & {
|
||||
token: string;
|
||||
channelsId: {
|
||||
idChannelYtb: string;
|
||||
idChannelPeerTube: string;
|
||||
};
|
||||
keyWord: string;
|
||||
};
|
||||
}
|
||||
|
||||
///Penser a split mes event peertube Ytb en 2 channel differents
|
||||
class DiscordParser extends ImplementableApi {
|
||||
readonly token: string;
|
||||
readonly channelid: string;
|
||||
client: Client;
|
||||
readonly keyWord: string;
|
||||
readonly channels: {
|
||||
readonly ChannelYtb: TextChannel;
|
||||
readonly ChannelPeerTube: TextChannel;
|
||||
};
|
||||
readonly client: Client;
|
||||
|
||||
constructor(readonly config: DiscordParser.Config) {
|
||||
super(config);
|
||||
this.token = config.token;
|
||||
// this.settingEvents();
|
||||
this.client = new Client();
|
||||
this.instantiateClient();
|
||||
this.keyWord = config.keyWord;
|
||||
this.settingEvents();
|
||||
this.client = this.instantiateClient();
|
||||
this.channels = this.setChannel(config.channelsId);
|
||||
}
|
||||
|
||||
private instantiateClient() {
|
||||
this.client.login(this.token);
|
||||
private setChannel(ids: {
|
||||
|
||||
[key: string]: string;
|
||||
idChannelYtb: string;
|
||||
florent
commented
`youtubeChannel` / `peertubeChannel` comme nom
|
||||
idChannelPeerTube: string;
|
||||
}): {
|
||||
ChannelYtb: TextChannel;
|
||||
ChannelPeerTube: TextChannel;
|
||||
florent
commented
```ts
type Channels = {
channelYtb: TextChannel;
channelPeerTube: TextChannel;
};
```
```ts
static async instanciate(readonly config: DiscordParser.Config) {
const client = new Client();
client.login(this.token);
await eventsOnce(client, 'ready');
const channels = {
channelYtb: this.client.channels.resolve("..."),
channelPeerTube: ...
};
return new Discord({
client,
channels,
keyword
});
```
amaury.joly
commented
I made a thing like this
The problem with this is for the usage inside the router.
So I have again an async function inside my constructor, and to solved this I need to create the same pattern inside the router class. We could try maybe an events who's called at the the end of the constructor. (I took this idea from DiscordJS)
With this, the restriction is to call the event 'ready' at the end of the constrctor's tasks. It's looking more friendly than the Dependency Injection for a beginner who would to make his own ImplementableAPI. I made a thing like this
```ts
namespace DiscordParser {
/*...*/
export type ConstructorPattern = ImplementableApi.Config & {
keyWord: string;
channels: {
ChannelYtb: TextChannel;
ChannelPeerTube: TextChannel;
};
client: Client;
};
}
/*...*/
class DiscordParser extends ImplementableApi {
readonly keyWord: string;
readonly channels: {
[key: string]: TextChannel;
ChannelYtb: TextChannel;
ChannelPeerTube: TextChannel;
};
readonly client: Client;
constructor(readonly config: DiscordParser.ConstructorPattern) {
super(config);
this.keyWord = config.keyWord;
this.channels = config.channels;
this.client = config.client;
this.settingEvents();
}
static async instanciate(
config: DiscordParser.Config
): Promise<DiscordParser.ConstructorPattern> {
const client = new Client();
client.login(config.token);
await eventsOnce(client, 'ready');
const channels: ChannelsType = {
ChannelPeerTube: this.getTextChannel(
client,
config.channelsId.idChannelPeerTube
),
ChannelYtb: this.getTextChannel(
client,
config.channelsId.idChannelYtb
),
};
return {
name: config.name,
channels: channels,
client: client,
keyWord: config.keyWord,
};
}
private static getTextChannel(client: Client, id: string): TextChannel {
const channel = client.channels.resolve(id);
if (!channel || !(channel instanceof TextChannel))
throw 'Bad token or bad channel id. Be careful, the channel must be a TextChannel';
return channel;
}
/*...*/
};
```
The problem with this is for the usage inside the router.
I need to make a thing like this :
```ts
class Router {
api_array: { [key: string]: ImplementableApi } = {};
readonly routes: Router.Config;
constructor(readonly config: Router.GlobalConfig) {
this.routes = config.router;
this.api_array[config.discord.name] = new DiscordParser(
await DiscordParser.instanciate(config.discord)
);
this.api_array[config.peertubeRequester.name] = new PeerTubeRequester(
config.peertubeRequester
);
this.api_array[config.logWriter.name] = new LogWriter(config.logWriter);
}
/*...*/
}
```
So I have again an async function inside my constructor, and to solved this I need to create the same pattern inside the router class.
And by extension i need to generalize this for my ImplementableAPI class. It's included to force all the users of the ImplementableAPI to make an Dependence Injection Pattern for their API. And it's look a little bit restrictive to me.
We could try maybe an events who's called at the the end of the constructor. (I took this idea from DiscordJS)
Like that we could imagine a thing like this :
```ts
class MyClass extends ImplementableAPI {
constructor(...) {
super(...);
const promise = startAnAsynchronousTask(...);
doSomeSynchronousTask(...);
promise.then(() = > {
this.emit('ready');
});
}
private startAnAsynchronousTask(...) : Promise<void> {
await something(..);
await somethingElse();
await events.once(anObject, 'ready');
}
/*...*/
}
```
With this, the restriction is to call the event 'ready' at the end of the constrctor's tasks. It's looking more friendly than the Dependency Injection for a beginner who would to make his own ImplementableAPI.
amaury.joly
commented
Ok, my bad. I think I missunderstanded the Dependency Injection. Sleep on it. Ok, my bad. I think I missunderstanded the Dependency Injection. Sleep on it.
I'm going to try another thing.
I keep my DiscordParser Class with the instantiate method. And I 'm going to insert the created object inside an method in Router who could be named 'inject' or something in this idea. And who could take an ImplementableAPI instance in params.
|
||||
} {
|
||||
let resp: {
|
||||
[key: string]: TextChannel | undefined;
|
||||
ChannelYtb?: TextChannel;
|
||||
ChannelPeerTube?: TextChannel;
|
||||
} = {};
|
||||
// construct and check the channels
|
||||
for (const key in ids) {
|
||||
if (ids[key]) {
|
||||
const tmpChannel = this.client.channels.resolve(ids[key]);
|
||||
if (tmpChannel)
|
||||
if (tmpChannel instanceof TextChannel)
|
||||
resp[key] = tmpChannel;
|
||||
else throw new Error('The channel must be a TextChannel');
|
||||
else
|
||||
throw new Error(
|
||||
dedent`The channel cannot be found by the bot.
|
||||
Check if you had the bot to your server or if the channel ID is correct`
|
||||
);
|
||||
}
|
||||
}
|
||||
// return the well formed object
|
||||
if (resp.ChannelPeerTube)
|
||||
if (resp.ChannelYtb)
|
||||
return {
|
||||
ChannelPeerTube: resp.ChannelPeerTube,
|
||||
ChannelYtb: resp.ChannelYtb,
|
||||
};
|
||||
throw new Error('Theres an issue concerned the channel check');
|
||||
}
|
||||
|
||||
private instantiateClient(): Client {
|
||||
const newClient = new Client();
|
||||
newClient.login(this.token);
|
||||
return newClient;
|
||||
}
|
||||
|
||||
amaury.joly marked this conversation as resolved
Outdated
florent
commented
```ts
if (!resp.ChannelPeerTube || !resp.ChannelYtb) {
throw new Error('Theres an issue concerned the channel check');
}
return {...};
```
|
||||
public receivedMessage(message: ImplementableApi.Message) {
|
||||
switch (message.type) {
|
||||
case 'newEntriesNotify':
|
||||
this.sendMsgYtb(
|
||||
`New YouTubes entries received :\n${JSON.parse(
|
||||
message.rawContent
|
||||
).items.map((item: any) => `Author : ${item.author}`)}`
|
||||
);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private sendMsg(message: string) {
|
||||
// this.client.channels.resolveID();
|
||||
private sendMsgYtb(message: string) {
|
||||
this.channels.ChannelYtb.send(message);
|
||||
}
|
||||
private sendMsgPeerTube(message: string) {
|
||||
this.channels.ChannelPeerTube.send(message);
|
||||
}
|
||||
|
||||
private settingEvents(): void {
|
||||
throw 'empty';
|
||||
this.client.on('message', (message) => {
|
||||
const msg_splitted = message.content.split(' ');
|
||||
if (msg_splitted[0] === this.keyWord) {
|
||||
// if (!this.channel) {
|
||||
// this.channel = message.channel;
|
||||
// }
|
||||
switch (msg_splitted[1]) {
|
||||
case 'add':
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
florent
commented
Je comprends pas la partie de ce code, il répond à quel cas d'utilisation ? Sinon :
Je comprends pas la partie de ce code, il répond à quel cas d'utilisation ?
Sinon :
```ts
if ( Object.values(this.channels).some(channel => channel.id === message.channel.id) ) {
```
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
import { ImplementableApi } from './implementableApi';
|
||||
// Api request lib
|
||||
import fetch, { FetchError, Headers } from 'node-fetch';
|
||||
import { URLSearchParams } from 'url';
|
||||
import FormData from 'form-data';
|
||||
import dedent from 'ts-dedent';
|
||||
|
||||
namespace PeerTubeRequester {
|
||||
export type Config = ImplementableApi.Config & {
|
||||
|
@ -12,10 +17,6 @@ type UploadInstruction = {
|
|||
[key: string]: string;
|
||||
channelID: string;
|
||||
targetUrl: string;
|
||||
name: string;
|
||||
description: string;
|
||||
originallyPublishedAt: string;
|
||||
thumbnailsfile: string;
|
||||
};
|
||||
|
||||
type ClientToken = {
|
||||
|
@ -46,71 +47,110 @@ class PeerTubeRequester extends ImplementableApi {
|
|||
this.password = config.password;
|
||||
}
|
||||
|
||||
public receivedMessage(message: ImplementableApi.Message) {
|
||||
public async receivedMessage(
|
||||
message: ImplementableApi.Message
|
||||
): Promise<void> {
|
||||
switch (message.type) {
|
||||
case 'newEntriesNotify':
|
||||
this.newEntriesNotify(message);
|
||||
await this.newEntriesNotify(message);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private newEntriesNotify(message: ImplementableApi.Message) {
|
||||
private async newEntriesNotify(
|
||||
message: ImplementableApi.Message
|
||||
): Promise<void> {
|
||||
// parse content
|
||||
const items = message.rawContent['items'];
|
||||
if (Array.isArray(items))
|
||||
items.forEach((item) => {
|
||||
for (const item of items) {
|
||||
const media_group = item['media:group'];
|
||||
const args: UploadInstruction = {
|
||||
channelID: 'undefined', // to do binding avec les idDeChaines Skeptikom
|
||||
description: media_group['media:description'][0],
|
||||
name: media_group['media:title'][0],
|
||||
originallyPublishedAt: item.pubDate,
|
||||
targetUrl: media_group['media:content'][0]['$']['url'],
|
||||
thumbnailsfile:
|
||||
media_group['media:thumbnail'][0]['$']['url'],
|
||||
targetUrl: item.link,
|
||||
};
|
||||
this.apiRequest(args);
|
||||
});
|
||||
await this.apiRequest(args);
|
||||
}
|
||||
}
|
||||
|
||||
private async apiRequest(message: UploadInstruction) {
|
||||
// Auth
|
||||
const client_info: ClientToken = {
|
||||
...(await (
|
||||
await fetch(
|
||||
`https://${this.domain_name}/api/v1/oauth-clients/local`
|
||||
)
|
||||
).json()),
|
||||
...{
|
||||
grant_type: 'password',
|
||||
response_type: 'code',
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
},
|
||||
private async apiRequest(message: UploadInstruction): Promise<void> {
|
||||
let response = await fetch(
|
||||
`https://${this.domain_name}/api/v1/oauth-clients/local`
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(response.statusText); // CRASH
|
||||
}
|
||||
const { client_id, client_secret } = await response.json();
|
||||
|
||||
const client_info: { [key: string]: string } = {
|
||||
client_id,
|
||||
client_secret,
|
||||
grant_type: 'password',
|
||||
response_type: 'code',
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
};
|
||||
|
||||
const myAuthForm = new FormData();
|
||||
for (const key in client_info) myAuthForm.append(key, message[key]);
|
||||
let myParams = new URLSearchParams();
|
||||
for (const key in client_info) myParams.append(key, client_info[key]);
|
||||
|
||||
const tokens_info: UserToken = await (
|
||||
await fetch(`https://${this.domain_name}/api/v1/users/token`, {
|
||||
method: 'get',
|
||||
body: myAuthForm,
|
||||
})
|
||||
).json();
|
||||
response = await fetch(
|
||||
`https://${this.domain_name}/api/v1/users/token`,
|
||||
{
|
||||
method: 'post',
|
||||
body: myParams,
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(response.statusText); // CRASH
|
||||
}
|
||||
const { access_token } = await response.json();
|
||||
|
||||
// Upload
|
||||
florent
commented
Un commentaire ici, c'est compenser à mon sens un problème sémantique dans le code. J'aurais tendance à faire deux fonctions privées Un commentaire ici, c'est compenser à mon sens un problème sémantique dans le code.
J'aurais tendance à faire deux fonctions privées `this.authenticate()` et `this.upload()`
|
||||
const myUploadForm = new FormData();
|
||||
const myUploadForm = new URLSearchParams();
|
||||
const myHeader = new Headers();
|
||||
myHeader.append('Authorization', `Bearer ${tokens_info.access_token}`);
|
||||
myHeader.append('Authorization', `Bearer ${access_token}`);
|
||||
for (const key in message) myUploadForm.append(key, message[key]);
|
||||
await fetch(`https://${this.domain_name}/api/v1/videos/imports`, {
|
||||
method: 'post',
|
||||
headers: myHeader,
|
||||
body: myUploadForm,
|
||||
});
|
||||
|
||||
response = await fetch(
|
||||
`https://${this.domain_name}/api/v1/videos/imports`,
|
||||
{
|
||||
method: 'post',
|
||||
headers: myHeader,
|
||||
body: myUploadForm,
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
switch (response.status) {
|
||||
case 400:
|
||||
throw new Error(
|
||||
dedent`Your target URL was not accepted by the API.\
|
||||
Actualy it's : ${message.targetUrl}
|
||||
${response.statusText}`
|
||||
);
|
||||
break;
|
||||
case 403:
|
||||
throw new Error(response.statusText);
|
||||
break;
|
||||
case 409:
|
||||
throw new Error(
|
||||
dedent`Oops, your instance had not allowed the HTTPS import.\
|
||||
Contact your administrator.
|
||||
${response.statusText}`
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
dedent`Oh, you resolved an undocumented issues.\
|
||||
Please report this on the git if you have the time.
|
||||
ERROR: ${response.statusText}`
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
55
tests/discord-spec.ts
Normal file
|
@ -0,0 +1,55 @@
|
|||
import chai from 'chai';
|
||||
import sinon from 'ts-sinon';
|
||||
import sinonChai from 'sinon-chai';
|
||||
|
||||
chai.use(sinonChai);
|
||||
const expect = chai.expect;
|
||||
|
||||
import nock, { disableNetConnect, RequestBodyMatcher } from 'nock';
|
||||
|
||||
import { DiscordParser } from '../src/discordParser';
|
||||
|
||||
//data
|
||||
const config: DiscordParser.Config = {
|
||||
channelsId: {
|
||||
idChannelPeerTube: 'peertubeID',
|
||||
idChannelYtb: 'ytbChannel',
|
||||
},
|
||||
keyWord: 'myDiscordKeyword',
|
||||
name: 'DiscordTestedInterface',
|
||||
token: 'mySecretDiscordToken',
|
||||
};
|
||||
|
||||
//stubed imports
|
||||
import { Channel, ChannelManager, Client } from 'discord.js';
|
||||
import { utils } from 'mocha';
|
||||
|
||||
describe.only('test DiscordParser', function () {
|
||||
let clientStub: sinon.SinonStubbedInstance<Client>,
|
||||
channelManagerStub: sinon.SinonStubbedInstance<ChannelManager>;
|
||||
before(() => {
|
||||
clientStub = sinon.createStubInstance(Client);
|
||||
clientStub.login.withArgs(config.token).onFirstCall().resolves('ok');
|
||||
clientStub.login.throws('Error, bad parameter or too much call');
|
||||
|
||||
channelManagerStub = sinon.createStubInstance(ChannelManager);
|
||||
channelManagerStub.resolve
|
||||
.withArgs(config.channelsId.idChannelPeerTube)
|
||||
.onFirstCall()
|
||||
.returns(new Channel(new Client()))
|
||||
.withArgs(config.channelsId.idChannelYtb)
|
||||
.onFirstCall()
|
||||
.returns(new Channel(new Client()));
|
||||
channelManagerStub.resolve.throws("Error Bad id's or too much call");
|
||||
});
|
||||
|
||||
after(() => {
|
||||
clientStub.login.restore();
|
||||
channelManagerStub.resolve.restore();
|
||||
});
|
||||
it('it will test the DiscordParser constructor', function () {
|
||||
const discordParser = new DiscordParser(config);
|
||||
|
||||
console.log("c'est bon signe");
|
||||
});
|
||||
});
|
|
@ -1,54 +1,54 @@
|
|||
import chai from "chai";
|
||||
import sinon from "ts-sinon";
|
||||
import sinonChai from "sinon-chai";
|
||||
import chai from 'chai';
|
||||
import sinon from 'ts-sinon';
|
||||
import sinonChai from 'sinon-chai';
|
||||
|
||||
chai.use(sinonChai)
|
||||
chai.use(sinonChai);
|
||||
const expect = chai.expect;
|
||||
|
||||
import { withFile } from "tmp-promise"
|
||||
import { writeFileSync } from "fs";
|
||||
|
||||
import { Router } from "../src/index";
|
||||
import { DiscordParser } from "../src/discordParser";
|
||||
import { LogWriter } from "../src/logWriter";
|
||||
import { PeerTubeRequester } from "../src/peertubeRequester";
|
||||
import { withFile } from 'tmp-promise';
|
||||
import { writeFileSync } from 'fs';
|
||||
|
||||
import { Router } from '../src/index';
|
||||
import { DiscordParser } from '../src/discordParser';
|
||||
import { LogWriter } from '../src/logWriter';
|
||||
import { PeerTubeRequester } from '../src/peertubeRequester';
|
||||
|
||||
// const path = require("path");
|
||||
const well_build_routing_file: Router.GlobalConfig = require("./rsrc/wellBuildedRoutingFile.json");
|
||||
const well_build_routing_file: Router.GlobalConfig = require('./rsrc/wellBuildedRoutingFile.json');
|
||||
|
||||
describe("testing the routing part", function () {
|
||||
describe("testing the building part", function () {
|
||||
it("it will test a normal building", async function () {
|
||||
await withFile(async (file) => {
|
||||
const edit_config = {
|
||||
...well_build_routing_file,
|
||||
logWriter: {
|
||||
...well_build_routing_file.logWriter,
|
||||
...{path: file.path}
|
||||
}
|
||||
};
|
||||
const r = new Router(edit_config);
|
||||
describe('testing the routing part', function () {
|
||||
describe('testing the building part', function () {
|
||||
it('it will test a normal building', async function () {
|
||||
await withFile(
|
||||
async (file) => {
|
||||
const edit_config = {
|
||||
...well_build_routing_file,
|
||||
logWriter: {
|
||||
...well_build_routing_file.logWriter,
|
||||
...{ path: file.path },
|
||||
},
|
||||
};
|
||||
const r = new Router(edit_config);
|
||||
|
||||
expect(r.api_array['Discord']).to.be.instanceOf(DiscordParser);
|
||||
expect(r.api_array['logWriter']).to.be.instanceOf(LogWriter);
|
||||
expect(r.api_array['peertubeRequester']).to.be.instanceOf(PeerTubeRequester);
|
||||
|
||||
}, {postfix: '.log'})
|
||||
|
||||
})
|
||||
})
|
||||
describe("testing the data transmission", function () {
|
||||
it("it will emit a upload request message", function () {
|
||||
|
||||
})
|
||||
it("it will emit a new listener request", function () {
|
||||
|
||||
})
|
||||
})
|
||||
describe("testing the data reception", function () {
|
||||
it("it will received a new entries notification", function() {
|
||||
|
||||
})
|
||||
})
|
||||
expect(r.api_array['Discord']).to.be.instanceOf(
|
||||
DiscordParser
|
||||
);
|
||||
expect(r.api_array['logWriter']).to.be.instanceOf(
|
||||
LogWriter
|
||||
);
|
||||
expect(r.api_array['peertubeRequester']).to.be.instanceOf(
|
||||
PeerTubeRequester
|
||||
);
|
||||
},
|
||||
{ postfix: '.log' }
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('testing the data transmission', function () {
|
||||
it('it will emit a upload request message', function () {});
|
||||
it('it will emit a new listener request', function () {});
|
||||
});
|
||||
describe('testing the data reception', function () {
|
||||
it('it will received a new entries notification', function () {});
|
||||
});
|
||||
});
|
123
tests/peertubeRequeter-spec.ts
Normal file
|
@ -0,0 +1,123 @@
|
|||
import chai from 'chai';
|
||||
import sinon from 'ts-sinon';
|
||||
import sinonChai from 'sinon-chai';
|
||||
|
||||
chai.use(sinonChai);
|
||||
const expect = chai.expect;
|
||||
|
||||
import nock, { disableNetConnect, RequestBodyMatcher } from 'nock';
|
||||
|
||||
import { ImplementableApi } from '../src';
|
||||
import { PeerTubeRequester } from '../src/peertubeRequester';
|
||||
import { Request } from 'node-fetch';
|
||||
|
||||
const paramsPeerTube: PeerTubeRequester.Config = {
|
||||
name: 'testedRequester',
|
||||
domain_name: 'myApiAddress.yolo',
|
||||
password: 'mySuperSecretPassphrase',
|
||||
username: 'myUsername',
|
||||
};
|
||||
|
||||
const newEntriesMessage: ImplementableApi.Message = {
|
||||
type: 'newEntriesNotify',
|
||||
rawContent: {
|
||||
items: [
|
||||
{
|
||||
author: 'channel1',
|
||||
link: 'link1',
|
||||
title: 'title1',
|
||||
},
|
||||
{
|
||||
author: 'channel2',
|
||||
link: 'link2',
|
||||
title: 'title2',
|
||||
},
|
||||
{
|
||||
author: 'channel3',
|
||||
link: 'link3',
|
||||
title: 'title3',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const UploadInstruction = {
|
||||
channelID: 'undefined', //todo uncompleted test but incompleted function too
|
||||
targetUrl: 'myTargerUrl',
|
||||
};
|
||||
|
||||
// nock data
|
||||
const expectedReplyOauthClient = {
|
||||
client_id: 'expectedClientID',
|
||||
client_secret: 'expectedClientSecret',
|
||||
};
|
||||
|
||||
const expectedReplyTokenAddress = {
|
||||
access_token: 'expectedAccessToken',
|
||||
};
|
||||
|
||||
const bodyTokenRequest: RequestBodyMatcher = {
|
||||
client_id: expectedReplyOauthClient.client_id,
|
||||
client_secret: expectedReplyOauthClient.client_secret,
|
||||
grant_type: 'password',
|
||||
response_type: 'code',
|
||||
username: paramsPeerTube.username,
|
||||
password: paramsPeerTube.password,
|
||||
};
|
||||
describe('PeerTube Requester Test', function () {
|
||||
before(function () {
|
||||
disableNetConnect();
|
||||
});
|
||||
|
||||
after(function () {
|
||||
nock.cleanAll();
|
||||
});
|
||||
|
||||
it('it will test the upload function', async function () {
|
||||
// given
|
||||
const scope = nock(`https://${paramsPeerTube.domain_name}/api/v1`)
|
||||
.get(`/oauth-clients/local`)
|
||||
.times(3)
|
||||
.reply(200, expectedReplyOauthClient)
|
||||
.post(`/users/token`, bodyTokenRequest)
|
||||
.times(3)
|
||||
.reply(200, expectedReplyTokenAddress)
|
||||
.post(`/videos/imports`, {
|
||||
channelID: 'undefined',
|
||||
targeUrl: newEntriesMessage.rawContent.items[0].link,
|
||||
})
|
||||
.matchHeader(
|
||||
'Authorization',
|
||||
`Bearer ${expectedReplyTokenAddress.access_token}`
|
||||
)
|
||||
.reply(200)
|
||||
.post(`/videos/imports`, {
|
||||
channelID: 'undefined',
|
||||
targeUrl: newEntriesMessage.rawContent.items[1].link,
|
||||
})
|
||||
.matchHeader(
|
||||
'Authorization',
|
||||
`Bearer ${expectedReplyTokenAddress.access_token}`
|
||||
)
|
||||
.reply(200)
|
||||
.post(`/videos/imports`, {
|
||||
channelID: 'undefined',
|
||||
targeUrl: newEntriesMessage.rawContent.items[2].link,
|
||||
})
|
||||
.matchHeader(
|
||||
'Authorization',
|
||||
`Bearer ${expectedReplyTokenAddress.access_token}`
|
||||
)
|
||||
.reply(200);
|
||||
|
||||
if (scope.isDone()) console.log(scope.activeMocks());
|
||||
|
||||
const requester = new PeerTubeRequester(paramsPeerTube);
|
||||
|
||||
// when
|
||||
await requester.receivedMessage(newEntriesMessage);
|
||||
|
||||
//expected
|
||||
if (scope.isDone()) console.log(scope.activeMocks());
|
||||
});
|
||||
});
|
Toujours utile ?