Early support for managing bots in the room

Known drawbacks:
* Cannot remove bots
* Cannot use upstream neb instances
This commit is contained in:
Travis Ralston 2018-03-25 00:45:57 -06:00
parent dcda2fa46e
commit 947ecd43cd
8 changed files with 76 additions and 14 deletions

View File

@ -5,6 +5,7 @@ import { Cache, CACHE_INTEGRATIONS } from "../../MemoryCache";
import { Integration } from "../../integrations/Integration";
import { ApiError } from "../ApiError";
import { WidgetStore } from "../../db/WidgetStore";
import { NebStore } from "../../db/NebStore";
export interface IntegrationsResponse {
widgets: Widget[],
@ -14,14 +15,18 @@ export interface IntegrationsResponse {
export class DimensionIntegrationsService {
public static async getIntegrations(isEnabledCheck?: boolean): Promise<IntegrationsResponse> {
const cachedWidgets = Cache.for(CACHE_INTEGRATIONS).get("integrations_" + isEnabledCheck);
if (cachedWidgets) {
return {widgets: cachedWidgets};
const cachedIntegrations = Cache.for(CACHE_INTEGRATIONS).get("integrations_" + isEnabledCheck);
if (cachedIntegrations) {
return cachedIntegrations;
}
const widgets = await WidgetStore.listAll(isEnabledCheck);
Cache.for(CACHE_INTEGRATIONS).put("integrations_" + isEnabledCheck, widgets);
return {widgets: widgets};
const integrations = {
widgets: await WidgetStore.listAll(isEnabledCheck),
bots: await NebStore.listSimpleBots(), // No enabled check - managed internally
};
Cache.for(CACHE_INTEGRATIONS).put("integrations_" + isEnabledCheck, integrations);
return integrations;
}
@GET

View File

@ -9,6 +9,7 @@ import NebBotUser from "./models/NebBotUser";
import NebNotificationUser from "./models/NebNotificationUser";
import { AppserviceStore } from "./AppserviceStore";
import config from "../config";
import { SimpleBot } from "../integrations/SimpleBot";
export interface SupportedIntegration {
type: string;
@ -32,16 +33,19 @@ export class NebStore {
name: "Echo",
avatarUrl: "/img/avatars/echo.png", // TODO: Make this image
description: "Repeats text given to it from !echo",
simple: true,
},
"giphy": {
name: "Giphy",
avatarUrl: "/img/avatars/giphy.png",
description: "Posts a GIF from Giphy using !giphy <query>",
simple: true,
},
"guggy": {
name: "Guggy",
avatarUrl: "/img/avatars/guggy.png",
description: "Send a reaction GIF using !guggy <query>",
simple: true,
},
// TODO: Support Github
// "github": {
@ -53,11 +57,13 @@ export class NebStore {
name: "Google",
avatarUrl: "/img/avatars/google.png",
description: "Searches Google Images using !google image <query>",
simple: true,
},
"imgur": {
name: "Imgur",
avatarUrl: "/img/avatars/imgur.png",
description: "Searches and posts images from Imgur using !imgur <query>",
simple: true,
},
// TODO: Support JIRA
// "jira": {
@ -79,9 +85,34 @@ export class NebStore {
name: "Wikipedia",
avatarUrl: "/img/avatars/wikipedia.png",
description: "Searches wikipedia using !wikipedia <query>",
simple: true,
},
};
public static async listSimpleBots(): Promise<SimpleBot[]> {
const configs = await NebStore.getAllConfigs();
const integrations: { integration: NebIntegration, userId: string }[] = [];
const hasTypes: string[] = [];
for (const config of configs) {
for (const integration of config.dbIntegrations) {
if (!integration.isEnabled) continue;
const metadata = NebStore.INTEGRATIONS[integration.type];
if (!metadata || !metadata.simple) continue;
if (hasTypes.indexOf(integration.type) !== -1) continue;
// TODO: Handle case of upstream bots
const user = await NebStore.getOrCreateBotUser(config.id, integration.type);
integrations.push({integration: integration, userId: user.appserviceUserId});
hasTypes.push(integration.type);
}
}
return integrations.map(i => new SimpleBot(i.integration, i.userId));
}
public static async getAllConfigs(): Promise<NebConfig[]> {
const configs = await NebConfiguration.findAll();
return Promise.all((configs || []).map(c => NebStore.getConfig(c.id)));

View File

@ -25,7 +25,7 @@ export class Integration {
}
export interface IntegrationRequirement {
condition: "publicRoom" | "canSendEventTypes";
condition: "publicRoom" | "canSendEventTypes" | "userInRoom";
argument: any;
// For publicRoom this is true or false (boolean)

View File

@ -0,0 +1,13 @@
import { Integration } from "./Integration";
import NebIntegration from "../db/models/NebIntegration";
export class SimpleBot extends Integration {
constructor(bot: NebIntegration, public userId: string) {
super(bot);
this.category = "bot";
this.requirements = [];
// We're going to go ahead and claim that none of the bots are supported in e2e rooms
this.isEncryptionSupported = false;
}
}

View File

@ -8,6 +8,7 @@ export class NebConfig {
public appserviceId?: string;
public upstreamId?: number;
public integrations: Integration[];
public dbIntegrations: NebIntegration[];
public constructor(config: NebConfiguration, integrations: NebIntegration[]) {
this.id = config.id;
@ -15,5 +16,6 @@ export class NebConfig {
this.appserviceId = config.appserviceId;
this.upstreamId = config.upstreamId;
this.integrations = integrations.map(i => new Integration(i));
this.dbIntegrations = integrations;
}
}

View File

@ -4,7 +4,7 @@ import { ActivatedRoute, Router } from "@angular/router";
import { ScalarClientApiService } from "../../shared/services/scalar/scalar-client-api.service";
import * as _ from "lodash";
import { ScalarServerApiService } from "../../shared/services/scalar/scalar-server-api.service";
import { FE_Integration, FE_IntegrationRequirement } from "../../shared/models/integration";
import { FE_Integration, FE_IntegrationRequirement, FE_SimpleBot } from "../../shared/models/integration";
import { IntegrationsRegistry } from "../../shared/registry/integrations.registry";
import { SessionStorage } from "../../shared/SessionStorage";
import { AdminApiService } from "../../shared/services/admin/admin-api.service";
@ -129,13 +129,15 @@ export class RiotHomeComponent {
if (integration.category === "bot") {
// It's a bot
const bot = <FE_SimpleBot>integration;
// TODO: "Are you sure?" dialog
// let promise = null;
const promise = Promise.resolve();
// if (!integration._inRoom) {
// promise = this.scalar.inviteUser(this.roomId, integration.userId);
// } else promise = this.api.removeIntegration(this.roomId, integration.type, integration.integrationType, this.scalarToken);
let promise:Promise<any> = Promise.resolve();
if (!integration._inRoom) {
promise = this.scalar.inviteUser(this.roomId, bot.userId);
}
// TODO: Handle removal of bots
// else promise = this.api.removeIntegration(this.roomId, integration.type, integration.integrationType, this.scalarToken);
// We set this ahead of the promise for debouncing
integration._inRoom = !integration._inRoom;
@ -228,11 +230,16 @@ export class RiotHomeComponent {
console.log("Failed to find integration component for category=" + category + " type=" + type);
}
private updateIntegrationState(integration: FE_Integration) {
private async updateIntegrationState(integration: FE_Integration) {
if (!integration.requirements) return;
let promises = integration.requirements.map(r => this.checkRequirement(r));
if (integration.category === "bot") {
const state = await this.scalar.getMembershipState(this.roomId, (<FE_SimpleBot>integration).userId);
integration._inRoom = ["join", "invite"].indexOf(state.response.membership) !== -1;
}
return Promise.all(promises).then(() => {
integration._isSupported = true;
integration._notSupportedReason = null;

View File

@ -16,6 +16,10 @@ export interface FE_Integration {
_notSupportedReason: string;
}
export interface FE_SimpleBot extends FE_Integration {
userId: string;
}
export interface FE_Widget extends FE_Integration {
options: any;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB