basic-implementation #1

Open
florent wants to merge 8 commits from basic-implementation into master
10 changed files with 223 additions and 101 deletions
Showing only changes of commit 7fc2e29f0e - Show all commits

19
package-lock.json generated
View File

@ -39,6 +39,7 @@
"typescript": "^4.2.4" "typescript": "^4.2.4"
}, },
"devDependencies": { "devDependencies": {
"@types/chai-string": "^1.4.2",
"@types/ws": "^7.4.4" "@types/ws": "^7.4.4"
} }
}, },
@ -256,6 +257,15 @@
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.18.tgz", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.18.tgz",
"integrity": "sha512-rS27+EkB/RE1Iz3u0XtVL5q36MGDWbgYe7zWiodyKNUnthxY0rukK5V36eiUCtCisB7NN8zKYH6DO2M37qxFEQ==" "integrity": "sha512-rS27+EkB/RE1Iz3u0XtVL5q36MGDWbgYe7zWiodyKNUnthxY0rukK5V36eiUCtCisB7NN8zKYH6DO2M37qxFEQ=="
}, },
"node_modules/@types/chai-string": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@types/chai-string/-/chai-string-1.4.2.tgz",
"integrity": "sha512-ld/1hV5qcPRGuwlPdvRfvM3Ka/iofOk2pH4VkasK4b1JJP1LjNmWWn0LsISf6RRzyhVOvs93rb9tM09e+UuF8Q==",
"dev": true,
"dependencies": {
"@types/chai": "*"
}
},
"node_modules/@types/json-schema": { "node_modules/@types/json-schema": {
"version": "7.0.7", "version": "7.0.7",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
@ -3923,6 +3933,15 @@
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.18.tgz", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.18.tgz",
"integrity": "sha512-rS27+EkB/RE1Iz3u0XtVL5q36MGDWbgYe7zWiodyKNUnthxY0rukK5V36eiUCtCisB7NN8zKYH6DO2M37qxFEQ==" "integrity": "sha512-rS27+EkB/RE1Iz3u0XtVL5q36MGDWbgYe7zWiodyKNUnthxY0rukK5V36eiUCtCisB7NN8zKYH6DO2M37qxFEQ=="
}, },
"@types/chai-string": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@types/chai-string/-/chai-string-1.4.2.tgz",
"integrity": "sha512-ld/1hV5qcPRGuwlPdvRfvM3Ka/iofOk2pH4VkasK4b1JJP1LjNmWWn0LsISf6RRzyhVOvs93rb9tM09e+UuF8Q==",
"dev": true,
"requires": {
"@types/chai": "*"
}
},
"@types/json-schema": { "@types/json-schema": {
"version": "7.0.7", "version": "7.0.7",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",

View File

@ -54,6 +54,7 @@
"typescript": "^4.2.4" "typescript": "^4.2.4"
}, },
"devDependencies": { "devDependencies": {
"@types/chai-string": "^1.4.2",
"@types/ws": "^7.4.4" "@types/ws": "^7.4.4"
} }
} }

View File

@ -1,7 +1,9 @@
import { Client, TextChannel } from 'discord.js'; import { Client, Message, TextChannel } from 'discord.js';
import dedent from 'ts-dedent'; import dedent from 'ts-dedent';
import { ImplementableApi } from './implementableApi'; import { ImplementableApi } from './implementableApi';
import { once as eventsOnce } from 'events';
namespace DiscordParser { namespace DiscordParser {
export type Config = ImplementableApi.Config & { export type Config = ImplementableApi.Config & {
token: string; token: string;
@ -13,23 +15,28 @@ namespace DiscordParser {
}; };
} }
///Penser a split mes event peertube Ytb en 2 channel differents
class DiscordParser extends ImplementableApi { class DiscordParser extends ImplementableApi {
readonly token: string; readonly token: string;
readonly keyWord: string; readonly keyWord: string;
readonly channels: { readonly channels: Promise<{
readonly ChannelYtb: TextChannel; [key: string]: TextChannel;
readonly ChannelPeerTube: TextChannel; ChannelYtb: TextChannel;
}; ChannelPeerTube: TextChannel;
}>;
readonly client: Client; readonly client: Client;
constructor(readonly config: DiscordParser.Config) { constructor(readonly config: DiscordParser.Config) {
super(config); super(config);
this.token = config.token; this.token = config.token;
this.keyWord = config.keyWord; this.keyWord = config.keyWord;
this.settingEvents();
this.client = this.instantiateClient(); this.client = this.instantiateClient();
this.channels = this.setChannel(config.channelsId); this.channels = eventsOnce(this.client, 'ready').then(() => {
return this.setChannel(config.channelsId);
});
this.channels.then(() => {
this.settingEvents();
});
} }
private setChannel(ids: { private setChannel(ids: {
@ -46,12 +53,14 @@ class DiscordParser extends ImplementableApi {
ChannelPeerTube?: TextChannel; ChannelPeerTube?: TextChannel;
} = {}; } = {};
// construct and check the channels // construct and check the channels
for (const key in ids) { for (const key in ids) {
if (ids[key]) { if (ids[key]) {
// console.log(ids[key]);
const tmpChannel = this.client.channels.resolve(ids[key]); const tmpChannel = this.client.channels.resolve(ids[key]);
if (tmpChannel) if (tmpChannel)
if (tmpChannel instanceof TextChannel) if (tmpChannel instanceof TextChannel)
resp[key] = tmpChannel; resp[key.slice(2)] = tmpChannel;
else throw new Error('The channel must be a TextChannel'); else throw new Error('The channel must be a TextChannel');
else else
throw new Error( throw new Error(
@ -67,6 +76,10 @@ class DiscordParser extends ImplementableApi {
ChannelPeerTube: resp.ChannelPeerTube, ChannelPeerTube: resp.ChannelPeerTube,
ChannelYtb: resp.ChannelYtb, ChannelYtb: resp.ChannelYtb,
}; };
console.log(resp);
// console.log(resp.ChannelYtb);
throw new Error('Theres an issue concerned the channel check'); throw new Error('Theres an issue concerned the channel check');
} }
@ -80,31 +93,49 @@ class DiscordParser extends ImplementableApi {
switch (message.type) { switch (message.type) {
case 'newEntriesNotify': case 'newEntriesNotify':
this.sendMsgYtb( this.sendMsgYtb(
`New YouTubes entries received :\n${JSON.parse( `New YouTubes entries received :\n${message.rawContent.items.map(
message.rawContent (item: any) =>
).items.map((item: any) => `Author : ${item.author}`)}` `Author : ${item.author}\nTitle: ${item.title}\nlink: ${item.link}`
)}`
); );
default: default:
break; break;
} }
} }
private sendMsgYtb(message: string) { private async sendMsgYtb(message: string) {
this.channels.ChannelYtb.send(message); const resolvedChannel = await this.channels;
resolvedChannel.ChannelYtb.send(message);
} }
private sendMsgPeerTube(message: string) { private async sendMsgPeerTube(message: string) {
this.channels.ChannelPeerTube.send(message); const resolvedChannel = await this.channels;
resolvedChannel.ChannelPeerTube.send(message);
} }
private settingEvents(): void { private settingEvents(): void {
this.client.on('message', (message) => { this.on('message', async (message: Message) => {
const resolvedChannel = await this.channels;
let id_arr: string[] = [];
for (const key in this.channels)
id_arr.push(resolvedChannel[key].id);
if (this.channels)
if (id_arr.includes(message.channel.id)) {
const msg_splitted = message.content.split(' '); const msg_splitted = message.content.split(' ');
if (msg_splitted[0] === this.keyWord) { if (msg_splitted[0] === this.keyWord) {
// if (!this.channel) {
// this.channel = message.channel;
// }
switch (msg_splitted[1]) { switch (msg_splitted[1]) {
case 'add': case 'add':
const send_message: ImplementableApi.Message = {
rawContent: {
address: msg_splitted[2],
user: message.author.toString(),
date: message.createdAt.toUTCString(),
},
type: 'newListener',
};
message.channel.send('Ceci est un feedback');
this.emit('addListener', send_message);
}
} }
} }
}); });

View File

@ -12,7 +12,9 @@ namespace LogWriter {
path: string; path: string;
}; };
} }
/**
* check nodejs buffer et throttle
*/
class LogWriter extends ImplementableApi { class LogWriter extends ImplementableApi {
readonly path: string; readonly path: string;
@ -31,14 +33,15 @@ class LogWriter extends ImplementableApi {
private writeMsg(message: ImplementableApi.Message): void; private writeMsg(message: ImplementableApi.Message): void;
private writeMsg(message: ImplementableApi.Message | string) { private writeMsg(message: ImplementableApi.Message | string) {
if (typeof message !== 'string') if (typeof message !== 'string')
message = `[${message.type} :: ${new Date().toLocaleString( message = `[${message.type} :: ${new Date().toISOString()}] ${
'fr-FR' message.rawContent
)}] ${message.rawContent} ${ } ${
message.idListener ?? `( listener_id : ${message.idListener} )` message.idListener ??
`( listener_id : ${message.idListener} )\n`
}`; }`;
const fd = openSync(this.path, 'a'); const fd = openSync(this.path, 'a');
const str = `[${new Date().toLocaleString('fr-FR')}] ${message}`; const str = `[${new Date().toISOString()}] ${message}\n`;
appendFileSync(fd, str); appendFileSync(fd, str);
closeSync(fd); closeSync(fd);
} }

View File

@ -15,7 +15,7 @@ namespace PeerTubeRequester {
type UploadInstruction = { type UploadInstruction = {
[key: string]: string; [key: string]: string;
channelID: string; channelId: string;
targetUrl: string; targetUrl: string;
}; };
@ -68,7 +68,7 @@ class PeerTubeRequester extends ImplementableApi {
for (const item of items) { for (const item of items) {
const media_group = item['media:group']; const media_group = item['media:group'];
const args: UploadInstruction = { const args: UploadInstruction = {
channelID: 'undefined', // to do binding avec les idDeChaines Skeptikom channelId: '848', // to do binding avec les idDeChaines Skeptikom
targetUrl: item.link, targetUrl: item.link,
}; };
await this.apiRequest(args); await this.apiRequest(args);

View File

@ -1,26 +1,26 @@
import { ImplementableApi } from "./implementableApi"; import { ImplementableApi } from './implementableApi';
import { DiscordParser } from './discordParser' import { DiscordParser } from './discordParser';
import { LogWriter } from './logWriter' import { LogWriter } from './logWriter';
import { PeerTubeRequester } from './peertubeRequester' import { PeerTubeRequester } from './peertubeRequester';
namespace Router { namespace Router {
export type Config = { export type Config = {
events: { events: {
name: string, name: string;
type: 0 | 1 type: 'emit' | 'received';
}[]; }[];
apis: { apis: {
apiName: string apiName: string;
}[] }[];
} };
export type GlobalConfig = { export type GlobalConfig = {
router: Config; router: Config;
discord: DiscordParser.Config; discord: DiscordParser.Config;
peertubeRequester: PeerTubeRequester.Config; peertubeRequester: PeerTubeRequester.Config;
logWriter: LogWriter.Config; logWriter: LogWriter.Config;
} };
} }
class Router { class Router {
@ -31,12 +31,16 @@ class Router {
this.routes = config.router; this.routes = config.router;
this.api_array[config.discord.name] = new DiscordParser(config.discord); this.api_array[config.discord.name] = new DiscordParser(config.discord);
this.api_array[config.peertubeRequester.name] = new PeerTubeRequester(config.peertubeRequester); this.api_array[config.peertubeRequester.name] = new PeerTubeRequester(
config.peertubeRequester
);
this.api_array[config.logWriter.name] = new LogWriter(config.logWriter); this.api_array[config.logWriter.name] = new LogWriter(config.logWriter);
} }
public receivedMessage(message: ImplementableApi.Message) { public receivedMessage(message: ImplementableApi.Message) {
this.routes.apis.forEach((api) => this.api_array[api.apiName].receivedMessage(message)) this.routes.apis.forEach((api) =>
this.api_array[api.apiName].receivedMessage(message)
);
} }
} }

View File

@ -1,11 +1,8 @@
import chai from 'chai'; import chai, { expect } from 'chai';
import sinon from 'ts-sinon'; import sinon from 'ts-sinon';
import sinonChai from 'sinon-chai'; import sinonChai from 'sinon-chai';
chai.use(sinonChai); chai.use(sinonChai);
const expect = chai.expect;
import nock, { disableNetConnect, RequestBodyMatcher } from 'nock';
import { DiscordParser } from '../src/discordParser'; import { DiscordParser } from '../src/discordParser';
@ -20,36 +17,61 @@ const config: DiscordParser.Config = {
token: 'mySecretDiscordToken', token: 'mySecretDiscordToken',
}; };
//stubed imports //mockeded imports
import { Channel, ChannelManager, Client } from 'discord.js'; import { Channel, ChannelManager, Client } from 'discord.js';
import { utils } from 'mocha';
describe.only('test DiscordParser', function () { describe.skip('test DiscordParser', function () {
let clientStub: sinon.SinonStubbedInstance<Client>, let clientMockOn: sinon.SinonStub,
channelManagerStub: sinon.SinonStubbedInstance<ChannelManager>; clientMockLogin: sinon.SinonStub,
channelStub: sinon.SinonStubbedInstance<Channel>,
channelManagerMockResolve: sinon.SinonStub;
before(() => { before(() => {
clientStub = sinon.createStubInstance(Client); clientMockOn = sinon.stub(Client.prototype, 'on');
clientStub.login.withArgs(config.token).onFirstCall().resolves('ok'); clientMockOn.withArgs('message', sinon.match.func).onFirstCall();
clientStub.login.throws('Error, bad parameter or too much call'); clientMockOn.throws('Error, bad parameter or too much call');
channelManagerStub = sinon.createStubInstance(ChannelManager); clientMockLogin = sinon.stub(Client.prototype, 'login');
channelManagerStub.resolve clientMockLogin
.withArgs(config.token)
.onFirstCall()
.resolves('ok')
.returnsThis();
clientMockLogin.throws('Error, bad parameter or too much call');
channelStub = sinon.createStubInstance(Channel);
channelManagerMockResolve = sinon.stub(
ChannelManager.prototype,
'resolve'
);
channelManagerMockResolve
.withArgs(config.channelsId.idChannelPeerTube) .withArgs(config.channelsId.idChannelPeerTube)
.onFirstCall() .onFirstCall()
.returns(new Channel(new Client())) .returns(channelStub);
channelManagerMockResolve
.withArgs(config.channelsId.idChannelYtb) .withArgs(config.channelsId.idChannelYtb)
.onFirstCall() .onFirstCall()
.returns(new Channel(new Client())); .returns(channelStub);
channelManagerStub.resolve.throws("Error Bad id's or too much call"); channelManagerMockResolve.throws("Error Bad id's or too much call");
}); });
after(() => { after(() => {
clientStub.login.restore(); clientMockOn.restore();
channelManagerStub.resolve.restore(); clientMockLogin.restore();
channelManagerMockResolve.restore();
}); });
it('it will test the DiscordParser constructor', function () {
it('it will test the DiscordParser constructor', async function () {
//when
const discordParser = new DiscordParser(config); const discordParser = new DiscordParser(config);
console.log("c'est bon signe"); // expect
expect(discordParser.token).to.be.eql(config.token);
await discordParser.channels.then((channels) => {
expect(channels.ChannelYtb.id).to.be.eql(
config.channelsId.idChannelYtb
);
});
}); });
}); });

View File

@ -13,7 +13,6 @@ import { DiscordParser } from '../src/discordParser';
import { LogWriter } from '../src/logWriter'; import { LogWriter } from '../src/logWriter';
import { PeerTubeRequester } from '../src/peertubeRequester'; 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 routing part', function () {
@ -30,9 +29,9 @@ describe('testing the routing part', function () {
}; };
const r = new Router(edit_config); const r = new Router(edit_config);
expect(r.api_array['Discord']).to.be.instanceOf( // expect(r.api_array['Discord']).to.be.instanceOf(
DiscordParser // DiscordParser
); // );
expect(r.api_array['logWriter']).to.be.instanceOf( expect(r.api_array['logWriter']).to.be.instanceOf(
LogWriter LogWriter
); );

43
tests/logWriter-spec.ts Normal file
View File

@ -0,0 +1,43 @@
import chai, { expect } from 'chai';
import sinon from 'ts-sinon';
import sinonChai from 'sinon-chai';
chai.use(sinonChai);
import { withDir } from 'tmp-promise';
import { existsSync, readFileSync } from 'fs';
import { LogWriter } from '../src/logWriter';
const config: LogWriter.Config = {
name: 'logWirterTested',
path: 'it will be set by tmp-promise',
};
describe('test logWriter', function () {
describe('constructor', function () {
it('will test the constructor with a new file', async function () {
await withDir(
async (dir) => {
const log_writer = new LogWriter({
...config,
...{ path: dir.path + '/toto.log' },
});
expect(existsSync(dir.path + '/toto.log')).to.be.true;
expect(
readFileSync(dir.path + '/toto.log', {
encoding: 'utf-8',
})
).to.match(/LogWriter is running\n+$/g);
},
{ unsafeCleanup: true }
);
});
});
describe('received message', function () {
it('will test the print function', function () {});
});
});
//presenter le projet . listener -> import sous forme de plugin

View File

@ -9,7 +9,6 @@ import nock, { disableNetConnect, RequestBodyMatcher } from 'nock';
import { ImplementableApi } from '../src'; import { ImplementableApi } from '../src';
import { PeerTubeRequester } from '../src/peertubeRequester'; import { PeerTubeRequester } from '../src/peertubeRequester';
import { Request } from 'node-fetch';
const paramsPeerTube: PeerTubeRequester.Config = { const paramsPeerTube: PeerTubeRequester.Config = {
name: 'testedRequester', name: 'testedRequester',
@ -42,7 +41,7 @@ const newEntriesMessage: ImplementableApi.Message = {
}; };
const UploadInstruction = { const UploadInstruction = {
channelID: 'undefined', //todo uncompleted test but incompleted function too channelId: 'undefined', //todo uncompleted test but incompleted function too
targetUrl: 'myTargerUrl', targetUrl: 'myTargerUrl',
}; };
@ -64,6 +63,7 @@ const bodyTokenRequest: RequestBodyMatcher = {
username: paramsPeerTube.username, username: paramsPeerTube.username,
password: paramsPeerTube.password, password: paramsPeerTube.password,
}; };
describe('PeerTube Requester Test', function () { describe('PeerTube Requester Test', function () {
before(function () { before(function () {
disableNetConnect(); disableNetConnect();
@ -81,36 +81,34 @@ describe('PeerTube Requester Test', function () {
.reply(200, expectedReplyOauthClient) .reply(200, expectedReplyOauthClient)
.post(`/users/token`, bodyTokenRequest) .post(`/users/token`, bodyTokenRequest)
.times(3) .times(3)
.reply(200, expectedReplyTokenAddress) .reply(200, expectedReplyTokenAddress);
.post(`/videos/imports`, { const import_scope = nock(
channelID: 'undefined', `https://${paramsPeerTube.domain_name}/api/v1`
targeUrl: newEntriesMessage.rawContent.items[0].link, )
})
.matchHeader( .matchHeader(
'Authorization', 'authorization',
`Bearer ${expectedReplyTokenAddress.access_token}` `Bearer ${expectedReplyTokenAddress.access_token}`
) )
.reply(200) .post(`/videos/imports`, (reqBody) => {
.post(`/videos/imports`, { let links: string[] = newEntriesMessage.rawContent.items.map(
channelID: 'undefined', (item: any) => item.link
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 body = new URLSearchParams(reqBody);
if (body.get('channelId') === 'undefined') {
const targUrl = body.get('targetUrl');
if (targUrl && links.includes(targUrl)) {
const index = links.findIndex(
(elmt) => elmt === targUrl
);
links.splice(index, 1);
return true;
}
}
return false;
})
.times(3)
.reply(200);
const requester = new PeerTubeRequester(paramsPeerTube); const requester = new PeerTubeRequester(paramsPeerTube);
@ -118,6 +116,8 @@ describe('PeerTube Requester Test', function () {
await requester.receivedMessage(newEntriesMessage); await requester.receivedMessage(newEntriesMessage);
//expected //expected
if (scope.isDone()) console.log(scope.activeMocks()); // all the scope need to be completed
expect(scope.isDone()).to.be.true;
expect(import_scope.isDone()).to.be.true;
}); });
}); });