mirror of
https://github.com/turt2live/matrix-dimension.git
synced 2024-10-01 05:05:53 +00:00
parent
1233be85e9
commit
18597db540
@ -1,4 +1,4 @@
|
|||||||
import { DELETE, GET, Path, PathParam, QueryParam } from "typescript-rest";
|
import { DELETE, GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||||
import { ScalarService } from "../scalar/ScalarService";
|
import { ScalarService } from "../scalar/ScalarService";
|
||||||
import { Widget } from "../../integrations/Widget";
|
import { Widget } from "../../integrations/Widget";
|
||||||
import { Cache, CACHE_INTEGRATIONS } from "../../MemoryCache";
|
import { Cache, CACHE_INTEGRATIONS } from "../../MemoryCache";
|
||||||
@ -67,6 +67,18 @@ export class DimensionIntegrationsService {
|
|||||||
else throw new ApiError(400, "Unrecognized category");
|
else throw new ApiError(400, "Unrecognized category");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("room/:roomId/integrations/:category/:type/config")
|
||||||
|
public async setIntegrationConfigurationInRoom(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string, @PathParam("category") category: string, @PathParam("type") integrationType: string, newConfig: any): Promise<any> {
|
||||||
|
const userId = await ScalarService.getTokenOwner(scalarToken);
|
||||||
|
|
||||||
|
if (category === "complex-bot") await NebStore.setComplexBotConfig(userId, integrationType, roomId, newConfig);
|
||||||
|
else throw new ApiError(400, "Unrecognized category");
|
||||||
|
|
||||||
|
Cache.for(CACHE_INTEGRATIONS).clear(); // TODO: Improve which cache we invalidate
|
||||||
|
return {}; // 200 OK
|
||||||
|
}
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
@Path("room/:roomId/integrations/:category/:type")
|
@Path("room/:roomId/integrations/:category/:type")
|
||||||
public async removeIntegrationInRoom(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string, @PathParam("category") category: string, @PathParam("type") integrationType: string): Promise<any> {
|
public async removeIntegrationInRoom(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string, @PathParam("category") category: string, @PathParam("type") integrationType: string): Promise<any> {
|
||||||
@ -74,8 +86,10 @@ export class DimensionIntegrationsService {
|
|||||||
|
|
||||||
if (category === "widget") throw new ApiError(400, "Widgets should be removed client-side");
|
if (category === "widget") throw new ApiError(400, "Widgets should be removed client-side");
|
||||||
else if (category === "bot") await NebStore.removeSimpleBot(integrationType, roomId, userId);
|
else if (category === "bot") await NebStore.removeSimpleBot(integrationType, roomId, userId);
|
||||||
|
else if (category === "complex-bot") throw new ApiError(400, "Complex bots should be removed automatically");
|
||||||
else throw new ApiError(400, "Unrecognized category");
|
else throw new ApiError(400, "Unrecognized category");
|
||||||
|
|
||||||
|
Cache.for(CACHE_INTEGRATIONS).clear(); // TODO: Improve which cache we invalidate
|
||||||
return {}; // 200 OK
|
return {}; // 200 OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import NebConfiguration from "./models/NebConfiguration";
|
|||||||
import NebIntegration from "./models/NebIntegration";
|
import NebIntegration from "./models/NebIntegration";
|
||||||
import NebBotUser from "./models/NebBotUser";
|
import NebBotUser from "./models/NebBotUser";
|
||||||
import NebNotificationUser from "./models/NebNotificationUser";
|
import NebNotificationUser from "./models/NebNotificationUser";
|
||||||
|
import NebIntegrationConfig from "./models/NebIntegrationConfig";
|
||||||
|
|
||||||
class _DimensionStore {
|
class _DimensionStore {
|
||||||
private sequelize: Sequelize;
|
private sequelize: Sequelize;
|
||||||
@ -37,6 +38,7 @@ class _DimensionStore {
|
|||||||
NebIntegration,
|
NebIntegration,
|
||||||
NebBotUser,
|
NebBotUser,
|
||||||
NebNotificationUser,
|
NebNotificationUser,
|
||||||
|
NebIntegrationConfig,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,13 +137,22 @@ export class NebStore {
|
|||||||
const rawIntegrations = await NebStore.listEnabledNebComplexBots();
|
const rawIntegrations = await NebStore.listEnabledNebComplexBots();
|
||||||
return Promise.all(rawIntegrations.map(async i => {
|
return Promise.all(rawIntegrations.map(async i => {
|
||||||
const proxy = new NebProxy(i.neb, requestingUserId);
|
const proxy = new NebProxy(i.neb, requestingUserId);
|
||||||
const notifUserId = await proxy.getNotificationUserId(i.integration, roomId, requestingUserId);
|
const notifUserId = await proxy.getNotificationUserId(i.integration, roomId);
|
||||||
const botUserId = null; // TODO: For github
|
const botUserId = null; // TODO: For github
|
||||||
// TODO: Get configuration
|
const botConfig = await proxy.getServiceConfiguration(i.integration, roomId);
|
||||||
return new ComplexBot(i.integration, notifUserId, botUserId);
|
return new ComplexBot(i.integration, notifUserId, botUserId, botConfig);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async setComplexBotConfig(requestingUserId: string, type: string, roomId: string, newConfig: any): Promise<any> {
|
||||||
|
const rawIntegrations = await NebStore.listEnabledNebComplexBots();
|
||||||
|
const integration = rawIntegrations.find(i => i.integration.type === type);
|
||||||
|
if (!integration) throw new Error("Integration not found");
|
||||||
|
|
||||||
|
const proxy = new NebProxy(integration.neb, requestingUserId);
|
||||||
|
return proxy.setServiceConfiguration(integration.integration, roomId, newConfig);
|
||||||
|
}
|
||||||
|
|
||||||
public static async removeSimpleBot(type: string, roomId: string, requestingUserId: string): Promise<any> {
|
public static async removeSimpleBot(type: string, roomId: string, requestingUserId: string): Promise<any> {
|
||||||
const rawIntegrations = await NebStore.listEnabledNebSimpleBots();
|
const rawIntegrations = await NebStore.listEnabledNebSimpleBots();
|
||||||
const integration = rawIntegrations.find(i => i.integration.type === type);
|
const integration = rawIntegrations.find(i => i.integration.type === type);
|
||||||
|
22
src/db/migrations/20180326185545-AddNebIntegrationConfig.ts
Normal file
22
src/db/migrations/20180326185545-AddNebIntegrationConfig.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { QueryInterface } from "sequelize";
|
||||||
|
import { DataType } from "sequelize-typescript";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
up: (queryInterface: QueryInterface) => {
|
||||||
|
return Promise.resolve()
|
||||||
|
.then(() => queryInterface.createTable("dimension_neb_integration_config", {
|
||||||
|
"id": {type: DataType.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false},
|
||||||
|
"integrationId": {
|
||||||
|
type: DataType.INTEGER, allowNull: false,
|
||||||
|
references: {model: "dimension_neb_integrations", key: "id"},
|
||||||
|
onUpdate: "cascade", onDelete: "cascade",
|
||||||
|
},
|
||||||
|
"roomId": {type: DataType.STRING, allowNull: false},
|
||||||
|
"jsonContent": {type: DataType.STRING, allowNull: false},
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return Promise.resolve()
|
||||||
|
.then(() => queryInterface.dropTable("dimension_neb_integration_config"));
|
||||||
|
}
|
||||||
|
}
|
24
src/db/models/NebIntegrationConfig.ts
Normal file
24
src/db/models/NebIntegrationConfig.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { AutoIncrement, Column, ForeignKey, Model, PrimaryKey, Table } from "sequelize-typescript";
|
||||||
|
import NebIntegration from "./NebIntegration";
|
||||||
|
|
||||||
|
@Table({
|
||||||
|
tableName: "dimension_neb_integration_config",
|
||||||
|
underscoredAll: false,
|
||||||
|
timestamps: false,
|
||||||
|
})
|
||||||
|
export default class NebIntegrationConfig extends Model<NebIntegrationConfig> {
|
||||||
|
@PrimaryKey
|
||||||
|
@AutoIncrement
|
||||||
|
@Column
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@ForeignKey(() => NebIntegration)
|
||||||
|
integrationId: string;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
roomId: string;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
jsonContent: string;
|
||||||
|
}
|
@ -2,7 +2,7 @@ import { Integration } from "./Integration";
|
|||||||
import NebIntegration from "../db/models/NebIntegration";
|
import NebIntegration from "../db/models/NebIntegration";
|
||||||
|
|
||||||
export class ComplexBot extends Integration {
|
export class ComplexBot extends Integration {
|
||||||
constructor(bot: NebIntegration, public notificationUserId: string, public botUserId?: string) {
|
constructor(bot: NebIntegration, public notificationUserId: string, public botUserId: string, public config: any) {
|
||||||
super(bot);
|
super(bot);
|
||||||
this.category = "complex-bot";
|
this.category = "complex-bot";
|
||||||
this.requirements = [];
|
this.requirements = [];
|
||||||
@ -11,3 +11,11 @@ export class ComplexBot extends Integration {
|
|||||||
this.isEncryptionSupported = true;
|
this.isEncryptionSupported = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RssBotConfiguration {
|
||||||
|
feeds: {
|
||||||
|
[url: string]: {
|
||||||
|
addedByUserId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
export interface ModularIntegrationInfoResponse {
|
export interface ModularIntegrationInfoResponse {
|
||||||
bot_user_id: string;
|
bot_user_id: string;
|
||||||
|
integrations?: any[];
|
||||||
}
|
}
|
@ -73,6 +73,7 @@ export class NebClient {
|
|||||||
reject(err);
|
reject(err);
|
||||||
} else if (res.statusCode !== 200) {
|
} else if (res.statusCode !== 200) {
|
||||||
LogService.error("NebClient", "Got status code " + res.statusCode + " while performing request");
|
LogService.error("NebClient", "Got status code " + res.statusCode + " while performing request");
|
||||||
|
LogService.error("NebClient", res.body);
|
||||||
reject(new Error("Request error"));
|
reject(new Error("Request error"));
|
||||||
} else {
|
} else {
|
||||||
resolve(res.body);
|
resolve(res.body);
|
||||||
|
@ -9,6 +9,8 @@ import { NebClient } from "./NebClient";
|
|||||||
import { ModularIntegrationInfoResponse } from "../models/ModularResponses";
|
import { ModularIntegrationInfoResponse } from "../models/ModularResponses";
|
||||||
import { AppserviceStore } from "../db/AppserviceStore";
|
import { AppserviceStore } from "../db/AppserviceStore";
|
||||||
import { MatrixAppserviceClient } from "../matrix/MatrixAppserviceClient";
|
import { MatrixAppserviceClient } from "../matrix/MatrixAppserviceClient";
|
||||||
|
import NebIntegrationConfig from "../db/models/NebIntegrationConfig";
|
||||||
|
import { RssBotConfiguration } from "../integrations/ComplexBot";
|
||||||
|
|
||||||
export class NebProxy {
|
export class NebProxy {
|
||||||
constructor(private neb: NebConfig, private requestingUserId: string) {
|
constructor(private neb: NebConfig, private requestingUserId: string) {
|
||||||
@ -31,7 +33,7 @@ export class NebProxy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getNotificationUserId(integration: NebIntegration, inRoomId: string, forUserId: string): Promise<string> {
|
public async getNotificationUserId(integration: NebIntegration, inRoomId: string): Promise<string> {
|
||||||
if (integration.nebId !== this.neb.id) throw new Error("Integration is not for this NEB proxy");
|
if (integration.nebId !== this.neb.id) throw new Error("Integration is not for this NEB proxy");
|
||||||
|
|
||||||
if (this.neb.upstreamId) {
|
if (this.neb.upstreamId) {
|
||||||
@ -45,13 +47,133 @@ export class NebProxy {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return (await NebStore.getOrCreateNotificationUser(this.neb.id, integration.type, forUserId)).appserviceUserId;
|
return (await NebStore.getOrCreateNotificationUser(this.neb.id, integration.type, this.requestingUserId)).appserviceUserId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// public async getComplexBotConfiguration(integration: NebIntegration, roomId: string): Promise<any> {
|
public async getServiceConfiguration(integration: NebIntegration, inRoomId: string): Promise<any> {
|
||||||
//
|
if (integration.nebId !== this.neb.id) throw new Error("Integration is not for this NEB proxy");
|
||||||
// }
|
|
||||||
|
if (this.neb.upstreamId) {
|
||||||
|
// TODO: Verify
|
||||||
|
try {
|
||||||
|
const response = await this.doUpstreamRequest<ModularIntegrationInfoResponse>("/integrations/" + NebClient.getNebType(integration.type), {
|
||||||
|
room_id: inRoomId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (integration.type === "rss") return this.parseUpstreamRssConfiguration(response.integrations);
|
||||||
|
else return {};
|
||||||
|
} catch (err) {
|
||||||
|
LogService.error("NebProxy", err);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const serviceConfig = await NebIntegrationConfig.findOne({
|
||||||
|
where: {
|
||||||
|
integrationId: integration.id,
|
||||||
|
roomId: inRoomId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return serviceConfig ? JSON.parse(serviceConfig.jsonContent) : {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async setServiceConfiguration(integration: NebIntegration, inRoomId: string, newConfig: any): Promise<any> {
|
||||||
|
if (integration.nebId !== this.neb.id) throw new Error("Integration is not for this NEB proxy");
|
||||||
|
|
||||||
|
if (!this.neb.upstreamId) {
|
||||||
|
const serviceConfig = await NebIntegrationConfig.findOne({
|
||||||
|
where: {
|
||||||
|
integrationId: integration.id,
|
||||||
|
roomId: inRoomId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (serviceConfig) {
|
||||||
|
serviceConfig.jsonContent = JSON.stringify(newConfig);
|
||||||
|
await serviceConfig.save();
|
||||||
|
} else {
|
||||||
|
await NebIntegrationConfig.create({
|
||||||
|
integrationId: integration.id,
|
||||||
|
roomId: inRoomId,
|
||||||
|
jsonContent: JSON.stringify(newConfig),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (integration.type === "rss") await this.updateRssConfiguration(inRoomId, newConfig);
|
||||||
|
else throw new Error("Cannot update go-neb: unrecognized type");
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseUpstreamRssConfiguration(integrations: any[]): any {
|
||||||
|
if (!integrations) return {};
|
||||||
|
|
||||||
|
const result: RssBotConfiguration = {feeds: {}};
|
||||||
|
for (const integration of integrations) {
|
||||||
|
const userId = integration.user_id;
|
||||||
|
const feeds = integration.config ? integration.config.feeds : {};
|
||||||
|
if (!userId || !feeds) continue;
|
||||||
|
|
||||||
|
const urls = Object.keys(feeds);
|
||||||
|
urls.forEach(u => result.feeds[u] = {addedByUserId: userId});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async updateRssConfiguration(roomId: string, newOpts: RssBotConfiguration): Promise<any> {
|
||||||
|
const feedUrls = Object.keys(newOpts.feeds).filter(f => newOpts.feeds[f].addedByUserId === this.requestingUserId);
|
||||||
|
const newConfig = {feeds: {}};
|
||||||
|
let currentConfig = {feeds: {}};
|
||||||
|
|
||||||
|
if (this.neb.upstreamId) {
|
||||||
|
const response = await this.doUpstreamRequest<ModularIntegrationInfoResponse>("/integrations/rssbot", {room_id: roomId});
|
||||||
|
currentConfig = await this.parseUpstreamRssConfiguration(response.integrations);
|
||||||
|
} else {
|
||||||
|
const client = new NebClient(this.neb);
|
||||||
|
const notifUser = await NebStore.getOrCreateNotificationUser(this.neb.id, "rss", this.requestingUserId);
|
||||||
|
currentConfig = await client.getServiceConfig(notifUser.serviceId);
|
||||||
|
|
||||||
|
if (feedUrls.length === 0) {
|
||||||
|
const client = new MatrixAppserviceClient(await AppserviceStore.getAppservice(this.neb.appserviceId));
|
||||||
|
await client.leaveRoom(notifUser.appserviceUserId, roomId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentConfig || !currentConfig.feeds) currentConfig = {feeds: {}};
|
||||||
|
|
||||||
|
const allUrls = feedUrls.concat(Object.keys(currentConfig.feeds));
|
||||||
|
for (const feedUrl of allUrls) {
|
||||||
|
let feed = currentConfig.feeds[feedUrl];
|
||||||
|
if (!feed) feed = {poll_interval_mins: 60, rooms: []};
|
||||||
|
|
||||||
|
const hasRoom = feed.rooms.indexOf(roomId) !== -1;
|
||||||
|
const isEnabled = feedUrls.indexOf(feedUrl) !== -1;
|
||||||
|
|
||||||
|
if (hasRoom && !isEnabled) {
|
||||||
|
feed.rooms.splice(feed.rooms.indexOf(roomId), 1);
|
||||||
|
} else if (!hasRoom && isEnabled) {
|
||||||
|
feed.rooms.push(roomId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (feed.rooms.length > 0) {
|
||||||
|
newConfig.feeds[feedUrl] = {
|
||||||
|
poll_interval_mins: feed.poll_interval_mins,
|
||||||
|
rooms: feed.rooms,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.neb.upstreamId) {
|
||||||
|
await this.doUpstreamRequest<ModularIntegrationInfoResponse>("/integrations/rssbot/configureService", {
|
||||||
|
room_id: roomId,
|
||||||
|
feeds: newConfig.feeds,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const client = new NebClient(this.neb);
|
||||||
|
const notifUser = await NebStore.getOrCreateNotificationUser(this.neb.id, "rss", this.requestingUserId);
|
||||||
|
await client.setServiceConfig(notifUser.serviceId, notifUser.appserviceUserId, "rssbot", newConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async removeBotFromRoom(integration: NebIntegration, roomId: string): Promise<any> {
|
public async removeBotFromRoom(integration: NebIntegration, roomId: string): Promise<any> {
|
||||||
if (integration.nebId !== this.neb.id) throw new Error("Integration is not for this NEB proxy");
|
if (integration.nebId !== this.neb.id) throw new Error("Integration is not for this NEB proxy");
|
||||||
|
@ -5,6 +5,7 @@ import { Subscription } from "rxjs/Subscription";
|
|||||||
import { IntegrationsApiService } from "../../shared/services/integrations/integrations-api.service";
|
import { IntegrationsApiService } from "../../shared/services/integrations/integrations-api.service";
|
||||||
import { ToasterService } from "angular2-toaster";
|
import { ToasterService } from "angular2-toaster";
|
||||||
import { ServiceLocator } from "../../shared/registry/locator.service";
|
import { ServiceLocator } from "../../shared/registry/locator.service";
|
||||||
|
import { ScalarClientApiService } from "../../shared/services/scalar/scalar-client-api.service";
|
||||||
|
|
||||||
export class ComplexBotComponent<T> implements OnInit, OnDestroy {
|
export class ComplexBotComponent<T> implements OnInit, OnDestroy {
|
||||||
|
|
||||||
@ -12,14 +13,14 @@ export class ComplexBotComponent<T> implements OnInit, OnDestroy {
|
|||||||
public isUpdating = false;
|
public isUpdating = false;
|
||||||
public bot: FE_ComplexBot<T>;
|
public bot: FE_ComplexBot<T>;
|
||||||
public newConfig: T;
|
public newConfig: T;
|
||||||
|
public roomId: string;
|
||||||
private roomId: string;
|
|
||||||
|
|
||||||
private routeQuerySubscription: Subscription;
|
private routeQuerySubscription: Subscription;
|
||||||
|
|
||||||
protected toaster = ServiceLocator.injector.get(ToasterService);
|
protected toaster = ServiceLocator.injector.get(ToasterService);
|
||||||
protected integrationsApi = ServiceLocator.injector.get(IntegrationsApiService);
|
protected integrationsApi = ServiceLocator.injector.get(IntegrationsApiService);
|
||||||
protected route = ServiceLocator.injector.get(ActivatedRoute);
|
protected route = ServiceLocator.injector.get(ActivatedRoute);
|
||||||
|
protected scalarClientApi = ServiceLocator.injector.get(ScalarClientApiService);
|
||||||
|
|
||||||
constructor(private integrationType: string) {
|
constructor(private integrationType: string) {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
@ -45,10 +46,25 @@ export class ComplexBotComponent<T> implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.integrationsApi.getIntegrationInRoom("complex-bot", this.integrationType, this.roomId).then(i => {
|
this.integrationsApi.getIntegrationInRoom("complex-bot", this.integrationType, this.roomId).then(i => {
|
||||||
this.bot = <FE_ComplexBot<T>>i;
|
this.bot = <FE_ComplexBot<T>>i;
|
||||||
|
this.newConfig = JSON.parse(JSON.stringify(this.bot.config));
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
this.toaster.pop("error", "Failed to load configuration");
|
this.toaster.pop("error", "Failed to load configuration");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public save(): void {
|
||||||
|
this.isUpdating = true;
|
||||||
|
this.integrationsApi.setIntegrationConfiguration("complex-bot", this.integrationType, this.roomId, this.newConfig).then(() => {
|
||||||
|
this.toaster.pop("success", "Configuration updated");
|
||||||
|
this.bot.config = this.newConfig;
|
||||||
|
this.newConfig = JSON.parse(JSON.stringify(this.bot.config));
|
||||||
|
this.isUpdating = false;
|
||||||
|
}).catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
this.toaster.pop("error", "Error updating configuration");
|
||||||
|
this.isUpdating = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
@ -2,20 +2,5 @@
|
|||||||
<my-spinner></my-spinner>
|
<my-spinner></my-spinner>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!botComponent.isLoading">
|
<div *ngIf="!botComponent.isLoading">
|
||||||
<my-ibox>
|
|
||||||
<h5 class="my-ibox-title">
|
|
||||||
{{ botComponent.bot.displayName }} configuration
|
|
||||||
</h5>
|
|
||||||
<div class="my-ibox-content">
|
|
||||||
<form (submit)="botComponent.save()" novalidate name="saveForm">
|
|
||||||
<ng-container *ngTemplateOutlet="botParamsTemplate"></ng-container>
|
<ng-container *ngTemplateOutlet="botParamsTemplate"></ng-container>
|
||||||
|
|
||||||
<div style="margin-top: 25px">
|
|
||||||
<button type="submit" class="btn btn-sm btn-success" [disabled]="botComponent.isUpdating">
|
|
||||||
<i class="far fa-save"></i> Save
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</my-ibox>
|
|
||||||
</div>
|
</div>
|
@ -1,5 +1,58 @@
|
|||||||
<my-complex-bot-config [botComponent]="this">
|
<my-complex-bot-config [botComponent]="this">
|
||||||
<ng-template #botParamsTemplate>
|
<ng-template #botParamsTemplate>
|
||||||
<p>{{ bot | json }}</p>
|
<my-ibox>
|
||||||
|
<h5 class="my-ibox-title">
|
||||||
|
Feeds
|
||||||
|
</h5>
|
||||||
|
<div class="my-ibox-content">
|
||||||
|
<form (submit)="interceptSave()" novalidate name="saveForm">
|
||||||
|
<table class="table table-striped table-condensed table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>URL</th>
|
||||||
|
<th>Added by</th>
|
||||||
|
<th class="actions-col">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let feed of getFeeds()">
|
||||||
|
<td>{{ feed.url }}</td>
|
||||||
|
<td>{{ feed.addedByUserId }}</td>
|
||||||
|
<td class="actions-col">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-danger"
|
||||||
|
[disabled]="isUpdating || !feed.isSelf"
|
||||||
|
(click)="removeFeed(feed)">
|
||||||
|
<i class="far fa-trash-alt"></i> Remove
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
<input type="text" class="form-control"
|
||||||
|
[(ngModel)]="newFeedUrl"
|
||||||
|
placeholder="https://example.org/feed.atom"
|
||||||
|
name="newFeedUrl"
|
||||||
|
title="New feed URL" />
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button type="button" class="btn btn-outline-success"
|
||||||
|
[disabled]="isUpdating"
|
||||||
|
(click)="addFeed()">
|
||||||
|
<i class="fa fa-plus"></i> Add
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div style="margin-top: 25px">
|
||||||
|
<button type="submit" class="btn btn-sm btn-primary" [disabled]="isUpdating">
|
||||||
|
<i class="far fa-save"></i> Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</my-ibox>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</my-complex-bot-config>
|
</my-complex-bot-config>
|
@ -0,0 +1,4 @@
|
|||||||
|
.actions-col {
|
||||||
|
width: 120px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
@ -1,10 +1,19 @@
|
|||||||
import { ComplexBotComponent } from "../complex-bot.component";
|
import { ComplexBotComponent } from "../complex-bot.component";
|
||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
import { SessionStorage } from "../../../shared/SessionStorage";
|
||||||
|
|
||||||
interface RssConfig {
|
interface RssConfig {
|
||||||
feeds: {
|
feeds: {
|
||||||
[feedUrl: string]: {}; // No options currently
|
[feedUrl: string]: {
|
||||||
|
addedByUserId: string;
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LocalFeed {
|
||||||
|
url: string;
|
||||||
|
addedByUserId: string;
|
||||||
|
isSelf: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -12,7 +21,46 @@ interface RssConfig {
|
|||||||
styleUrls: ["rss.complex-bot.component.scss"],
|
styleUrls: ["rss.complex-bot.component.scss"],
|
||||||
})
|
})
|
||||||
export class RssComplexBotConfigComponent extends ComplexBotComponent<RssConfig> {
|
export class RssComplexBotConfigComponent extends ComplexBotComponent<RssConfig> {
|
||||||
|
|
||||||
|
public newFeedUrl = "";
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("rss");
|
super("rss");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addFeed(): void {
|
||||||
|
if (!this.newFeedUrl.trim()) {
|
||||||
|
this.toaster.pop('warning', 'Please enter a feed URL');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.newConfig.feeds[this.newFeedUrl] = {addedByUserId: SessionStorage.userId};
|
||||||
|
this.newFeedUrl = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFeeds(): LocalFeed[] {
|
||||||
|
if (!this.newConfig.feeds) this.newConfig.feeds = {};
|
||||||
|
return Object.keys(this.newConfig.feeds).map(url => {
|
||||||
|
return {
|
||||||
|
url: url,
|
||||||
|
addedByUserId: this.newConfig.feeds[url].addedByUserId,
|
||||||
|
isSelf: SessionStorage.userId === this.newConfig.feeds[url].addedByUserId,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeFeed(feed: LocalFeed): void {
|
||||||
|
delete this.newConfig.feeds[feed.url];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async interceptSave(): Promise<any> {
|
||||||
|
const memberEvent = await this.scalarClientApi.getMembershipState(this.roomId, this.bot.notificationUserId);
|
||||||
|
const isJoined = memberEvent && memberEvent.response && ["join", "invite"].indexOf(memberEvent.response.membership) !== -1;
|
||||||
|
|
||||||
|
if (!isJoined) {
|
||||||
|
await this.scalarClientApi.inviteUser(this.roomId, this.bot.notificationUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.save();
|
||||||
|
}
|
||||||
}
|
}
|
@ -22,6 +22,10 @@ export class IntegrationsApiService extends AuthedApi {
|
|||||||
return this.authedGet("/api/v1/dimension/integrations/room/" + roomId + "/integrations/" + category + "/" + type).map(r => r.json()).toPromise();
|
return this.authedGet("/api/v1/dimension/integrations/room/" + roomId + "/integrations/" + category + "/" + type).map(r => r.json()).toPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setIntegrationConfiguration(category: string, type: string, roomId: string, newConfig: any): Promise<any> {
|
||||||
|
return this.authedPost("/api/v1/dimension/integrations/room/" + roomId + "/integrations/" + category + "/" + type + "/config", newConfig).map(r => r.json()).toPromise();
|
||||||
|
}
|
||||||
|
|
||||||
public getWidget(type: string): Promise<FE_Widget> {
|
public getWidget(type: string): Promise<FE_Widget> {
|
||||||
return this.getIntegration("widget", type).then(i => <FE_Widget>i);
|
return this.getIntegration("widget", type).then(i => <FE_Widget>i);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user