basic-implementation #1

Open
florent wants to merge 8 commits from basic-implementation into master
4 changed files with 96 additions and 3884 deletions
Showing only changes of commit d1f1165136 - Show all commits

3798
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,80 +13,69 @@ namespace DiscordParser {
}; };
keyWord: string; keyWord: string;
}; };
export type ConstructorPattern = ImplementableApi.Config & {
botPrefix: string;
channels: {
ChannelYtb: TextChannel;
ChannelPeerTube: TextChannel;
};
client: Client;
};
} }
type ChannelsType = {
ChannelYtb: TextChannel;
ChannelPeerTube: TextChannel;
};
class DiscordParser extends ImplementableApi { class DiscordParser extends ImplementableApi {
readonly token: string; readonly botPrefix: string;
readonly keyWord: string; readonly channels: {
readonly channels: Promise<{
[key: string]: TextChannel; [key: string]: TextChannel;

Toujours utile ?

Toujours utile ?
ChannelYtb: TextChannel; ChannelYtb: TextChannel;
ChannelPeerTube: TextChannel; ChannelPeerTube: TextChannel;

youtubeChannel / peertubeChannel comme nom

`youtubeChannel` / `peertubeChannel` comme nom
}>; };
readonly client: Client; readonly client: Client;
constructor(readonly config: DiscordParser.Config) { constructor(readonly config: DiscordParser.ConstructorPattern) {
type Channels = {
  channelYtb: TextChannel;
  channelPeerTube: TextChannel;
};

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
  });
```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 }); ```

I made a thing like this

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 :

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 :

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.

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.

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.

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.
super(config); super(config);
this.token = config.token; this.botPrefix = config.botPrefix;
this.keyWord = config.keyWord; this.channels = config.channels;
this.client = config.client;
this.client = this.instantiateClient(); this.settingEvents();
this.channels = eventsOnce(this.client, 'ready').then(() => {
return this.setChannel(config.channelsId);
});
this.channels.then(() => {
this.settingEvents();
});
} }
private setChannel(ids: { static async instanciate(
[key: string]: string; config: DiscordParser.Config
idChannelYtb: string; ): Promise<DiscordParser.ConstructorPattern> {
idChannelPeerTube: string; const client = new Client();
}): { client.login(config.token);
ChannelYtb: TextChannel; await eventsOnce(client, 'ready');
ChannelPeerTube: TextChannel; const channels: ChannelsType = {
} { ChannelPeerTube: this.getTextChannel(
let resp: { client,
[key: string]: TextChannel | undefined; config.channelsId.idChannelPeerTube
ChannelYtb?: TextChannel; ),
ChannelPeerTube?: TextChannel; ChannelYtb: this.getTextChannel(
} = {}; client,
// construct and check the channels config.channelsId.idChannelYtb
),
};
for (const key in ids) { return {
if (ids[key]) { name: config.name,
// console.log(ids[key]); channels: channels,
const tmpChannel = this.client.channels.resolve(ids[key]); client: client,
if (tmpChannel) botPrefix: config.keyWord,
if (tmpChannel instanceof TextChannel) };
resp[key.slice(2)] = 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,
};
console.log(resp);
// console.log(resp.ChannelYtb);
throw new Error('Theres an issue concerned the channel check');
} }
private instantiateClient(): Client { private static getTextChannel(client: Client, id: string): TextChannel {
const newClient = new Client(); const channel = client.channels.resolve(id);
newClient.login(this.token); if (!channel || !(channel instanceof TextChannel))
return newClient; throw 'Bad token or bad channel id. Be careful, the channel must be a TextChannel';
return channel;
amaury.joly marked this conversation as resolved Outdated
if (!resp.ChannelPeerTube || !resp.ChannelYtb) {
  throw new Error('Theres an issue concerned the channel check');
}
return {...};
```ts if (!resp.ChannelPeerTube || !resp.ChannelYtb) { throw new Error('Theres an issue concerned the channel check'); } return {...}; ```
} }
public receivedMessage(message: ImplementableApi.Message) { public receivedMessage(message: ImplementableApi.Message) {
@ -113,8 +102,8 @@ class DiscordParser extends ImplementableApi {
} }
private settingEvents(): void { private settingEvents(): void {
this.on('message', async (message: Message) => { this.client.on('message', (message: Message) => {
const resolvedChannel = await this.channels; const resolvedChannel = this.channels;
let id_arr: string[] = []; let id_arr: string[] = [];
for (const key in this.channels) for (const key in this.channels)
id_arr.push(resolvedChannel[key].id); id_arr.push(resolvedChannel[key].id);
@ -122,7 +111,7 @@ class DiscordParser extends ImplementableApi {
if (this.channels) if (this.channels)
if (id_arr.includes(message.channel.id)) { if (id_arr.includes(message.channel.id)) {

Je comprends pas la partie de ce code, il répond à quel cas d'utilisation ?

Sinon :

if ( Object.values(this.channels).some(channel => channel.id === message.channel.id) ) {
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) ) { ```
const msg_splitted = message.content.split(' '); const msg_splitted = message.content.split(' ');
if (msg_splitted[0] === this.keyWord) { if (msg_splitted[0] === this.botPrefix) {
switch (msg_splitted[1]) { switch (msg_splitted[1]) {
case 'add': case 'add':
const send_message: ImplementableApi.Message = { const send_message: ImplementableApi.Message = {

View File

@ -1,20 +1,20 @@
import EventEmitter from "events"; import EventEmitter from 'events';
namespace ImplementableApi { namespace ImplementableApi {
export type Config = { export type Config = {
name: string; name: string;
} };
/** /**
* [optional] idListener is the way to identificate the listener who's the src of the newEntries * [optional] idListener is the way to identificate the listener who's the src of the newEntries
* the type field permit to know which type of message it is * the type field permit to know which type of message it is
* rawContent field is the string content of the message * rawContent field is the string content of the message
*/ */
export type Message = { export type Message = {
idListener?: number; idListener?: number;
type: "newEntriesNotify" | "newListener"; type: 'newEntriesNotify' | 'newListener';
rawContent: any; rawContent: any;
} };
} }
abstract class ImplementableApi extends EventEmitter { abstract class ImplementableApi extends EventEmitter {
@ -28,4 +28,4 @@ abstract class ImplementableApi extends EventEmitter {
public abstract receivedMessage(message: ImplementableApi.Message): void; public abstract receivedMessage(message: ImplementableApi.Message): void;
} }
export { ImplementableApi } export { ImplementableApi };

View File

@ -3,44 +3,53 @@ 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';
import EventEmitter from 'events';
namespace Router { namespace Router {
export type Config = { export type EventsType = {
events: { name: string;
name: string; type: 'emit' | 'received';
type: 'emit' | 'received'; };
}[]; export type InternalConfig = {
events: EventsType[];
apis: { apis: {
apiName: string; apiName: string;
}[]; }[];
}; };
export type GlobalConfig = { // export type GlobalConfig = {
router: Config; // router: InternalConfig;
discord: DiscordParser.Config; // };
peertubeRequester: PeerTubeRequester.Config;
logWriter: LogWriter.Config;
};
} }
class Router { class Router extends EventEmitter {
api_array: { [key: string]: ImplementableApi } = {}; api_array: { [key: string]: ImplementableApi } = {};
readonly routes: Router.Config; routes: Router.InternalConfig;
constructor(readonly config: Router.GlobalConfig) { constructor(readonly config: Router.EventsType[]) {
this.routes = config.router; super();
this.routes = { events: config, apis: [] };
}
this.api_array[config.discord.name] = new DiscordParser(config.discord); public injectDependency(api: ImplementableApi): void {
this.api_array[config.peertubeRequester.name] = new PeerTubeRequester( if (api.name in this.api_array)
config.peertubeRequester throw `The api name '${api.name}' is already take`;
); this.routes.apis.push({ apiName: api.name });
this.api_array[config.logWriter.name] = new LogWriter(config.logWriter); this.api_array[api.name] = api;
this.setEvents(api);
}
private setEvents(api: ImplementableApi) {
for (const eventName in this.routes.events.map(
(ev) => ev.type === 'received'
))
api.on(eventName, () => this.emit(eventName));
} }
public receivedMessage(message: ImplementableApi.Message) { public receivedMessage(message: ImplementableApi.Message) {
this.routes.apis.forEach((api) => for (const apiName in this.routes.apis)
this.api_array[api.apiName].receivedMessage(message) this.api_array[apiName].receivedMessage(message);
);
} }
} }