From 30cfe080cebf11cbaad52527f3c91db9a4f0658b Mon Sep 17 00:00:00 2001 From: turt2live Date: Sun, 4 Jun 2017 21:31:31 -0600 Subject: [PATCH 1/7] Start of the IRC bridge config: Pull networks and channels TODO: * Provisioning support (IRC API) * Deprovisioning support (IRC API) * Ops query (IRC API) * State update interval --- config/integrations/irc-bridge.yaml | 10 ++ docs/dimension_api.md | 120 ++++++++++++++- docs/integrations/irc_bridge.md | 47 ++++++ docs/scalar_server_api.md | 142 ++++++++++++++++++ src/DimensionApi.js | 9 +- src/integration/generic_types/Bridge.js | 30 ++++ src/integration/impl/index.js | 7 +- src/integration/impl/irc/IRCBridge.js | 44 ++++++ src/integration/impl/irc/IRCFactory.js | 12 ++ .../impl/irc/StubbedIrcBackbone.js | 29 ++++ src/integration/impl/irc/VectorIrcBackbone.js | 69 +++++++++ src/scalar/VectorScalarClient.js | 62 +++++++- web/app/app.module.ts | 3 + web/app/configs/irc/irc-config.component.html | 10 ++ web/app/configs/irc/irc-config.component.scss | 1 + web/app/configs/irc/irc-config.component.ts | 25 +++ .../integration/integration.component.html | 8 +- web/app/riot/riot.component.ts | 38 +++++ web/app/shared/integration.service.ts | 7 + web/app/shared/models/integration.ts | 7 + web/app/shared/models/scalar_responses.ts | 6 + web/app/shared/scalar.service.ts | 8 +- web/public/img/avatars/irc.png | Bin 0 -> 5692 bytes 23 files changed, 684 insertions(+), 10 deletions(-) create mode 100644 config/integrations/irc-bridge.yaml create mode 100644 docs/integrations/irc_bridge.md create mode 100644 src/integration/generic_types/Bridge.js create mode 100644 src/integration/impl/irc/IRCBridge.js create mode 100644 src/integration/impl/irc/IRCFactory.js create mode 100644 src/integration/impl/irc/StubbedIrcBackbone.js create mode 100644 src/integration/impl/irc/VectorIrcBackbone.js create mode 100644 web/app/configs/irc/irc-config.component.html create mode 100644 web/app/configs/irc/irc-config.component.scss create mode 100644 web/app/configs/irc/irc-config.component.ts create mode 100644 web/public/img/avatars/irc.png diff --git a/config/integrations/irc-bridge.yaml b/config/integrations/irc-bridge.yaml new file mode 100644 index 0000000..6350fe6 --- /dev/null +++ b/config/integrations/irc-bridge.yaml @@ -0,0 +1,10 @@ +type: "bridge" +integrationType: "irc" +enabled: true +name: "IRC" +about: "Bridges an IRC channel to the room" +avatar: "/img/avatars/irc.png" +requirements: + joinRule: 'public' +upstream: + type: "vector" \ No newline at end of file diff --git a/docs/dimension_api.md b/docs/dimension_api.md index ae109f1..0d96b18 100644 --- a/docs/dimension_api.md +++ b/docs/dimension_api.md @@ -1,5 +1,121 @@ # Dimension API -This document describes the various endpoints of Dimension and how to interact with the API. +Dimension has its own API that allows for management of integrations in Riot/Matrix. -Coming soon? \ No newline at end of file +## Types of integrations + +### Simple Bots + +* Can only be in a room or not +* No state information held + +### Complex Bots + +* Simple Bots that hold state information + +### Bridges + +* Manage their own state through dedicated API endpoints + +## Endpoints + +### `GET /api/v1/dimension/integrations/{roomId}?scalar_token=your_token_here` + +**Parameters** +* `{roomId}` - The room ID to get integrations for +* `scalar_token` - The scalar (dimension) token to authenticate with + +**Example Response** +``` +TODO +``` + +### `DELETE /api/v1/dimension/integrations/{roomId}/{type}/{integrationType}?scalar_token=your_token_here` + +**Parameters** +* `{roomId}` - The room ID to remove the integration from +* `{type}` - The integration type (eg: `bot`, `complex-bot`, `bridge`, etc) +* `{integrationType}` - The integration subtype (eg: `irc`, `rssbot`, `giphy`, etc) +* `scalar_token` - The scalar (dimension) token to authenticate with + +**Example Response** +``` +TODO +``` + +### `PUT /api/v1/dimension/integrations/{roomId}/{type}/{integrationType}/state` + +**Parameters** +* `{roomId}` - The room ID to update the integration state in +* `{type}` - The integration type (eg: `bot`, `complex-bot`, `bridge`, etc) +* `{integrationType}` - The integration subtype (eg: `irc`, `rssbot`, `giphy`, etc) + +**Example Body** +``` +{ + "scalar_token": "your_token_here", + "state": { + // integration specific state goes here + } +} +``` + +### `GET /api/v1/dimension/integrations/{roomId}/{type}/{integrationType}/state` + +**Parameters** +* `{roomId}` - The room ID to get the integration state in +* `{type}` - The integration type (eg: `bot`, `complex-bot`, `bridge`, etc) +* `{integrationType}` - The integration subtype (eg: `irc`, `rssbot`, `giphy`, etc) + +**Response** + +An object representing the integration-specific state. See the documentation for the desired integration for more information. + +## Integration State Information + +### Simple Bots + +Do not hold state. + +### Complex Bots + +#### RSS Bot +``` +{ + // Mutable using state API + "feeds": [ + "https://some.domain.com/feed.rss", + "https://some.domain.com/another_feed.rss" + ], + + // Read only. Controlled by other users. + "immutableFeeds": [ + "https://some.domain.com/third_feed.rss", + "https://some.domain.com/fourth_feed.rss" + ] +} +``` + +### Bridges + +#### IRC +``` +{ + // Read only + "availableNetworks": [ + {"name": "Freenode", "id": "freenode"}, + {"name": "EsperNet", "id": "espernet"}, + {"name": "OFTC", "id": "oftc"} + ], + + // Read only. Use IRC API to mutate + "channels": { + "freenode": [ + "#dimensiontesting", + "#dimensiontest" + ], + "espernet": [], + "oftc": [] + } +} +``` \ No newline at end of file diff --git a/docs/integrations/irc_bridge.md b/docs/integrations/irc_bridge.md new file mode 100644 index 0000000..36fa71c --- /dev/null +++ b/docs/integrations/irc_bridge.md @@ -0,0 +1,47 @@ +# Dimension IRC Bridge API + +As with most bridges, the IRC bridge uses a dedicated set of API endpoints to manage the state of the bridge. The IRC bridge still uses the state API provided by Dimension to report basic state information, but does not allow edits through the regular API. Instead, it is expected that the IRC API be used to mutate the state. + +## Getting available networks/bridged channels + +Make a call to the Dimension state API: `GET /api/v1/dimension/integrations/{roomId}/bridge/irc/state?scalar_token=...`. + +*Example state* +``` +{ + "availableNetworks": [ + {"name": "Freenode", "id": "freenode"}, + {"name": "Espernet", "id": "espernet"}, + {"name": "OFTC", "id": "oftc"} + ], + "channels": { + "freenode": [ + "#dimensiontesting", + "#dimensiontest" + ], + "espernet": [], + "oftc": [] + } +} +``` + +## Getting the OPs in a channel + +IRC API Endpoint: `GET /api/v1/irc/{network}/{channel}/ops?scalar_token=...`. Be sure to encode the channel parameter. + +*Example response* +``` +["turt2live", "johndoe"] +``` + +## Linking a new channel + +IRC API Endpoint: `PUT /api/v1/irc/{roomId}/channels/{network}/{channel}?op=turt2live&scalar_token=...`. Be sure to encode the channel parameter. + +A 200 OK is returned if the request to add the channel was sent. The channel will not appear in the state information until the op has approved the bridge. + +## Unlinking a channel + +IRC API Endpoint: `DELETE /api/v1/irc/{roomId}/channels/{network}/{channel}?scalar_token=...`. Be sure to encode the channel parameter. + +A 200 OK is returned if the delete was successful. \ No newline at end of file diff --git a/docs/scalar_server_api.md b/docs/scalar_server_api.md index 2b0f88f..8195851 100644 --- a/docs/scalar_server_api.md +++ b/docs/scalar_server_api.md @@ -178,4 +178,146 @@ None of these are officially documented, and are subject to change. } } } +``` + +## GET `/api/bridges/irc/_matrix/provision/querynetworks?scalar_token=...` + +**Response** +``` +{ + "replies": [ + { + "rid": "...", + "response": { + "servers": [ + { + "bot_user_id": "@appservice-irc:matrix.org", + "desc": "Freenode", + "fields": { + "domain": "chat.freenode.net" + }, + "icon": "https:\/\/matrix.org\/_matrix\/media\/v1\/download\/matrix.org\/DHLHpDDgWNNejFmrewvwEAHX", + "network_id": "freenode" + } + ] + } + }, + { + "rid": "...", + "response": { + "servers": [ + { + "bot_user_id": "@mozilla-irc:matrix.org", + "desc": "Moznet", + "fields": { + "domain": "irc.mozilla.org" + }, + "icon": "https:\/\/matrix.org\/_matrix\/media\/v1\/download\/matrix.org\/DHLHpDDgWNNejFmrewvwEAHX", + "network_id": "mozilla" + } + ] + } + } + ] +} +``` + +## POST `/api/bridges/irc/_matrix/provision/querylink?rid=...&scalar_token=...` + +**Body** +``` +{ + "remote_room_channel": "#dimensiontesting", + "remote_room_server": "chat.freenode.net" +} +``` + +**Response** +``` +{ + "replies": [{ + "rid": "...", + "response": { + "operators": ["travis-test"] + } + }] +} +``` + +## POST `/api/bridges/irc/_matrix/provision/link?rid=...&scalar_token=...` + +**Body** +``` +{ + "matrix_room_id": "!JmvocvDuPTYUfuvKgs:t2l.io", + "remote_room_channel": "#dimensiontesting", + "remote_room_server": "chat.freenode.net", + "op_nick": "travis-test", + "key": "" +} +``` + +**Response** +``` +{ + "replies": [{ + "rid": "...", + "response":{} + }] +} +``` + +*Note*: This returns 200 OK after sending the request to link. If the link succeeds, `listlinks` will show as such. + +## GET `/api/bridges/irc/_matrix/provision/listlinks/{roomId}?scalar_token=...` + +**Params** +* `{roomId}` - the matrix room id (ie: `!JmvocvDuPTYUfuvKgs:t2l.io`) + +**Response** +``` +{ + "replies": [ + { + "rid": "...", + "response": [ + { + "matrix_room_id": "!JmvocvDuPTYUfuvKgs:t2l.io", + "remote_room_channel": "#dimensiontesting", + "remote_room_server": "chat.freenode.net" + } + ] + }, + { + "rid": "...", + "response": [] + } + ] +} +``` + +*Note*: This is called on a timer in Scalar to show when a user has approved a link. Called every few seconds. + +## POST `/api/bridges/irc/_matrix/provision/unlink?rid=...&scalar_token=...` + +**Body** +``` +{ + "matrix_room_id": "!JmvocvDuPTYUfuvKgs:t2l.io", + "remote_room_channel": "#dimensiontest", + "remote_room_server": "chat.freenode.net", + "rid": "..." +} +``` + +**Response** +``` +{ + "replies": [ + { + "rid": "..", + "response": {} + } + ] +} ``` \ No newline at end of file diff --git a/src/DimensionApi.js b/src/DimensionApi.js index d85ac3d..66cd375 100644 --- a/src/DimensionApi.js +++ b/src/DimensionApi.js @@ -50,14 +50,15 @@ class DimensionApi { var promises = []; _.forEach(integrations, integration => { promises.push(this._getIntegration(integration, roomId, scalarToken).then(builtIntegration => { - return builtIntegration.getUserId().then(userId => { - integration.userId = userId; - return builtIntegration.getState(); - }).then(state => { + return builtIntegration.getState().then(state => { var keys = _.keys(state); for (var key of keys) { integration[key] = state[key]; } + + return builtIntegration.getUserId(); + }).then(userId => { + integration.userId = userId; }); })); }); diff --git a/src/integration/generic_types/Bridge.js b/src/integration/generic_types/Bridge.js new file mode 100644 index 0000000..85e4cd5 --- /dev/null +++ b/src/integration/generic_types/Bridge.js @@ -0,0 +1,30 @@ +var IntegrationStub = require("./IntegrationStub"); + +/** + * Represents a bridge. Normally bridges have enhanced configuration and requirements over bots. + */ +class Bridge extends IntegrationStub { + + /** + * Creates a new bridge + * @param bridgeConfig the configuration for the bridge + */ + constructor(bridgeConfig) { + super(bridgeConfig); + } + + /** + * Registers the API routes for this bridge with the given app. + * @param app the app to register the routes on + */ + registerApi(app) { + // nothing + } + + /*override*/ + getUserId() { + return null; // bridges don't have bot users we care about + } +} + +module.exports = Bridge; \ No newline at end of file diff --git a/src/integration/impl/index.js b/src/integration/impl/index.js index b44c01d..93a1f92 100644 --- a/src/integration/impl/index.js +++ b/src/integration/impl/index.js @@ -2,16 +2,21 @@ var log = require("../../util/LogService"); var StubbedFactory = require("./StubbedFactory"); var SimpleBotFactory = require("./simple_bot/SimpleBotFactory"); var RSSFactory = require("./rss/RSSFactory"); +var IRCFactory = require("./irc/IRCFactory"); var mapping = { "complex-bot": { "rss": RSSFactory + }, + "bridge": { + "irc": IRCFactory } }; var defaultFactories = { "complex-bot": null, - "bot": SimpleBotFactory + "bot": SimpleBotFactory, + "bridge": null }; module.exports = { diff --git a/src/integration/impl/irc/IRCBridge.js b/src/integration/impl/irc/IRCBridge.js new file mode 100644 index 0000000..927f3cb --- /dev/null +++ b/src/integration/impl/irc/IRCBridge.js @@ -0,0 +1,44 @@ +var Bridge = require("../../generic_types/Bridge"); + +/** + * Represents an IRC bridge + */ +class IRCBridge extends Bridge { + + /** + * Creates a new IRC bridge + * @param bridgeConfig the bridge configuration + * @param backbone the backbone powering this bridge + */ + constructor(bridgeConfig, backbone) { + super(bridgeConfig); + this._backbone = backbone; + } + + /*override*/ + getState() { + var response = { + availableNetworks: [], + channels: {} + }; + return this._backbone.getNetworks().then(networks => { + response.availableNetworks = networks; + return this._backbone.getLinkedChannels(); + }).then(channels => { + response.channels = channels; + return response; + }); + } + + /*override*/ + removeFromRoom(roomId) { + return this._backbone.removeFromRoom(roomId); + } + + /*override*/ + updateState(newState) { + throw new Error("State cannot be updated for an IRC bridge. Use the IRC API instead."); + } +} + +module.exports = IRCBridge; \ No newline at end of file diff --git a/src/integration/impl/irc/IRCFactory.js b/src/integration/impl/irc/IRCFactory.js new file mode 100644 index 0000000..fe6c660 --- /dev/null +++ b/src/integration/impl/irc/IRCFactory.js @@ -0,0 +1,12 @@ +var IRCBridge = require("./IRCBridge"); +var VectorIrcBackbone = require("./VectorIrcBackbone"); + +module.exports = (db, integrationConfig, roomId, scalarToken) => { + if (integrationConfig.upstream) { + if (integrationConfig.upstream.type !== "vector") throw new Error("Unsupported upstream"); + return db.getUpstreamToken(scalarToken).then(upstreamToken => { + var backbone = new VectorIrcBackbone(roomId, upstreamToken); + return new IRCBridge(integrationConfig, backbone); + }); + } else throw new Error("Unsupported config"); +}; \ No newline at end of file diff --git a/src/integration/impl/irc/StubbedIrcBackbone.js b/src/integration/impl/irc/StubbedIrcBackbone.js new file mode 100644 index 0000000..fea0e56 --- /dev/null +++ b/src/integration/impl/irc/StubbedIrcBackbone.js @@ -0,0 +1,29 @@ +/** + * Stubbed/placeholder IRC backbone + */ +class StubbedIrcBackbone { + + /** + * Creates a new stubbed IRC backbone + */ + constructor() { + } + + /** + * Gets a list of all available networks + * @returns {Promise<{name: string, id: string}[]>} resolves to the list of available networks + */ + getNetworks() { + return Promise.resolve([]); + } + + /** + * Gets a network representation of the linked channels + * @returns {Promise<{[string]: string[]}>} resolves to the network representation of linked channels + */ + getLinkedChannels() { + return Promise.resolve({}); + } +} + +module.exports = StubbedIrcBackbone; \ No newline at end of file diff --git a/src/integration/impl/irc/VectorIrcBackbone.js b/src/integration/impl/irc/VectorIrcBackbone.js new file mode 100644 index 0000000..9225c13 --- /dev/null +++ b/src/integration/impl/irc/VectorIrcBackbone.js @@ -0,0 +1,69 @@ +var StubbedIrcBackbone = require("./StubbedIrcBackbone"); +var VectorScalarClient = require("../../../scalar/VectorScalarClient"); +var _ = require("lodash"); +var log = require("../../../util/LogService"); + +/** + * Backbone for IRC bridges running on vector.im through scalar + */ +class VectorIrcBackbone extends StubbedIrcBackbone { + + /** + * Creates a new Vector IRC backbone + * @param {string} roomId the room ID to manage + * @param {string} upstreamScalarToken the vector scalar token + */ + constructor(roomId, upstreamScalarToken) { + super(); + this._roomId = roomId; + this._scalarToken = upstreamScalarToken; + this._lastNetworkResponse = null; + } + + /*override*/ + getNetworks() { + return this._getNetworks().then(networks => _.map(networks, n => { + return {name: n.title, id: n.id}; + })); + } + + /*override*/ + getLinkedChannels() { + var networks; + return this._getNetworks().then(n => { + networks = n; + return VectorScalarClient.getIrcLinks(this._roomId, this._scalarToken); + }).then(links => { + var container = {}; + + var ridToServerId = {}; + + for (var network of networks) { + ridToServerId[network.rid] = network.id; + container[network.id] = []; + } + + for (var link of links) { + var server = ridToServerId[link.rid]; + if (!server) { + log.error("VectorIrcBackbone", "Could not find network for RID " + link.rid); + throw new Error("Unexpected RID"); + } + + container[server.id].push(link.channel); + } + + return container; + }); + } + + _getNetworks() { + if (this._lastNetworkResponse !== null) return Promise.resolve(this._lastNetworkResponse); + return VectorScalarClient.getIrcNetworks(this._scalarToken).then(networks => { + this._lastNetworkResponse = networks; + return networks; + }); + } +} + +module.exports = VectorIrcBackbone; \ No newline at end of file diff --git a/src/scalar/VectorScalarClient.js b/src/scalar/VectorScalarClient.js index 2a44aa5..ec685f7 100644 --- a/src/scalar/VectorScalarClient.js +++ b/src/scalar/VectorScalarClient.js @@ -82,7 +82,7 @@ class VectorScalarClient { } /** - * Gets information on + * Gets information for an integration * @param {string} type the type to lookup * @param {string} roomId the room ID to look in * @param {string} scalarToken the scalar token @@ -99,6 +99,66 @@ class VectorScalarClient { }); } + /** + * Gets a list of supported IRC networks + * @param {string} scalarToken the scalar token + * @returns {Promise<{rid: string, title: string, domain: string, id: string}[]>} resolves to the list of IRC networks + */ + getIrcNetworks(scalarToken) { + return this._do("GET", "/bridges/irc/_matrix/provision/querynetworks", {scalar_token: scalarToken}).then((response, body) => { + if (response.statusCode !== 200) { + log.error("VectorScalarClient", response.body); + return Promise.reject(response.body); + } + + response.body = JSON.parse(response.body); + + var results = []; + for (var network of response.body["replies"]) { + var result = { + rid: network["rid"], + // Assumption: All networks have 1 server from vector + id: network["response"]["servers"][0]["network_id"], + title: network["response"]["servers"][0]["desc"], + domain: network["response"]["servers"][0]["fields"]["domain"] + }; + results.push(result); + } + + return results; + }); + } + + /** + * Gets a list of all linked IRC channels for a given room + * @param {string} roomId the room ID to look in + * @param {string} scalarToken the scalar token + * @returns {Promise<{rid: string, server: string, channel: string}>} resolves to a list of linked channels + */ + getIrcLinks(roomId, scalarToken) { + return this._do("GET", "/bridges/irc/_matrix/provision/listlinks/" + roomId, {scalar_token: scalarToken}).then((response, body) => { + if (response.statusCode !== 200) { + log.error("VectorScalarClient", response.body); + return Promise.reject(response.body); + } + + response.body = JSON.parse(response.body); + + var results = []; + for (var linkContainer of response.body["replies"]) { + for (var link of linkContainer["response"]) { + results.push({ + rid: linkContainer["rid"], + server: link["remote_room_server"], + channel: link["remote_room_channel"] + }); + } + } + + return results; + }); + } + _do(method, endpoint, qs = null, body = null) { var url = config.get("upstreams.vector") + endpoint; diff --git a/web/app/app.module.ts b/web/app/app.module.ts index 3757b08..8853005 100644 --- a/web/app/app.module.ts +++ b/web/app/app.module.ts @@ -19,6 +19,7 @@ import { IntegrationService } from "./shared/integration.service"; import { BootstrapModalModule } from "angular2-modal/plugins/bootstrap"; import { ModalModule } from "angular2-modal"; import { RssConfigComponent } from "./configs/rss/rss-config.component"; +import { IrcConfigComponent } from "./configs/irc/irc-config.component"; @NgModule({ imports: [ @@ -40,6 +41,7 @@ import { RssConfigComponent } from "./configs/rss/rss-config.component"; IntegrationComponent, ScalarCloseComponent, RssConfigComponent, + IrcConfigComponent, // Vendor ], @@ -53,6 +55,7 @@ import { RssConfigComponent } from "./configs/rss/rss-config.component"; bootstrap: [AppComponent], entryComponents: [ RssConfigComponent, + IrcConfigComponent, ] }) export class AppModule { diff --git a/web/app/configs/irc/irc-config.component.html b/web/app/configs/irc/irc-config.component.html new file mode 100644 index 0000000..f458943 --- /dev/null +++ b/web/app/configs/irc/irc-config.component.html @@ -0,0 +1,10 @@ +
+ +
+ +

Configure IRC Bridge

+
+
+
{{ integration | json }}
+
+
\ No newline at end of file diff --git a/web/app/configs/irc/irc-config.component.scss b/web/app/configs/irc/irc-config.component.scss new file mode 100644 index 0000000..12e299a --- /dev/null +++ b/web/app/configs/irc/irc-config.component.scss @@ -0,0 +1 @@ +// component styles are encapsulated and only applied to their components diff --git a/web/app/configs/irc/irc-config.component.ts b/web/app/configs/irc/irc-config.component.ts new file mode 100644 index 0000000..820e544 --- /dev/null +++ b/web/app/configs/irc/irc-config.component.ts @@ -0,0 +1,25 @@ +import { Component } from "@angular/core"; +import { IRCIntegration } from "../../shared/models/integration"; +import { ModalComponent, DialogRef } from "angular2-modal"; +import { ConfigModalContext } from "../../integration/integration.component"; + +@Component({ + selector: 'my-irc-config', + templateUrl: './irc-config.component.html', + styleUrls: ['./irc-config.component.scss', './../config.component.scss'], +}) +export class IrcConfigComponent implements ModalComponent { + + public integration: IRCIntegration; + + private roomId: string; + private scalarToken: string; + + constructor(public dialog: DialogRef) {// , + // private toaster: ToasterService, + // private api: ApiService) { + this.integration = dialog.context.integration; + this.roomId = dialog.context.roomId; + this.scalarToken = dialog.context.scalarToken; + } +} diff --git a/web/app/integration/integration.component.html b/web/app/integration/integration.component.html index 893e59a..0c6cbb2 100644 --- a/web/app/integration/integration.component.html +++ b/web/app/integration/integration.component.html @@ -3,9 +3,15 @@
{{ integration.name }}
-
+
+
+ +
+
+ +
diff --git a/web/app/riot/riot.component.ts b/web/app/riot/riot.component.ts index 9b61638..da44e67 100644 --- a/web/app/riot/riot.component.ts +++ b/web/app/riot/riot.component.ts @@ -54,6 +54,26 @@ export class RiotComponent { private updateIntegrationState(integration: Integration) { integration.hasConfig = IntegrationService.hasConfig(integration); + if (integration.requirements) { + let keys = _.keys(integration.requirements); + let promises = []; + + for (let key of keys) { + let requirement = this.checkRequirement(integration, key); + promises.push(requirement); + } + + return Promise.all(promises).then(() => { + integration.isEnabled = true; + integration.isBroken = false; + }, error => { + console.error(error); + integration.bridgeError = error.message; + integration.isEnabled = false; + integration.isBroken = false; + }); + } + return this.scalar.getMembershipState(this.roomId, integration.userId).then(payload => { integration.isBroken = false; @@ -70,6 +90,24 @@ export class RiotComponent { }); } + private checkRequirement(integration: Integration, key: string) { + let requirement = integration.requirements[key]; + + switch (key) { + case "joinRule": + return this.scalar.getJoinRule(this.roomId).then(payload => { + if (!payload.response) { + return Promise.reject("Could not communicate with Riot"); + } + return payload.response.join_rule === requirement + ? Promise.resolve() + : Promise.reject(new Error("The room must be " + requirement + " to use this integration.")); + }); + default: + return Promise.reject(new Error("Requirement '" + key + "' not found")); + } + } + public updateIntegration(integration: Integration) { let promise = null; diff --git a/web/app/shared/integration.service.ts b/web/app/shared/integration.service.ts index 6f8c93f..b68b837 100644 --- a/web/app/shared/integration.service.ts +++ b/web/app/shared/integration.service.ts @@ -2,6 +2,7 @@ import { Injectable } from "@angular/core"; import { Integration } from "./models/integration"; import { RssConfigComponent } from "../configs/rss/rss-config.component"; import { ContainerContent } from "angular2-modal"; +import { IrcConfigComponent } from "../configs/irc/irc-config.component"; @Injectable() export class IntegrationService { @@ -10,12 +11,18 @@ export class IntegrationService { "bot": true, "complex-bot": { "rss": true + }, + "bridge": { + "irc": true } }; private static components = { "complex-bot": { "rss": RssConfigComponent + }, + "bridge": { + "irc": IrcConfigComponent } }; diff --git a/web/app/shared/models/integration.ts b/web/app/shared/models/integration.ts index e4a809b..b2fbb32 100644 --- a/web/app/shared/models/integration.ts +++ b/web/app/shared/models/integration.ts @@ -8,9 +8,16 @@ export interface Integration { isEnabled: boolean; isBroken: boolean; hasConfig: boolean; + requirements?: any; // nullable + bridgeError: string; // nullable } export interface RSSIntegration extends Integration { feeds: string[]; immutableFeeds: {url: string, ownerId: string}[]; +} + +export interface IRCIntegration extends Integration { + availableNetworks: {name: string, id: string}; + channels: {[networkId: string]: string[]}; } \ No newline at end of file diff --git a/web/app/shared/models/scalar_responses.ts b/web/app/shared/models/scalar_responses.ts index 93b1231..4b3e4be 100644 --- a/web/app/shared/models/scalar_responses.ts +++ b/web/app/shared/models/scalar_responses.ts @@ -24,4 +24,10 @@ export interface MembershipStateResponse extends ScalarUserResponse { avatar_url: string; displayname: string; }; +} + +export interface JoinRuleStateResponse extends ScalarRoomResponse { + response: { + join_rule: string; + }; } \ No newline at end of file diff --git a/web/app/shared/scalar.service.ts b/web/app/shared/scalar.service.ts index dd00527..abb2cbb 100644 --- a/web/app/shared/scalar.service.ts +++ b/web/app/shared/scalar.service.ts @@ -1,6 +1,6 @@ import { Injectable } from "@angular/core"; import * as randomString from "random-string"; -import { MembershipStateResponse, ScalarSuccessResponse } from "./models/scalar_responses"; +import { MembershipStateResponse, ScalarSuccessResponse, JoinRuleStateResponse } from "./models/scalar_responses"; @Injectable() export class ScalarService { @@ -30,6 +30,12 @@ export class ScalarService { }); } + public getJoinRule(roomId: string): Promise { + return this.callAction("join_rules_state", { + room_id: roomId + }); + } + public close(): void { this.callAction("close_scalar", {}); } diff --git a/web/public/img/avatars/irc.png b/web/public/img/avatars/irc.png new file mode 100644 index 0000000000000000000000000000000000000000..bc24d31918e1bce89426e6a6c40070d291cfc345 GIT binary patch literal 5692 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 literal 0 HcmV?d00001 From 6fc1abc2a1d0e1ad12fec6b40b96e71a6ac93815 Mon Sep 17 00:00:00 2001 From: turt2live Date: Fri, 9 Jun 2017 20:21:54 -0600 Subject: [PATCH 2/7] Make webpack build slightly more annoying --- webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index 1bbf927..80a38e8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -122,7 +122,7 @@ module.exports = function () { config.devServer = { contentBase: './web/public', historyApiFallback: true, - quiet: true, + quiet: false, stats: 'minimal', proxy: { '/api': { From b5190ce6763d1bf171f4fbc1cd03d5c59b2f8f07 Mon Sep 17 00:00:00 2001 From: turt2live Date: Fri, 9 Jun 2017 20:22:10 -0600 Subject: [PATCH 3/7] Explicit upgrades to node packages --- package-lock.json | 176 +++++++++++++++++++++++++++++++--------------- package.json | 20 +++--- 2 files changed, 128 insertions(+), 68 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5daa62f..a00f8d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,8 +4,9 @@ "lockfileVersion": 1, "dependencies": { "@angular/animations": { - "version": "https://registry.npmjs.org/@angular/animations/-/animations-4.1.3.tgz", - "integrity": "sha1-bomh4Pv9bQ6Qvk8q4ZCqxn+DpBE=", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-4.2.1.tgz", + "integrity": "sha1-TIeIGS8Ux06wKg/I3Dhxd6ux/yQ=", "dev": true }, "@angular/common": { @@ -68,8 +69,9 @@ "integrity": "sha1-sC0QqwKOKSisWSoFGqpJgaGUHQM=" }, "@types/node": { - "version": "https://registry.npmjs.org/@types/node/-/node-7.0.22.tgz", - "integrity": "sha1-RZP02Ci91hKSlHjqQMZ7T0A8olU=", + "version": "7.0.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.29.tgz", + "integrity": "sha512-+8JrLZny/uR+d/jLK9eaV63buRM7X/gNzQk57q76NS4KNKLSKOmxJYFIlwuP2zDvA7wqZj05POPhSd9Z1hYQpQ==", "dev": true }, "abbrev": { @@ -533,6 +535,11 @@ "integrity": "sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY=", "dev": true }, + "cls-bluebird": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cls-bluebird/-/cls-bluebird-2.0.1.tgz", + "integrity": "sha1-wlmkgK4CwOUGE0MHuxPbMERu4uc=" + }, "co": { "version": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" @@ -720,17 +727,20 @@ "dev": true }, "css-loader": { - "version": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.3.tgz", - "integrity": "sha1-n9XguMQFtt+Se6EQOIcBXTYGQM4=", + "version": "0.28.4", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.4.tgz", + "integrity": "sha1-bPNXkZLONV6LONX0Ldeh8uyJjQ8=", "dev": true, "dependencies": { "json5": { - "version": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "dev": true }, "loader-utils": { - "version": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", "dev": true } @@ -920,10 +930,6 @@ "version": "https://registry.npmjs.org/dotenv/-/dotenv-2.0.0.tgz", "integrity": "sha1-vXWcNXqqcDZeAclrewvsCKbg2Uk=" }, - "dottie": { - "version": "https://registry.npmjs.org/dottie/-/dottie-1.1.1.tgz", - "integrity": "sha1-RcKj9IvWUo7u0memmoSOqspvqmo=" - }, "ecc-jsbn": { "version": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", @@ -962,6 +968,11 @@ "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", "dev": true }, + "env-cmd": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/env-cmd/-/env-cmd-5.1.0.tgz", + "integrity": "sha1-AjbbOTw/AzAFIE/NCpLuQHI6nJ4=" + }, "errno": { "version": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=", @@ -1089,17 +1100,20 @@ "dev": true }, "file-loader": { - "version": "https://registry.npmjs.org/file-loader/-/file-loader-0.11.1.tgz", - "integrity": "sha1-azKO4SNKcp5OR9Njdd1tNcDh24Q=", + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-0.11.2.tgz", + "integrity": "sha512-N+uhF3mswIFeziHQjGScJ/yHXYt3DiLBeC+9vWW+WjUBiClMSOlV1YrXQi+7KM2aA3Rn4Bybgv+uXFQbfkzpvg==", "dev": true, "dependencies": { "json5": { - "version": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "dev": true }, "loader-utils": { - "version": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", "dev": true } @@ -1903,10 +1917,6 @@ "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", "dev": true }, - "generic-pool": { - "version": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.4.2.tgz", - "integrity": "sha1-iGvFvwvrfblugby7oHiBjeWmJoM=" - }, "get-caller-file": { "version": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", @@ -2137,6 +2147,26 @@ "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", "dev": true }, + "icss-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", + "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", + "dev": true, + "dependencies": { + "postcss": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.1.tgz", + "integrity": "sha1-AA29H47vIXqjaLmiEsX8QLKo8/I=", + "dev": true + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true + } + } + }, "ieee754": { "version": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", @@ -2208,6 +2238,11 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true }, + "is-bluebird": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bluebird/-/is-bluebird-1.0.2.tgz", + "integrity": "sha1-CWQ5Bg9KpBGr7hkUOoTWpVNG1uI=" + }, "is-buffer": { "version": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", @@ -2531,8 +2566,9 @@ "dev": true }, "matrix-js-sdk": { - "version": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-0.7.8.tgz", - "integrity": "sha1-TUZSCMvzZCZzw2/l8ZFhHFkW8vk=" + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-0.7.10.tgz", + "integrity": "sha1-VPo8xuOwqlkkMOmEnuVl2vfGQEk=" }, "media-typer": { "version": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -2779,11 +2815,6 @@ "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true }, - "olm": { - "version": "https://matrix.org/packages/npm/olm/olm-2.2.1.tgz", - "integrity": "sha1-Xl21DQoUK3x6BlDZs9isw9N+aXs=", - "optional": true - }, "on-finished": { "version": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=" @@ -3815,12 +3846,39 @@ } }, "sequelize": { - "version": "https://registry.npmjs.org/sequelize/-/sequelize-3.30.4.tgz", - "integrity": "sha1-vaLfHjGFSwmeQUmhEen8Clyh0aQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-4.0.0.tgz", + "integrity": "sha512-sKJHt36Leyzlhsy2g8b2Q5DQxM4F8aIVFOohjnIC6d6rtutWmLjhW/7H2ZEqG8n/3hbK1mfSBnKqZCO7+1sJYA==", "dependencies": { - "lodash": { - "version": "https://registry.npmjs.org/lodash/-/lodash-4.12.0.tgz", - "integrity": "sha1-K9bcRqBA9Z5obJcu0h2T3FkFMlg=" + "@types/node": { + "version": "6.0.78", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.78.tgz", + "integrity": "sha512-+vD6E8ixntRzzZukoF3uP1iV+ZjVN3koTcaeK+BEoc/kSfGbLDIGC7RmCaUgVpUfN6cWvfczFRERCyKM9mkvXg==" + }, + "dottie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.0.tgz", + "integrity": "sha1-2hkZgci41xPKARXViYzzl8Lw3dA=" + }, + "generic-pool": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.1.7.tgz", + "integrity": "sha1-2sIrLHp6BOQXMvfY0tJaMDyI9mI=" + }, + "inflection": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.10.0.tgz", + "integrity": "sha1-W//LEZetPoEFD44X4hZoCH7p6y8=" + }, + "validator": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-6.3.0.tgz", + "integrity": "sha1-R84j7Y1Ord+p1LjvAHG2zxB418g=" + }, + "wkx": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.4.1.tgz", + "integrity": "sha1-L8FxtenLVcYlb+9L3h8hvkE77+4=" } } }, @@ -3877,8 +3935,9 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, "shelljs": { - "version": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.7.tgz", - "integrity": "sha1-svXHfvlxSPS09uImguELuoZnz/E=", + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", + "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", "dev": true }, "shimmer": { @@ -4018,17 +4077,20 @@ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, "style-loader": { - "version": "https://registry.npmjs.org/style-loader/-/style-loader-0.18.1.tgz", - "integrity": "sha1-avyolTyEKDDl4tyEeWMJiAqX9+g=", + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.18.2.tgz", + "integrity": "sha512-WPpJPZGUxWYHWIUMNNOYqql7zh85zGmr84FdTVWq52WTIkqlW9xSxD3QYWi/T31cqn9UNSsietVEgGn2aaSCzw==", "dev": true, "dependencies": { "json5": { - "version": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "dev": true }, "loader-utils": { - "version": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", "dev": true } @@ -4126,9 +4188,18 @@ "dev": true }, "tslint": { - "version": "https://registry.npmjs.org/tslint/-/tslint-5.3.2.tgz", - "integrity": "sha1-5WRZ+wlacwfxA7hAUhdPXju+9u0=", - "dev": true + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.4.3.tgz", + "integrity": "sha1-dhyEArgONHt3M6BDkKdXslNYBGc=", + "dev": true, + "dependencies": { + "tsutils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.4.0.tgz", + "integrity": "sha1-rUzm26Dlo+2934Ymt8oEB4IYn+o=", + "dev": true + } + } }, "tslint-loader": { "version": "https://registry.npmjs.org/tslint-loader/-/tslint-loader-3.5.3.tgz", @@ -4147,11 +4218,6 @@ } } }, - "tsutils": { - "version": "https://registry.npmjs.org/tsutils/-/tsutils-2.2.0.tgz", - "integrity": "sha1-IYYUZX8hxnfkU2tLp12vjrzhs2c=", - "dev": true - }, "tty-browserify": { "version": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", @@ -4175,8 +4241,9 @@ "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=" }, "typescript": { - "version": "https://registry.npmjs.org/typescript/-/typescript-2.3.3.tgz", - "integrity": "sha1-ljnzw7QBSOjKl/4IpR3RiRu2viI=", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.3.4.tgz", + "integrity": "sha1-PTgyGCgjHkNPKHUUlZw3qCtin0I=", "dev": true }, "uglify-js": { @@ -4291,10 +4358,6 @@ "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", "dev": true }, - "validator": { - "version": "https://registry.npmjs.org/validator/-/validator-5.7.0.tgz", - "integrity": "sha1-eoelgUa2laxIYHEUHAxJ1n2gXlw=" - }, "vary": { "version": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz", "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc=" @@ -4474,10 +4537,6 @@ } } }, - "wkx": { - "version": "https://registry.npmjs.org/wkx/-/wkx-0.2.0.tgz", - "integrity": "sha1-dsJPFqzQzY+TzTSqMx4PeWElboQ=" - }, "wordwrap": { "version": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" @@ -4535,8 +4594,9 @@ } }, "zone.js": { - "version": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.11.tgz", - "integrity": "sha1-dCvvsX+8SaVxcSuMfYfljKJv2IY=", + "version": "0.8.12", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.12.tgz", + "integrity": "sha1-hv9QU8mK7CkaC/S7rFAdaUoFz7s=", "dev": true } } diff --git a/package.json b/package.json index b698d5b..d066149 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "express": "^4.15.2", "js-yaml": "^3.8.2", "lodash": "^4.17.4", - "matrix-js-sdk": "^0.7.8", + "matrix-js-sdk": "^0.7.10", "moment": "^2.18.1", "random-string": "^0.2.0", "request": "^2.81.0", @@ -31,7 +31,7 @@ "winston": "^2.3.1" }, "devDependencies": { - "@angular/animations": "^4.1.3", + "@angular/animations": "^4.2.1", "@angular/common": "4.1.3", "@angular/compiler": "4.1.3", "@angular/core": "4.1.3", @@ -43,7 +43,7 @@ "@angularclass/hmr": "^1.2.2", "@angularclass/hmr-loader": "^3.0.2", "@ng-bootstrap/ng-bootstrap": "^1.0.0-alpha.22", - "@types/node": "^7.0.18", + "@types/node": "^7.0.29", "angular2-modal": "^2.0.3", "angular2-template-loader": "^0.6.2", "angular2-toaster": "^4.0.0", @@ -53,10 +53,10 @@ "codelyzer": "^3.0.1", "copy-webpack-plugin": "^4.0.1", "core-js": "^2.4.1", - "css-loader": "^0.28.0", + "css-loader": "^0.28.4", "cssnano": "^3.10.0", "extract-text-webpack-plugin": "^2.1.2", - "file-loader": "^0.11.1", + "file-loader": "^0.11.2", "html-loader": "^0.4.5", "html-webpack-plugin": "^2.28.0", "jquery": "^3.2.1", @@ -71,14 +71,14 @@ "rimraf": "^2.6.1", "rxjs": "^5.2.0", "sass-loader": "^6.0.3", - "shelljs": "^0.7.0", - "style-loader": "^0.18.1", + "shelljs": "^0.7.8", + "style-loader": "^0.18.2", "ts-helpers": "^1.1.2", - "tslint": "^5.2.0", + "tslint": "^5.4.3", "tslint-loader": "^3.4.3", - "typescript": "^2.2.2", + "typescript": "^2.3.4", "url-loader": "^0.5.8", "webpack": "^2.3.2", - "zone.js": "^0.8.5" + "zone.js": "^0.8.12" } } From 138f77db757333b830b7e15faeb7408af44a733b Mon Sep 17 00:00:00 2001 From: turt2live Date: Sat, 10 Jun 2017 14:42:13 -0600 Subject: [PATCH 4/7] Upgrade other angular dependencies --- package-lock.json | 40 ++++++++++++++++++++++++---------------- package.json | 18 +++++++++--------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index a00f8d9..73b6e54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,43 +10,51 @@ "dev": true }, "@angular/common": { - "version": "https://registry.npmjs.org/@angular/common/-/common-4.1.3.tgz", - "integrity": "sha1-58R5HjITHPdMI5QowqZ9qrLu8Bc=", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-4.2.1.tgz", + "integrity": "sha1-yzNOO3H00+nzS3rZQsVI0/k238g=", "dev": true }, "@angular/compiler": { - "version": "https://registry.npmjs.org/@angular/compiler/-/compiler-4.1.3.tgz", - "integrity": "sha1-0t0whTsM9KVHWLSjFGMsIx+clMM=", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-4.2.1.tgz", + "integrity": "sha1-jXe0WOBLiHlH2FrczXVG5KmH1LA=", "dev": true }, "@angular/core": { - "version": "https://registry.npmjs.org/@angular/core/-/core-4.1.3.tgz", - "integrity": "sha1-KFSY64arfQtvmC+Pn0h+9hABOzU=", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-4.2.1.tgz", + "integrity": "sha1-CGUwSz30MEaRm2RvAv3tqFoMRoU=", "dev": true }, "@angular/forms": { - "version": "https://registry.npmjs.org/@angular/forms/-/forms-4.1.3.tgz", - "integrity": "sha1-OAq0w6+ExdHXSMKn0EFRx9yOSYI=", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-4.2.1.tgz", + "integrity": "sha1-rRcs/TBwdDsqeoFh0sdax/W2H2I=", "dev": true }, "@angular/http": { - "version": "https://registry.npmjs.org/@angular/http/-/http-4.1.3.tgz", - "integrity": "sha1-650cMCoBcoFfmlczENm+C964Ra4=", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@angular/http/-/http-4.2.1.tgz", + "integrity": "sha1-2ebjSMh1PTGMdh1/S02fBf7EDvk=", "dev": true }, "@angular/platform-browser": { - "version": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-4.1.3.tgz", - "integrity": "sha1-T6HbURndF4sxXdrlsym+4akypb0=", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-4.2.1.tgz", + "integrity": "sha1-HwHU0x+jkCEYJiZs9uOj9imGDx0=", "dev": true }, "@angular/platform-browser-dynamic": { - "version": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.1.3.tgz", - "integrity": "sha1-PBP9z1kdSH9u/cHUaRPygMbYwuw=", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.2.1.tgz", + "integrity": "sha1-CDLSG6NIHZH0BWxqlUKfn892Ukw=", "dev": true }, "@angular/router": { - "version": "https://registry.npmjs.org/@angular/router/-/router-4.1.3.tgz", - "integrity": "sha1-3a/UaufMyLH3SQT/tF85TkRiUhY=", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-4.2.1.tgz", + "integrity": "sha1-tkoNh+XNAi89PFLsi3urkLLj8Zg=", "dev": true }, "@angularclass/hmr": { diff --git a/package.json b/package.json index d066149..900ff39 100644 --- a/package.json +++ b/package.json @@ -31,15 +31,15 @@ "winston": "^2.3.1" }, "devDependencies": { - "@angular/animations": "^4.2.1", - "@angular/common": "4.1.3", - "@angular/compiler": "4.1.3", - "@angular/core": "4.1.3", - "@angular/forms": "4.1.3", - "@angular/http": "4.1.3", - "@angular/platform-browser": "4.1.3", - "@angular/platform-browser-dynamic": "4.1.3", - "@angular/router": "4.1.3", + "@angular/animations": "^4.2.0", + "@angular/common": "^4.2.1", + "@angular/compiler": "^4.2.1", + "@angular/core": "^4.2.1", + "@angular/forms": "^4.2.1", + "@angular/http": "^4.2.1", + "@angular/platform-browser": "^4.2.1", + "@angular/platform-browser-dynamic": "^4.2.1", + "@angular/router": "^4.2.1", "@angularclass/hmr": "^1.2.2", "@angularclass/hmr-loader": "^3.0.2", "@ng-bootstrap/ng-bootstrap": "^1.0.0-alpha.22", From eed4e085b51461d45e10554c5e0e2afc0ddfacc8 Mon Sep 17 00:00:00 2001 From: turt2live Date: Sat, 10 Jun 2017 14:54:38 -0600 Subject: [PATCH 5/7] Use relative paths for all integration icons --- config/integrations/dimension_demo_bot.yaml | 2 +- config/integrations/giphy.yaml | 2 +- config/integrations/google.yaml | 2 +- config/integrations/guggy.yaml | 2 +- config/integrations/imgur.yaml | 2 +- config/integrations/irc-bridge.yaml | 4 ++-- config/integrations/pollbot.yaml | 2 +- config/integrations/rssbot.yaml | 2 +- config/integrations/wikipedia.yaml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/config/integrations/dimension_demo_bot.yaml b/config/integrations/dimension_demo_bot.yaml index 4dd7612..25d7e01 100644 --- a/config/integrations/dimension_demo_bot.yaml +++ b/config/integrations/dimension_demo_bot.yaml @@ -4,7 +4,7 @@ enabled: false userId: "@dimension:t2bot.io" name: "Demo Bot" about: "A bot that has no functionality. This is just a demonstration on the config." -avatar: "/img/avatars/demobot.png" +avatar: "img/avatars/demobot.png" hosted: homeserverUrl: "https://t2bot.io" accessToken: "your_matrix_access_token_here" diff --git a/config/integrations/giphy.yaml b/config/integrations/giphy.yaml index 379474c..f2a78b4 100644 --- a/config/integrations/giphy.yaml +++ b/config/integrations/giphy.yaml @@ -4,7 +4,7 @@ enabled: true userId: "@neb_giphy:matrix.org" name: "Giphy" about: "Use `!giphy query` to find an animated GIF on demand" -avatar: "/img/avatars/giphy.png" +avatar: "img/avatars/giphy.png" upstream: type: "vector" id: "giphy" \ No newline at end of file diff --git a/config/integrations/google.yaml b/config/integrations/google.yaml index 2ca8a09..95aa235 100644 --- a/config/integrations/google.yaml +++ b/config/integrations/google.yaml @@ -4,7 +4,7 @@ enabled: true userId: "@_neb_google:matrix.org" name: "Google" about: "Use `!google image query` to find an image from Google" -avatar: "/img/avatars/google.png" +avatar: "img/avatars/google.png" upstream: type: "vector" id: "google" \ No newline at end of file diff --git a/config/integrations/guggy.yaml b/config/integrations/guggy.yaml index 688c08d..b6c347f 100644 --- a/config/integrations/guggy.yaml +++ b/config/integrations/guggy.yaml @@ -4,7 +4,7 @@ enabled: true userId: "@_neb_guggy:matrix.org" name: "Guggy" about: "Use `!guggy sentence` to create an animated GIF from a sentence" -avatar: "/img/avatars/guggy.png" +avatar: "img/avatars/guggy.png" upstream: type: "vector" id: "guggy" \ No newline at end of file diff --git a/config/integrations/imgur.yaml b/config/integrations/imgur.yaml index 9bfd39e..4035348 100644 --- a/config/integrations/imgur.yaml +++ b/config/integrations/imgur.yaml @@ -4,7 +4,7 @@ enabled: true userId: "@_neb_imgur:matrix.org" name: "Imgur" about: "Use `!imgur query` to find an image from Imgur" -avatar: "/img/avatars/imgur.png" +avatar: "img/avatars/imgur.png" upstream: type: "vector" id: "imgur" \ No newline at end of file diff --git a/config/integrations/irc-bridge.yaml b/config/integrations/irc-bridge.yaml index 6350fe6..33d0b94 100644 --- a/config/integrations/irc-bridge.yaml +++ b/config/integrations/irc-bridge.yaml @@ -2,8 +2,8 @@ type: "bridge" integrationType: "irc" enabled: true name: "IRC" -about: "Bridges an IRC channel to the room" -avatar: "/img/avatars/irc.png" +about: "Bridges IRC channels to the room" +avatar: "img/avatars/irc.png" requirements: joinRule: 'public' upstream: diff --git a/config/integrations/pollbot.yaml b/config/integrations/pollbot.yaml index b601eeb..25a042f 100644 --- a/config/integrations/pollbot.yaml +++ b/config/integrations/pollbot.yaml @@ -4,7 +4,7 @@ enabled: false userId: "@pollbot:t2bot.io" name: "Poll Bot" about: "A bot to poll users in a room. Use `!pollhelp` for more information" -avatar: "/img/avatars/pollbot.png" +avatar: "img/avatars/pollbot.png" hosted: homeserverUrl: "https://t2bot.io" accessToken: "your_matrix_access_token_here" diff --git a/config/integrations/rssbot.yaml b/config/integrations/rssbot.yaml index b0ae410..332a77f 100644 --- a/config/integrations/rssbot.yaml +++ b/config/integrations/rssbot.yaml @@ -3,6 +3,6 @@ integrationType: "rss" enabled: true name: "RSS Bot" about: "Tracks any Atom/RSS feed and sends new items into this room" -avatar: "/img/avatars/rssbot.png" +avatar: "img/avatars/rssbot.png" upstream: type: "vector" \ No newline at end of file diff --git a/config/integrations/wikipedia.yaml b/config/integrations/wikipedia.yaml index de893dc..4090ec6 100644 --- a/config/integrations/wikipedia.yaml +++ b/config/integrations/wikipedia.yaml @@ -4,7 +4,7 @@ enabled: true userId: "@_neb_wikipedia:matrix.org" name: "Wikipedia" about: "Use `!wikipedia query` to find something from Wikipedia" -avatar: "/img/avatars/wikipedia.png" +avatar: "img/avatars/wikipedia.png" upstream: type: "vector" id: "wikipedia" \ No newline at end of file From b0ab80e33e2fb61a790f741c0359b231dd2872d5 Mon Sep 17 00:00:00 2001 From: turt2live Date: Sat, 10 Jun 2017 14:54:50 -0600 Subject: [PATCH 6/7] Force bootstrap to alpha 26 --- package-lock.json | 3 ++- package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 73b6e54..32ab35d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,7 +68,8 @@ "dev": true }, "@ng-bootstrap/ng-bootstrap": { - "version": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-1.0.0-alpha.26.tgz", + "version": "1.0.0-alpha.26", + "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-1.0.0-alpha.26.tgz", "integrity": "sha1-89nha1aC7CDts/E5fT1FpXbz9qQ=", "dev": true }, diff --git a/package.json b/package.json index 900ff39..0f2c2bc 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "@angular/router": "^4.2.1", "@angularclass/hmr": "^1.2.2", "@angularclass/hmr-loader": "^3.0.2", - "@ng-bootstrap/ng-bootstrap": "^1.0.0-alpha.22", + "@ng-bootstrap/ng-bootstrap": "^1.0.0-alpha.26", "@types/node": "^7.0.29", "angular2-modal": "^2.0.3", "angular2-template-loader": "^0.6.2", From a007e553a5b5ed61bd4d5cf6d0aea44521424e8c Mon Sep 17 00:00:00 2001 From: turt2live Date: Sat, 10 Jun 2017 14:56:29 -0600 Subject: [PATCH 7/7] Change how always-on bridges are represented UI-wise --- web/app/integration/integration.component.html | 5 +---- web/app/riot/riot.component.ts | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/web/app/integration/integration.component.html b/web/app/integration/integration.component.html index 0c6cbb2..3f9d756 100644 --- a/web/app/integration/integration.component.html +++ b/web/app/integration/integration.component.html @@ -6,11 +6,8 @@
-
- -
- +
diff --git a/web/app/riot/riot.component.ts b/web/app/riot/riot.component.ts index da44e67..06b68a8 100644 --- a/web/app/riot/riot.component.ts +++ b/web/app/riot/riot.component.ts @@ -68,7 +68,7 @@ export class RiotComponent { integration.isBroken = false; }, error => { console.error(error); - integration.bridgeError = error.message; + integration.bridgeError = error.message || error; integration.isEnabled = false; integration.isBroken = false; });