basic-implementation #1
|
@ -1,139 +1,135 @@
|
||||||
import { Client, Message, TextChannel } from 'discord.js';
|
import { Client, Message, TextChannel } from "discord.js";
|
||||||
import { ImplementableApi } from './implementableApi';
|
import { ImplementableApi } from "./implementableApi";
|
||||||
|
|
||||||
import { once as eventsOnce } from 'events';
|
import { once as eventsOnce } from "events";
|
||||||
|
|
||||||
namespace DiscordParser {
|
namespace DiscordParser {
|
||||||
export type Config = ImplementableApi.Config & {
|
export type Config = ImplementableApi.Config & {
|
||||||
token: string;
|
token: string;
|
||||||
channelsId: {
|
channelsId: {
|
||||||
idChannelYtb: string;
|
idChannelYtb: string;
|
||||||
idChannelPeerTube: string;
|
idChannelPeerTube: string;
|
||||||
};
|
|
||||||
keyWord: string;
|
|
||||||
};
|
};
|
||||||
|
keyWord: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type ConstructorPattern = ImplementableApi.Config & {
|
export type ConstructorPattern = ImplementableApi.Config & {
|
||||||
botPrefix: string;
|
botPrefix: string;
|
||||||
channels: {
|
channels: {
|
||||||
ChannelYtb: TextChannel;
|
ChannelYtb: TextChannel;
|
||||||
ChannelPeerTube: TextChannel;
|
ChannelPeerTube: TextChannel;
|
||||||
};
|
|
||||||
client: Client;
|
|
||||||
};
|
};
|
||||||
|
client: Client;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChannelsType = {
|
type ChannelsType = {
|
||||||
ChannelYtb: TextChannel;
|
ChannelYtb: TextChannel;
|
||||||
ChannelPeerTube: TextChannel;
|
ChannelPeerTube: TextChannel;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TBaseDiscordMessageType = ('addListener' | 'newEntriesNotify') &
|
type TBaseDiscordMessageType = ("addListener" | "newEntriesNotify") &
|
||||||
ImplementableApi.TBaseMessageType;
|
ImplementableApi.TBaseMessageType;
|
||||||
|
|
||||||
class DiscordParser<
|
class DiscordParser<
|
||||||
T extends TBaseDiscordMessageType
|
T extends TBaseDiscordMessageType
|
||||||
|
|||||||
> extends ImplementableApi<T> {
|
> extends ImplementableApi<T> {
|
||||||
readonly botPrefix: string;
|
readonly botPrefix: string;
|
||||||
florent
commented
`youtubeChannel` / `peertubeChannel` comme nom
|
|||||||
readonly channels: {
|
readonly channels: {
|
||||||
[key: string]: TextChannel;
|
[key: string]: TextChannel;
|
||||||
ChannelYtb: TextChannel;
|
ChannelYtb: TextChannel;
|
||||||
ChannelPeerTube: 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.
|
|||||||
|
};
|
||||||
|
readonly client: Client;
|
||||||
|
|
||||||
|
constructor(readonly config: DiscordParser.ConstructorPattern) {
|
||||||
|
super(config);
|
||||||
|
this.botPrefix = config.botPrefix;
|
||||||
|
this.channels = config.channels;
|
||||||
|
this.client = config.client;
|
||||||
|
this.settingEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async instanciate<T extends TBaseDiscordMessageType>(
|
||||||
|
config: DiscordParser.Config
|
||||||
|
): Promise<DiscordParser<T>> {
|
||||||
|
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),
|
||||||
};
|
};
|
||||||
readonly client: Client;
|
|
||||||
|
|
||||||
constructor(readonly config: DiscordParser.ConstructorPattern) {
|
return new DiscordParser<T>({
|
||||||
super(config);
|
name: config.name,
|
||||||
this.botPrefix = config.botPrefix;
|
channels: channels,
|
||||||
this.channels = config.channels;
|
client: client,
|
||||||
this.client = config.client;
|
botPrefix: config.keyWord,
|
||||||
this.settingEvents();
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
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 {...};
```
|
|||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public receivedMessage(message: ImplementableApi.Message<T>) {
|
||||||
|
switch (message.type) {
|
||||||
|
case "newEntriesNotify":
|
||||||
|
this.sendMsgYtb(
|
||||||
|
`New YouTubes entries received :\n${message.rawContent.items.map(
|
||||||
|
(item: any) =>
|
||||||
|
`Author : ${item.author}\nTitle: ${item.title}\nlink: ${item.link}`
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static async instanciate<T extends TBaseDiscordMessageType>(
|
private async sendMsgYtb(message: string) {
|
||||||
config: DiscordParser.Config
|
const resolvedChannel = await this.channels;
|
||||||
): Promise<DiscordParser<T>> {
|
resolvedChannel.ChannelYtb.send(message);
|
||||||
const client = new Client();
|
}
|
||||||
client.login(config.token);
|
private async sendMsgPeerTube(message: string) {
|
||||||
await eventsOnce(client, 'ready');
|
const resolvedChannel = await this.channels;
|
||||||
const channels: ChannelsType = {
|
resolvedChannel.ChannelPeerTube.send(message);
|
||||||
ChannelPeerTube: this.getTextChannel(
|
}
|
||||||
client,
|
|
||||||
config.channelsId.idChannelPeerTube
|
|
||||||
),
|
|
||||||
ChannelYtb: this.getTextChannel(
|
|
||||||
client,
|
|
||||||
config.channelsId.idChannelYtb
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
return new DiscordParser<T>({
|
private settingEvents(): void {
|
||||||
name: config.name,
|
this.client.on("message", (message: Message) => {
|
||||||
channels: channels,
|
const resolvedChannel = this.channels;
|
||||||
client: client,
|
let id_arr: string[] = [];
|
||||||
botPrefix: config.keyWord,
|
for (const key in this.channels) id_arr.push(resolvedChannel[key].id);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static getTextChannel(client: Client, id: string): TextChannel {
|
if (this.channels)
|
||||||
const channel = client.channels.resolve(id);
|
if (id_arr.includes(message.channel.id)) {
|
||||||
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) ) {
```
|
|||||||
if (!channel || !(channel instanceof TextChannel))
|
const msg_splitted = message.content.split(" ");
|
||||||
throw 'Bad token or bad channel id. Be careful, the channel must be a TextChannel';
|
if (msg_splitted[0] === this.botPrefix) {
|
||||||
return channel;
|
switch (msg_splitted[1]) {
|
||||||
}
|
case "add":
|
||||||
|
const send_message: ImplementableApi.Message<TBaseDiscordMessageType> =
|
||||||
public receivedMessage(message: ImplementableApi.Message<T>) {
|
{
|
||||||
switch (message.type) {
|
rawContent: {
|
||||||
case 'newEntriesNotify':
|
address: msg_splitted[2],
|
||||||
this.sendMsgYtb(
|
user: message.author.toString(),
|
||||||
`New YouTubes entries received :\n${message.rawContent.items.map(
|
date: message.createdAt.toUTCString(),
|
||||||
(item: any) =>
|
},
|
||||||
`Author : ${item.author}\nTitle: ${item.title}\nlink: ${item.link}`
|
type: "addListener",
|
||||||
)}`
|
};
|
||||||
);
|
message.channel.send("Ceci est un feedback");
|
||||||
default:
|
this.emit("addListener", send_message);
|
||||||
break;
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
private async sendMsgYtb(message: string) {
|
|
||||||
const resolvedChannel = await this.channels;
|
|
||||||
resolvedChannel.ChannelYtb.send(message);
|
|
||||||
}
|
|
||||||
private async sendMsgPeerTube(message: string) {
|
|
||||||
const resolvedChannel = await this.channels;
|
|
||||||
resolvedChannel.ChannelPeerTube.send(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private settingEvents(): void {
|
|
||||||
this.client.on('message', (message: Message) => {
|
|
||||||
const resolvedChannel = 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(' ');
|
|
||||||
if (msg_splitted[0] === this.botPrefix) {
|
|
||||||
switch (msg_splitted[1]) {
|
|
||||||
case 'add':
|
|
||||||
const send_message: ImplementableApi.Message<TBaseDiscordMessageType> =
|
|
||||||
{
|
|
||||||
rawContent: {
|
|
||||||
address: msg_splitted[2],
|
|
||||||
user: message.author.toString(),
|
|
||||||
date: message.createdAt.toUTCString(),
|
|
||||||
},
|
|
||||||
type: 'addListener',
|
|
||||||
};
|
|
||||||
message.channel.send('Ceci est un feedback');
|
|
||||||
this.emit('addListener', send_message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { DiscordParser };
|
export { DiscordParser };
|
||||||
|
|
|
@ -1,34 +1,34 @@
|
||||||
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<T extends TBaseMessageType> = {
|
export type Message<T extends TBaseMessageType> = {
|
||||||
idListener?: number;
|
idListener?: number;
|
||||||
type: T | 'logging';
|
type: T | "logging";
|
||||||
rawContent: any;
|
rawContent: any;
|
||||||
};
|
};
|
||||||
export type TBaseMessageType = string;
|
export type TBaseMessageType = string;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ImplementableApi<
|
abstract class ImplementableApi<
|
||||||
T extends ImplementableApi.TBaseMessageType
|
T extends ImplementableApi.TBaseMessageType
|
||||||
> extends EventEmitter {
|
> extends EventEmitter {
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
|
|
||||||
constructor(readonly config: ImplementableApi.Config) {
|
constructor(readonly config: ImplementableApi.Config) {
|
||||||
super();
|
super();
|
||||||
this.name = config.name;
|
this.name = config.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract receivedMessage(message: ImplementableApi.Message<T>): void;
|
public abstract receivedMessage(message: ImplementableApi.Message<T>): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { ImplementableApi };
|
export { ImplementableApi };
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// export * as Router from "./router"
|
// export * as Router from "./router"
|
||||||
export {Router} from "./router"
|
export { Router } from "./router";
|
||||||
|
|
||||||
export {ImplementableApi} from "./implementableApi"
|
export { ImplementableApi } from "./implementableApi";
|
||||||
|
|
|
@ -1,46 +1,43 @@
|
||||||
import { ImplementableApi } from './implementableApi';
|
import { ImplementableApi } from "./implementableApi";
|
||||||
|
|
||||||
import pino, { Logger } from 'pino';
|
import pino, { Logger } from "pino";
|
||||||
|
|
||||||
namespace LogWriter {
|
namespace LogWriter {
|
||||||
export type Config = ImplementableApi.Config & {
|
export type Config = ImplementableApi.Config & {
|
||||||
path: string;
|
path: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* check nodejs buffer et throttle
|
* check nodejs buffer et throttle
|
||||||
*/
|
*/
|
||||||
class LogWriter extends ImplementableApi {
|
class LogWriter extends ImplementableApi {
|
||||||
readonly path: string;
|
readonly path: string;
|
||||||
readonly logger: Logger;
|
readonly logger: Logger;
|
||||||
|
|
||||||
constructor(readonly config: LogWriter.Config) {
|
constructor(readonly config: LogWriter.Config) {
|
||||||
super(config);
|
super(config);
|
||||||
this.path = config.path;
|
this.path = config.path;
|
||||||
|
|
||||||
this.logger = pino(pino.destination({ dest: config.path }));
|
this.logger = pino(pino.destination({ dest: config.path }));
|
||||||
}
|
}
|
||||||
|
|
||||||
private writeMsg(message: string): void;
|
private writeMsg(message: string): void;
|
||||||
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}] ${
|
message = `[${message.type}] ${
|
||||||
typeof message.rawContent === 'string'
|
typeof message.rawContent === "string"
|
||||||
? message.rawContent
|
? message.rawContent
|
||||||
: JSON.stringify(message.rawContent)
|
: JSON.stringify(message.rawContent)
|
||||||
} ${
|
} ${message.idListener ?? `( listener_id : ${message.idListener} )\n`}`;
|
||||||
message.idListener ??
|
|
||||||
`( listener_id : ${message.idListener} )\n`
|
|
||||||
}`;
|
|
||||||
|
|
||||||
// const str = `[${new Date().toISOString()}] ${message}\n`;
|
// const str = `[${new Date().toISOString()}] ${message}\n`;
|
||||||
this.logger.info(message);
|
this.logger.info(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public receivedMessage(message: ImplementableApi.Message) {
|
public receivedMessage(message: ImplementableApi.Message) {
|
||||||
this.writeMsg(message);
|
this.writeMsg(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { LogWriter };
|
export { LogWriter };
|
||||||
|
|
|
@ -1,157 +1,154 @@
|
||||||
import { ImplementableApi } from './implementableApi';
|
import { ImplementableApi } from "./implementableApi";
|
||||||
// Api request lib
|
// Api request lib
|
||||||
import fetch, { FetchError, Headers } from 'node-fetch';
|
import fetch, { FetchError, Headers } from "node-fetch";
|
||||||
import { URLSearchParams } from 'url';
|
import { URLSearchParams } from "url";
|
||||||
import FormData from 'form-data';
|
import FormData from "form-data";
|
||||||
import dedent from 'ts-dedent';
|
import dedent from "ts-dedent";
|
||||||
|
|
||||||
namespace PeerTubeRequester {
|
namespace PeerTubeRequester {
|
||||||
export type Config = ImplementableApi.Config & {
|
export type Config = ImplementableApi.Config & {
|
||||||
domain_name: string;
|
domain_name: string;
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
type UploadInstruction = {
|
type UploadInstruction = {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
channelId: string;
|
channelId: string;
|
||||||
targetUrl: string;
|
targetUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ClientToken = {
|
type ClientToken = {
|
||||||
client_id: string;
|
client_id: string;
|
||||||
client_secret: string;
|
client_secret: string;
|
||||||
grant_type: 'password';
|
grant_type: "password";
|
||||||
response_type: 'code';
|
response_type: "code";
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type UserToken = {
|
type UserToken = {
|
||||||
access_token: string;
|
access_token: string;
|
||||||
token_type: string;
|
token_type: string;
|
||||||
expires_in: string;
|
expires_in: string;
|
||||||
refresh_token: string;
|
refresh_token: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PeerTubeRequester extends ImplementableApi {
|
class PeerTubeRequester extends ImplementableApi {
|
||||||
readonly domain_name: string;
|
readonly domain_name: string;
|
||||||
readonly username: string;
|
readonly username: string;
|
||||||
readonly password: string;
|
readonly password: string;
|
||||||
|
|
||||||
constructor(readonly config: PeerTubeRequester.Config) {
|
constructor(readonly config: PeerTubeRequester.Config) {
|
||||||
super(config);
|
super(config);
|
||||||
this.domain_name = config.domain_name;
|
this.domain_name = config.domain_name;
|
||||||
this.username = config.username;
|
this.username = config.username;
|
||||||
this.password = config.password;
|
this.password = config.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async receivedMessage(
|
||||||
|
message: ImplementableApi.Message
|
||||||
|
): Promise<void> {
|
||||||
|
switch (message.type) {
|
||||||
|
case "newEntriesNotify":
|
||||||
|
await this.newEntriesNotify(message);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async receivedMessage(
|
private async newEntriesNotify(
|
||||||
message: ImplementableApi.Message
|
message: ImplementableApi.Message
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
switch (message.type) {
|
// parse content
|
||||||
case 'newEntriesNotify':
|
const items = message.rawContent["items"];
|
||||||
await this.newEntriesNotify(message);
|
if (Array.isArray(items))
|
||||||
break;
|
for (const item of items) {
|
||||||
default:
|
const media_group = item["media:group"];
|
||||||
break;
|
const args: UploadInstruction = {
|
||||||
}
|
channelId: "848", // to do binding avec les idDeChaines Skeptikom
|
||||||
}
|
targetUrl: item.link,
|
||||||
|
|
||||||
private async newEntriesNotify(
|
|
||||||
message: ImplementableApi.Message
|
|
||||||
): Promise<void> {
|
|
||||||
// parse content
|
|
||||||
const items = message.rawContent['items'];
|
|
||||||
if (Array.isArray(items))
|
|
||||||
for (const item of items) {
|
|
||||||
const media_group = item['media:group'];
|
|
||||||
const args: UploadInstruction = {
|
|
||||||
channelId: '848', // to do binding avec les idDeChaines Skeptikom
|
|
||||||
targetUrl: item.link,
|
|
||||||
};
|
|
||||||
await this.apiRequest(args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
};
|
||||||
|
await this.apiRequest(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let myParams = new URLSearchParams();
|
private async apiRequest(message: UploadInstruction): Promise<void> {
|
||||||
for (const key in client_info) myParams.append(key, client_info[key]);
|
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();
|
||||||
|
|
||||||
response = await fetch(
|
const client_info: { [key: string]: string } = {
|
||||||
`https://${this.domain_name}/api/v1/users/token`,
|
client_id,
|
||||||
{
|
client_secret,
|
||||||
method: 'post',
|
grant_type: "password",
|
||||||
body: myParams,
|
response_type: "code",
|
||||||
}
|
username: this.username,
|
||||||
);
|
password: this.password,
|
||||||
if (!response.ok) {
|
};
|
||||||
throw new Error(response.statusText); // CRASH
|
|
||||||
}
|
|
||||||
const { access_token } = await response.json();
|
|
||||||
|
|
||||||
// Upload
|
let myParams = new URLSearchParams();
|
||||||
const myUploadForm = new URLSearchParams();
|
for (const key in client_info) myParams.append(key, client_info[key]);
|
||||||
const myHeader = new Headers();
|
|
||||||
myHeader.append('Authorization', `Bearer ${access_token}`);
|
|
||||||
for (const key in message) myUploadForm.append(key, message[key]);
|
|
||||||
|
|
||||||
response = await fetch(
|
response = await fetch(`https://${this.domain_name}/api/v1/users/token`, {
|
||||||
`https://${this.domain_name}/api/v1/videos/imports`,
|
method: "post",
|
||||||
{
|
body: myParams,
|
||||||
method: 'post',
|
});
|
||||||
headers: myHeader,
|
if (!response.ok) {
|
||||||
body: myUploadForm,
|
throw new Error(response.statusText); // CRASH
|
||||||
}
|
}
|
||||||
);
|
const { access_token } = await response.json();
|
||||||
|
|
||||||
if (!response.ok) {
|
// Upload
|
||||||
switch (response.status) {
|
const myUploadForm = new URLSearchParams();
|
||||||
case 400:
|
const myHeader = new Headers();
|
||||||
throw new Error(
|
myHeader.append("Authorization", `Bearer ${access_token}`);
|
||||||
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()`
|
|||||||
dedent`Your target URL was not accepted by the API.\
|
for (const key in message) myUploadForm.append(key, message[key]);
|
||||||
|
|
||||||
|
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}
|
Actualy it's : ${message.targetUrl}
|
||||||
${response.statusText}`
|
${response.statusText}`
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 403:
|
case 403:
|
||||||
throw new Error(response.statusText);
|
throw new Error(response.statusText);
|
||||||
break;
|
break;
|
||||||
case 409:
|
case 409:
|
||||||
throw new Error(
|
throw new Error(
|
||||||
dedent`Oops, your instance had not allowed the HTTPS import.\
|
dedent`Oops, your instance had not allowed the HTTPS import.\
|
||||||
Contact your administrator.
|
Contact your administrator.
|
||||||
${response.statusText}`
|
${response.statusText}`
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(
|
throw new Error(
|
||||||
dedent`Oh, you resolved an undocumented issues.\
|
dedent`Oh, you resolved an undocumented issues.\
|
||||||
Please report this on the git if you have the time.
|
Please report this on the git if you have the time.
|
||||||
ERROR: ${response.statusText}`
|
ERROR: ${response.statusText}`
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { PeerTubeRequester };
|
export { PeerTubeRequester };
|
||||||
|
|
|
@ -1,66 +1,64 @@
|
||||||
import { ImplementableApi } from './implementableApi';
|
import { ImplementableApi } from "./implementableApi";
|
||||||
|
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from "events";
|
||||||
|
|
||||||
namespace Router {
|
namespace Router {
|
||||||
export type EventsType = {
|
export type EventsType = {
|
||||||
name: string;
|
name: string;
|
||||||
type: 'emit' | 'received';
|
type: "emit" | "received";
|
||||||
};
|
};
|
||||||
export type InternalConfig = {
|
export type InternalConfig = {
|
||||||
events: EventsType[];
|
events: EventsType[];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class Router<
|
class Router<
|
||||||
MessageType extends ImplementableApi.TBaseMessageType
|
MessageType extends ImplementableApi.TBaseMessageType
|
||||||
> extends EventEmitter {
|
> extends EventEmitter {
|
||||||
api_array: { [key: string]: ImplementableApi<MessageType> } = {};
|
api_array: { [key: string]: ImplementableApi<MessageType> } = {};
|
||||||
events: Router.EventsType[];
|
events: Router.EventsType[];
|
||||||
|
|
||||||
constructor(readonly config: Router.EventsType[]) {
|
constructor(readonly config: Router.EventsType[]) {
|
||||||
super();
|
super();
|
||||||
this.events = config;
|
this.events = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public injectDependency(api: ImplementableApi<MessageType>): void {
|
public injectDependency(api: ImplementableApi<MessageType>): void {
|
||||||
if (api.name in this.api_array)
|
if (api.name in this.api_array)
|
||||||
throw `The api name '${api.name}' is already take`;
|
throw `The api name '${api.name}' is already take`;
|
||||||
this.api_array[api.name] = api;
|
this.api_array[api.name] = api;
|
||||||
|
|
||||||
this.setEvents(api);
|
this.setEvents(api);
|
||||||
|
|
||||||
|
this.receivedMessage({
|
||||||
|
rawContent: `The dependency \`${api.name}\` was well injected into the router`,
|
||||||
|
type: "logging",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private setEvents(api: ImplementableApi<MessageType>) {
|
||||||
|
for (const event of this.events.filter((ev) => ev.type === "received")) {
|
||||||
|
api.on(event.name, (obj: ImplementableApi.Message<MessageType>) => {
|
||||||
this.receivedMessage({
|
this.receivedMessage({
|
||||||
rawContent: `The dependency \`${api.name}\` was well injected into the router`,
|
type: "logging",
|
||||||
type: 'logging',
|
rawContent: `A message of type \`${obj.type}\` was emited by \`${api.name}\` with the event \`${event.name}\``,
|
||||||
});
|
});
|
||||||
|
this.emit(event.name, obj);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private setEvents(api: ImplementableApi<MessageType>) {
|
public receivedMessage(message: ImplementableApi.Message<MessageType>) {
|
||||||
for (const event of this.events.filter(
|
this.redirectMessage({
|
||||||
(ev) => ev.type === 'received'
|
rawContent: `A message of type \`${message.type}\` was received`,
|
||||||
)) {
|
type: "logging",
|
||||||
api.on(event.name, (obj: ImplementableApi.Message<MessageType>) => {
|
});
|
||||||
this.receivedMessage({
|
this.redirectMessage(message);
|
||||||
type: 'logging',
|
}
|
||||||
rawContent: `A message of type \`${obj.type}\` was emited by \`${api.name}\` with the event \`${event.name}\``,
|
private redirectMessage(message: ImplementableApi.Message<MessageType>) {
|
||||||
});
|
for (const api in this.api_array)
|
||||||
this.emit(event.name, obj);
|
this.api_array[api].receivedMessage(message);
|
||||||
});
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public receivedMessage(message: ImplementableApi.Message<MessageType>) {
|
|
||||||
this.redirectMessage({
|
|
||||||
rawContent: `A message of type \`${message.type}\` was received`,
|
|
||||||
type: 'logging',
|
|
||||||
});
|
|
||||||
this.redirectMessage(message);
|
|
||||||
}
|
|
||||||
private redirectMessage(message: ImplementableApi.Message<MessageType>) {
|
|
||||||
for (const api in this.api_array)
|
|
||||||
this.api_array[api].receivedMessage(message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Router };
|
export { Router };
|
||||||
|
|
Toujours utile ?