basic-implementation #1
|
@ -1,139 +1,135 @@
|
|||
import { Client, Message, TextChannel } from 'discord.js';
|
||||
import { ImplementableApi } from './implementableApi';
|
||||
import { Client, Message, TextChannel } from "discord.js";
|
||||
import { ImplementableApi } from "./implementableApi";
|
||||
|
||||
import { once as eventsOnce } from 'events';
|
||||
import { once as eventsOnce } from "events";
|
||||
|
||||
namespace DiscordParser {
|
||||
export type Config = ImplementableApi.Config & {
|
||||
token: string;
|
||||
channelsId: {
|
||||
idChannelYtb: string;
|
||||
idChannelPeerTube: string;
|
||||
};
|
||||
keyWord: string;
|
||||
export type Config = ImplementableApi.Config & {
|
||||
token: string;
|
||||
channelsId: {
|
||||
idChannelYtb: string;
|
||||
idChannelPeerTube: string;
|
||||
};
|
||||
keyWord: string;
|
||||
};
|
||||
|
||||
export type ConstructorPattern = ImplementableApi.Config & {
|
||||
botPrefix: string;
|
||||
channels: {
|
||||
ChannelYtb: TextChannel;
|
||||
ChannelPeerTube: TextChannel;
|
||||
};
|
||||
client: Client;
|
||||
export type ConstructorPattern = ImplementableApi.Config & {
|
||||
botPrefix: string;
|
||||
channels: {
|
||||
ChannelYtb: TextChannel;
|
||||
ChannelPeerTube: TextChannel;
|
||||
};
|
||||
client: Client;
|
||||
};
|
||||
}
|
||||
|
||||
type ChannelsType = {
|
||||
ChannelYtb: TextChannel;
|
||||
ChannelPeerTube: TextChannel;
|
||||
ChannelYtb: TextChannel;
|
||||
ChannelPeerTube: TextChannel;
|
||||
};
|
||||
|
||||
type TBaseDiscordMessageType = ('addListener' | 'newEntriesNotify') &
|
||||
ImplementableApi.TBaseMessageType;
|
||||
type TBaseDiscordMessageType = ("addListener" | "newEntriesNotify") &
|
||||
ImplementableApi.TBaseMessageType;
|
||||
|
||||
class DiscordParser<
|
||||
T extends TBaseDiscordMessageType
|
||||
T extends TBaseDiscordMessageType
|
||||
|
||||
> extends ImplementableApi<T> {
|
||||
readonly botPrefix: string;
|
||||
readonly channels: {
|
||||
[key: string]: TextChannel;
|
||||
ChannelYtb: TextChannel;
|
||||
ChannelPeerTube: TextChannel;
|
||||
readonly botPrefix: string;
|
||||
florent
commented
`youtubeChannel` / `peertubeChannel` comme nom
|
||||
readonly channels: {
|
||||
[key: string]: TextChannel;
|
||||
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.
|
||||
};
|
||||
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) {
|
||||
super(config);
|
||||
this.botPrefix = config.botPrefix;
|
||||
this.channels = config.channels;
|
||||
this.client = config.client;
|
||||
this.settingEvents();
|
||||
return new DiscordParser<T>({
|
||||
name: config.name,
|
||||
channels: channels,
|
||||
client: client,
|
||||
botPrefix: 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";
|
||||
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>(
|
||||
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
|
||||
),
|
||||
};
|
||||
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);
|
||||
}
|
||||
|
||||
return new DiscordParser<T>({
|
||||
name: config.name,
|
||||
channels: channels,
|
||||
client: client,
|
||||
botPrefix: config.keyWord,
|
||||
});
|
||||
}
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
if (this.channels)
|
||||
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) ) {
```
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 };
|
||||
|
|
|
@ -1,34 +1,34 @@
|
|||
import EventEmitter from 'events';
|
||||
import EventEmitter from "events";
|
||||
|
||||
namespace ImplementableApi {
|
||||
export type Config = {
|
||||
name: string;
|
||||
};
|
||||
export type Config = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* [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
|
||||
* rawContent field is the string content of the message
|
||||
*/
|
||||
export type Message<T extends TBaseMessageType> = {
|
||||
idListener?: number;
|
||||
type: T | 'logging';
|
||||
rawContent: any;
|
||||
};
|
||||
export type TBaseMessageType = string;
|
||||
/**
|
||||
* [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
|
||||
* rawContent field is the string content of the message
|
||||
*/
|
||||
export type Message<T extends TBaseMessageType> = {
|
||||
idListener?: number;
|
||||
type: T | "logging";
|
||||
rawContent: any;
|
||||
};
|
||||
export type TBaseMessageType = string;
|
||||
}
|
||||
|
||||
abstract class ImplementableApi<
|
||||
T extends ImplementableApi.TBaseMessageType
|
||||
T extends ImplementableApi.TBaseMessageType
|
||||
> extends EventEmitter {
|
||||
readonly name: string;
|
||||
readonly name: string;
|
||||
|
||||
constructor(readonly config: ImplementableApi.Config) {
|
||||
super();
|
||||
this.name = config.name;
|
||||
}
|
||||
constructor(readonly config: ImplementableApi.Config) {
|
||||
super();
|
||||
this.name = config.name;
|
||||
}
|
||||
|
||||
public abstract receivedMessage(message: ImplementableApi.Message<T>): void;
|
||||
public abstract receivedMessage(message: ImplementableApi.Message<T>): void;
|
||||
}
|
||||
|
||||
export { ImplementableApi };
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// 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 {
|
||||
export type Config = ImplementableApi.Config & {
|
||||
path: string;
|
||||
};
|
||||
export type Config = ImplementableApi.Config & {
|
||||
path: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* check nodejs buffer et throttle
|
||||
*/
|
||||
class LogWriter extends ImplementableApi {
|
||||
readonly path: string;
|
||||
readonly logger: Logger;
|
||||
readonly path: string;
|
||||
readonly logger: Logger;
|
||||
|
||||
constructor(readonly config: LogWriter.Config) {
|
||||
super(config);
|
||||
this.path = config.path;
|
||||
constructor(readonly config: LogWriter.Config) {
|
||||
super(config);
|
||||
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: ImplementableApi.Message): void;
|
||||
private writeMsg(message: ImplementableApi.Message | string) {
|
||||
if (typeof message !== 'string')
|
||||
message = `[${message.type}] ${
|
||||
typeof message.rawContent === 'string'
|
||||
? message.rawContent
|
||||
: JSON.stringify(message.rawContent)
|
||||
} ${
|
||||
message.idListener ??
|
||||
`( listener_id : ${message.idListener} )\n`
|
||||
}`;
|
||||
private writeMsg(message: string): void;
|
||||
private writeMsg(message: ImplementableApi.Message): void;
|
||||
private writeMsg(message: ImplementableApi.Message | string) {
|
||||
if (typeof message !== "string")
|
||||
message = `[${message.type}] ${
|
||||
typeof message.rawContent === "string"
|
||||
? message.rawContent
|
||||
: JSON.stringify(message.rawContent)
|
||||
} ${message.idListener ?? `( listener_id : ${message.idListener} )\n`}`;
|
||||
|
||||
// const str = `[${new Date().toISOString()}] ${message}\n`;
|
||||
this.logger.info(message);
|
||||
}
|
||||
// const str = `[${new Date().toISOString()}] ${message}\n`;
|
||||
this.logger.info(message);
|
||||
}
|
||||
|
||||
public receivedMessage(message: ImplementableApi.Message) {
|
||||
this.writeMsg(message);
|
||||
}
|
||||
public receivedMessage(message: ImplementableApi.Message) {
|
||||
this.writeMsg(message);
|
||||
}
|
||||
}
|
||||
|
||||
export { LogWriter };
|
||||
|
|
|
@ -1,157 +1,154 @@
|
|||
import { ImplementableApi } from './implementableApi';
|
||||
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';
|
||||
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 & {
|
||||
domain_name: string;
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
export type Config = ImplementableApi.Config & {
|
||||
domain_name: string;
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
}
|
||||
|
||||
type UploadInstruction = {
|
||||
[key: string]: string;
|
||||
channelId: string;
|
||||
targetUrl: string;
|
||||
[key: string]: string;
|
||||
channelId: string;
|
||||
targetUrl: string;
|
||||
};
|
||||
|
||||
type ClientToken = {
|
||||
client_id: string;
|
||||
client_secret: string;
|
||||
grant_type: 'password';
|
||||
response_type: 'code';
|
||||
username: string;
|
||||
password: string;
|
||||
client_id: string;
|
||||
client_secret: string;
|
||||
grant_type: "password";
|
||||
response_type: "code";
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
|
||||
type UserToken = {
|
||||
access_token: string;
|
||||
token_type: string;
|
||||
expires_in: string;
|
||||
refresh_token: string;
|
||||
access_token: string;
|
||||
token_type: string;
|
||||
expires_in: string;
|
||||
refresh_token: string;
|
||||
};
|
||||
|
||||
class PeerTubeRequester extends ImplementableApi {
|
||||
readonly domain_name: string;
|
||||
readonly username: string;
|
||||
readonly password: string;
|
||||
readonly domain_name: string;
|
||||
readonly username: string;
|
||||
readonly password: string;
|
||||
|
||||
constructor(readonly config: PeerTubeRequester.Config) {
|
||||
super(config);
|
||||
this.domain_name = config.domain_name;
|
||||
this.username = config.username;
|
||||
this.password = config.password;
|
||||
constructor(readonly config: PeerTubeRequester.Config) {
|
||||
super(config);
|
||||
this.domain_name = config.domain_name;
|
||||
this.username = config.username;
|
||||
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(
|
||||
message: ImplementableApi.Message
|
||||
): Promise<void> {
|
||||
switch (message.type) {
|
||||
case 'newEntriesNotify':
|
||||
await this.newEntriesNotify(message);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
let myParams = new URLSearchParams();
|
||||
for (const key in client_info) myParams.append(key, client_info[key]);
|
||||
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();
|
||||
|
||||
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();
|
||||
const client_info: { [key: string]: string } = {
|
||||
client_id,
|
||||
client_secret,
|
||||
grant_type: "password",
|
||||
response_type: "code",
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
};
|
||||
|
||||
// Upload
|
||||
const myUploadForm = new URLSearchParams();
|
||||
const myHeader = new Headers();
|
||||
myHeader.append('Authorization', `Bearer ${access_token}`);
|
||||
for (const key in message) myUploadForm.append(key, message[key]);
|
||||
let myParams = new URLSearchParams();
|
||||
for (const key in client_info) myParams.append(key, client_info[key]);
|
||||
|
||||
response = 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/users/token`, {
|
||||
method: "post",
|
||||
body: myParams,
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(response.statusText); // CRASH
|
||||
}
|
||||
const { access_token } = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
switch (response.status) {
|
||||
case 400:
|
||||
throw new Error(
|
||||
dedent`Your target URL was not accepted by the API.\
|
||||
// Upload
|
||||
const myUploadForm = new URLSearchParams();
|
||||
const myHeader = new Headers();
|
||||
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()`
|
||||
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}
|
||||
${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.\
|
||||
);
|
||||
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.\
|
||||
);
|
||||
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;
|
||||
}
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { PeerTubeRequester };
|
||||
|
|
|
@ -1,66 +1,64 @@
|
|||
import { ImplementableApi } from './implementableApi';
|
||||
import { ImplementableApi } from "./implementableApi";
|
||||
|
||||
import EventEmitter from 'events';
|
||||
import EventEmitter from "events";
|
||||
|
||||
namespace Router {
|
||||
export type EventsType = {
|
||||
name: string;
|
||||
type: 'emit' | 'received';
|
||||
};
|
||||
export type InternalConfig = {
|
||||
events: EventsType[];
|
||||
};
|
||||
export type EventsType = {
|
||||
name: string;
|
||||
type: "emit" | "received";
|
||||
};
|
||||
export type InternalConfig = {
|
||||
events: EventsType[];
|
||||
};
|
||||
}
|
||||
|
||||
class Router<
|
||||
MessageType extends ImplementableApi.TBaseMessageType
|
||||
MessageType extends ImplementableApi.TBaseMessageType
|
||||
> extends EventEmitter {
|
||||
api_array: { [key: string]: ImplementableApi<MessageType> } = {};
|
||||
events: Router.EventsType[];
|
||||
api_array: { [key: string]: ImplementableApi<MessageType> } = {};
|
||||
events: Router.EventsType[];
|
||||
|
||||
constructor(readonly config: Router.EventsType[]) {
|
||||
super();
|
||||
this.events = config;
|
||||
}
|
||||
constructor(readonly config: Router.EventsType[]) {
|
||||
super();
|
||||
this.events = config;
|
||||
}
|
||||
|
||||
public injectDependency(api: ImplementableApi<MessageType>): void {
|
||||
if (api.name in this.api_array)
|
||||
throw `The api name '${api.name}' is already take`;
|
||||
this.api_array[api.name] = api;
|
||||
public injectDependency(api: ImplementableApi<MessageType>): void {
|
||||
if (api.name in this.api_array)
|
||||
throw `The api name '${api.name}' is already take`;
|
||||
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({
|
||||
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>) {
|
||||
for (const event of this.events.filter(
|
||||
(ev) => ev.type === 'received'
|
||||
)) {
|
||||
api.on(event.name, (obj: ImplementableApi.Message<MessageType>) => {
|
||||
this.receivedMessage({
|
||||
type: 'logging',
|
||||
rawContent: `A message of type \`${obj.type}\` was emited by \`${api.name}\` with the event \`${event.name}\``,
|
||||
});
|
||||
this.emit(event.name, obj);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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 };
|
||||
|
|
Toujours utile ?