mirror of
https://github.com/turt2live/matrix-dimension.git
synced 2024-09-01 14:42:14 +00:00
Merge branch 'travis/irc'
This commit is contained in:
commit
03e6350d17
@ -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"
|
||||
|
@ -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"
|
@ -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"
|
@ -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"
|
@ -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"
|
10
config/integrations/irc-bridge.yaml
Normal file
10
config/integrations/irc-bridge.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
type: "bridge"
|
||||
integrationType: "irc"
|
||||
enabled: true
|
||||
name: "IRC"
|
||||
about: "Bridges IRC channels to the room"
|
||||
avatar: "img/avatars/irc.png"
|
||||
requirements:
|
||||
joinRule: 'public'
|
||||
upstream:
|
||||
type: "vector"
|
@ -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"
|
||||
|
@ -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"
|
@ -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"
|
@ -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?
|
||||
## 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": []
|
||||
}
|
||||
}
|
||||
```
|
47
docs/integrations/irc_bridge.md
Normal file
47
docs/integrations/irc_bridge.md
Normal file
@ -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.
|
@ -179,3 +179,145 @@ 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": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
217
package-lock.json
generated
217
package-lock.json
generated
@ -4,48 +4,57 @@
|
||||
"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": {
|
||||
"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": {
|
||||
@ -59,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
|
||||
},
|
||||
@ -68,8 +78,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 +544,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 +736,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 +939,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 +977,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 +1109,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 +1926,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 +2156,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 +2247,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 +2575,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 +2824,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 +3855,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 +3944,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 +4086,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 +4197,18 @@
|
||||
"dev": true
|
||||
},
|
||||
"tslint": {
|
||||
"version": "https://registry.npmjs.org/tslint/-/tslint-5.3.2.tgz",
|
||||
"integrity": "sha1-5WRZ+wlacwfxA7hAUhdPXju+9u0=",
|
||||
"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 +4227,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 +4250,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 +4367,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 +4546,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 +4603,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
|
||||
}
|
||||
}
|
||||
|
38
package.json
38
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,19 +31,19 @@
|
||||
"winston": "^2.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/animations": "^4.1.3",
|
||||
"@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",
|
||||
"@types/node": "^7.0.18",
|
||||
"@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",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
30
src/integration/generic_types/Bridge.js
Normal file
30
src/integration/generic_types/Bridge.js
Normal file
@ -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;
|
@ -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 = {
|
||||
|
44
src/integration/impl/irc/IRCBridge.js
Normal file
44
src/integration/impl/irc/IRCBridge.js
Normal file
@ -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;
|
12
src/integration/impl/irc/IRCFactory.js
Normal file
12
src/integration/impl/irc/IRCFactory.js
Normal file
@ -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");
|
||||
};
|
29
src/integration/impl/irc/StubbedIrcBackbone.js
Normal file
29
src/integration/impl/irc/StubbedIrcBackbone.js
Normal file
@ -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;
|
69
src/integration/impl/irc/VectorIrcBackbone.js
Normal file
69
src/integration/impl/irc/VectorIrcBackbone.js
Normal file
@ -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;
|
@ -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;
|
||||
|
||||
|
@ -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 {
|
||||
|
10
web/app/configs/irc/irc-config.component.html
Normal file
10
web/app/configs/irc/irc-config.component.html
Normal file
@ -0,0 +1,10 @@
|
||||
<div class="config-wrapper">
|
||||
<img src="/img/close.svg" (click)="dialog.close()" class="close-icon">
|
||||
<div class="config-header">
|
||||
<img src="/img/avatars/irc.png">
|
||||
<h4>Configure IRC Bridge</h4>
|
||||
</div>
|
||||
<div class="config-content">
|
||||
<pre>{{ integration | json }}</pre>
|
||||
</div>
|
||||
</div>
|
1
web/app/configs/irc/irc-config.component.scss
Normal file
1
web/app/configs/irc/irc-config.component.scss
Normal file
@ -0,0 +1 @@
|
||||
// component styles are encapsulated and only applied to their components
|
25
web/app/configs/irc/irc-config.component.ts
Normal file
25
web/app/configs/irc/irc-config.component.ts
Normal file
@ -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<ConfigModalContext> {
|
||||
|
||||
public integration: IRCIntegration;
|
||||
|
||||
private roomId: string;
|
||||
private scalarToken: string;
|
||||
|
||||
constructor(public dialog: DialogRef<ConfigModalContext>) {// ,
|
||||
// private toaster: ToasterService,
|
||||
// private api: ApiService) {
|
||||
this.integration = <IRCIntegration>dialog.context.integration;
|
||||
this.roomId = dialog.context.roomId;
|
||||
this.scalarToken = dialog.context.scalarToken;
|
||||
}
|
||||
}
|
@ -3,9 +3,12 @@
|
||||
<div class="title">
|
||||
<b>{{ integration.name }}</b>
|
||||
<div style="display: flex;">
|
||||
<div class="switch">
|
||||
<div class="switch" *ngIf="integration.type !== 'bridge'">
|
||||
<ui-switch [checked]="integration.isEnabled" size="small" [disabled]="integration.isBroken" (change)="update()"></ui-switch>
|
||||
</div>
|
||||
<div class="switch" *ngIf="integration.type == 'bridge' && !integration.isEnabled">
|
||||
<i class="fa fa-warning text-warning" ngbTooltip="{{ integration.bridgeError }}"></i>
|
||||
</div>
|
||||
<div class="toolbar">
|
||||
<i class="fa fa-question-circle text-info" ngbTooltip="{{integration.about}}" *ngIf="integration.about"></i>
|
||||
<i class="fa fa-cog text-info config-icon" (click)="configureIntegration()" *ngIf="integration.isEnabled && integration.hasConfig"></i>
|
||||
|
@ -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 || error;
|
||||
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;
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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[]};
|
||||
}
|
@ -25,3 +25,9 @@ export interface MembershipStateResponse extends ScalarUserResponse {
|
||||
displayname: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface JoinRuleStateResponse extends ScalarRoomResponse {
|
||||
response: {
|
||||
join_rule: string;
|
||||
};
|
||||
}
|
@ -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<JoinRuleStateResponse> {
|
||||
return this.callAction("join_rules_state", {
|
||||
room_id: roomId
|
||||
});
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this.callAction("close_scalar", {});
|
||||
}
|
||||
|
BIN
web/public/img/avatars/irc.png
Normal file
BIN
web/public/img/avatars/irc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.6 KiB |
@ -122,7 +122,7 @@ module.exports = function () {
|
||||
config.devServer = {
|
||||
contentBase: './web/public',
|
||||
historyApiFallback: true,
|
||||
quiet: true,
|
||||
quiet: false,
|
||||
stats: 'minimal',
|
||||
proxy: {
|
||||
'/api': {
|
||||
|
Loading…
Reference in New Issue
Block a user