Merge branch 'travis/irc'

This commit is contained in:
turt2live 2017-06-10 14:58:37 -06:00
commit 03e6350d17
34 changed files with 853 additions and 113 deletions

View file

@ -4,7 +4,7 @@ enabled: false
userId: "@dimension:t2bot.io" userId: "@dimension:t2bot.io"
name: "Demo Bot" name: "Demo Bot"
about: "A bot that has no functionality. This is just a demonstration on the config." 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: hosted:
homeserverUrl: "https://t2bot.io" homeserverUrl: "https://t2bot.io"
accessToken: "your_matrix_access_token_here" accessToken: "your_matrix_access_token_here"

View file

@ -4,7 +4,7 @@ enabled: true
userId: "@neb_giphy:matrix.org" userId: "@neb_giphy:matrix.org"
name: "Giphy" name: "Giphy"
about: "Use `!giphy query` to find an animated GIF on demand" about: "Use `!giphy query` to find an animated GIF on demand"
avatar: "/img/avatars/giphy.png" avatar: "img/avatars/giphy.png"
upstream: upstream:
type: "vector" type: "vector"
id: "giphy" id: "giphy"

View file

@ -4,7 +4,7 @@ enabled: true
userId: "@_neb_google:matrix.org" userId: "@_neb_google:matrix.org"
name: "Google" name: "Google"
about: "Use `!google image query` to find an image from Google" about: "Use `!google image query` to find an image from Google"
avatar: "/img/avatars/google.png" avatar: "img/avatars/google.png"
upstream: upstream:
type: "vector" type: "vector"
id: "google" id: "google"

View file

@ -4,7 +4,7 @@ enabled: true
userId: "@_neb_guggy:matrix.org" userId: "@_neb_guggy:matrix.org"
name: "Guggy" name: "Guggy"
about: "Use `!guggy sentence` to create an animated GIF from a sentence" about: "Use `!guggy sentence` to create an animated GIF from a sentence"
avatar: "/img/avatars/guggy.png" avatar: "img/avatars/guggy.png"
upstream: upstream:
type: "vector" type: "vector"
id: "guggy" id: "guggy"

View file

@ -4,7 +4,7 @@ enabled: true
userId: "@_neb_imgur:matrix.org" userId: "@_neb_imgur:matrix.org"
name: "Imgur" name: "Imgur"
about: "Use `!imgur query` to find an image from Imgur" about: "Use `!imgur query` to find an image from Imgur"
avatar: "/img/avatars/imgur.png" avatar: "img/avatars/imgur.png"
upstream: upstream:
type: "vector" type: "vector"
id: "imgur" id: "imgur"

View 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"

View file

@ -4,7 +4,7 @@ enabled: false
userId: "@pollbot:t2bot.io" userId: "@pollbot:t2bot.io"
name: "Poll Bot" name: "Poll Bot"
about: "A bot to poll users in a room. Use `!pollhelp` for more information" 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: hosted:
homeserverUrl: "https://t2bot.io" homeserverUrl: "https://t2bot.io"
accessToken: "your_matrix_access_token_here" accessToken: "your_matrix_access_token_here"

View file

@ -3,6 +3,6 @@ integrationType: "rss"
enabled: true enabled: true
name: "RSS Bot" name: "RSS Bot"
about: "Tracks any Atom/RSS feed and sends new items into this room" about: "Tracks any Atom/RSS feed and sends new items into this room"
avatar: "/img/avatars/rssbot.png" avatar: "img/avatars/rssbot.png"
upstream: upstream:
type: "vector" type: "vector"

View file

@ -4,7 +4,7 @@ enabled: true
userId: "@_neb_wikipedia:matrix.org" userId: "@_neb_wikipedia:matrix.org"
name: "Wikipedia" name: "Wikipedia"
about: "Use `!wikipedia query` to find something from Wikipedia" about: "Use `!wikipedia query` to find something from Wikipedia"
avatar: "/img/avatars/wikipedia.png" avatar: "img/avatars/wikipedia.png"
upstream: upstream:
type: "vector" type: "vector"
id: "wikipedia" id: "wikipedia"

View file

@ -1,5 +1,121 @@
# Dimension API # 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": []
}
}
```

View 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.

View file

@ -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": {}
}
]
}
``` ```

219
package-lock.json generated
View file

@ -4,48 +4,57 @@
"lockfileVersion": 1, "lockfileVersion": 1,
"dependencies": { "dependencies": {
"@angular/animations": { "@angular/animations": {
"version": "https://registry.npmjs.org/@angular/animations/-/animations-4.1.3.tgz", "version": "4.2.1",
"integrity": "sha1-bomh4Pv9bQ6Qvk8q4ZCqxn+DpBE=", "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-4.2.1.tgz",
"integrity": "sha1-TIeIGS8Ux06wKg/I3Dhxd6ux/yQ=",
"dev": true "dev": true
}, },
"@angular/common": { "@angular/common": {
"version": "https://registry.npmjs.org/@angular/common/-/common-4.1.3.tgz", "version": "4.2.1",
"integrity": "sha1-58R5HjITHPdMI5QowqZ9qrLu8Bc=", "resolved": "https://registry.npmjs.org/@angular/common/-/common-4.2.1.tgz",
"integrity": "sha1-yzNOO3H00+nzS3rZQsVI0/k238g=",
"dev": true "dev": true
}, },
"@angular/compiler": { "@angular/compiler": {
"version": "https://registry.npmjs.org/@angular/compiler/-/compiler-4.1.3.tgz", "version": "4.2.1",
"integrity": "sha1-0t0whTsM9KVHWLSjFGMsIx+clMM=", "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-4.2.1.tgz",
"integrity": "sha1-jXe0WOBLiHlH2FrczXVG5KmH1LA=",
"dev": true "dev": true
}, },
"@angular/core": { "@angular/core": {
"version": "https://registry.npmjs.org/@angular/core/-/core-4.1.3.tgz", "version": "4.2.1",
"integrity": "sha1-KFSY64arfQtvmC+Pn0h+9hABOzU=", "resolved": "https://registry.npmjs.org/@angular/core/-/core-4.2.1.tgz",
"integrity": "sha1-CGUwSz30MEaRm2RvAv3tqFoMRoU=",
"dev": true "dev": true
}, },
"@angular/forms": { "@angular/forms": {
"version": "https://registry.npmjs.org/@angular/forms/-/forms-4.1.3.tgz", "version": "4.2.1",
"integrity": "sha1-OAq0w6+ExdHXSMKn0EFRx9yOSYI=", "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-4.2.1.tgz",
"integrity": "sha1-rRcs/TBwdDsqeoFh0sdax/W2H2I=",
"dev": true "dev": true
}, },
"@angular/http": { "@angular/http": {
"version": "https://registry.npmjs.org/@angular/http/-/http-4.1.3.tgz", "version": "4.2.1",
"integrity": "sha1-650cMCoBcoFfmlczENm+C964Ra4=", "resolved": "https://registry.npmjs.org/@angular/http/-/http-4.2.1.tgz",
"integrity": "sha1-2ebjSMh1PTGMdh1/S02fBf7EDvk=",
"dev": true "dev": true
}, },
"@angular/platform-browser": { "@angular/platform-browser": {
"version": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-4.1.3.tgz", "version": "4.2.1",
"integrity": "sha1-T6HbURndF4sxXdrlsym+4akypb0=", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-4.2.1.tgz",
"integrity": "sha1-HwHU0x+jkCEYJiZs9uOj9imGDx0=",
"dev": true "dev": true
}, },
"@angular/platform-browser-dynamic": { "@angular/platform-browser-dynamic": {
"version": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.1.3.tgz", "version": "4.2.1",
"integrity": "sha1-PBP9z1kdSH9u/cHUaRPygMbYwuw=", "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.2.1.tgz",
"integrity": "sha1-CDLSG6NIHZH0BWxqlUKfn892Ukw=",
"dev": true "dev": true
}, },
"@angular/router": { "@angular/router": {
"version": "https://registry.npmjs.org/@angular/router/-/router-4.1.3.tgz", "version": "4.2.1",
"integrity": "sha1-3a/UaufMyLH3SQT/tF85TkRiUhY=", "resolved": "https://registry.npmjs.org/@angular/router/-/router-4.2.1.tgz",
"integrity": "sha1-tkoNh+XNAi89PFLsi3urkLLj8Zg=",
"dev": true "dev": true
}, },
"@angularclass/hmr": { "@angularclass/hmr": {
@ -59,7 +68,8 @@
"dev": true "dev": true
}, },
"@ng-bootstrap/ng-bootstrap": { "@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=", "integrity": "sha1-89nha1aC7CDts/E5fT1FpXbz9qQ=",
"dev": true "dev": true
}, },
@ -68,8 +78,9 @@
"integrity": "sha1-sC0QqwKOKSisWSoFGqpJgaGUHQM=" "integrity": "sha1-sC0QqwKOKSisWSoFGqpJgaGUHQM="
}, },
"@types/node": { "@types/node": {
"version": "https://registry.npmjs.org/@types/node/-/node-7.0.22.tgz", "version": "7.0.29",
"integrity": "sha1-RZP02Ci91hKSlHjqQMZ7T0A8olU=", "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.29.tgz",
"integrity": "sha512-+8JrLZny/uR+d/jLK9eaV63buRM7X/gNzQk57q76NS4KNKLSKOmxJYFIlwuP2zDvA7wqZj05POPhSd9Z1hYQpQ==",
"dev": true "dev": true
}, },
"abbrev": { "abbrev": {
@ -533,6 +544,11 @@
"integrity": "sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY=", "integrity": "sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY=",
"dev": true "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": { "co": {
"version": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "version": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
@ -720,17 +736,20 @@
"dev": true "dev": true
}, },
"css-loader": { "css-loader": {
"version": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.3.tgz", "version": "0.28.4",
"integrity": "sha1-n9XguMQFtt+Se6EQOIcBXTYGQM4=", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.4.tgz",
"integrity": "sha1-bPNXkZLONV6LONX0Ldeh8uyJjQ8=",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"json5": { "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=", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
"dev": true "dev": true
}, },
"loader-utils": { "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=", "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
"dev": true "dev": true
} }
@ -920,10 +939,6 @@
"version": "https://registry.npmjs.org/dotenv/-/dotenv-2.0.0.tgz", "version": "https://registry.npmjs.org/dotenv/-/dotenv-2.0.0.tgz",
"integrity": "sha1-vXWcNXqqcDZeAclrewvsCKbg2Uk=" "integrity": "sha1-vXWcNXqqcDZeAclrewvsCKbg2Uk="
}, },
"dottie": {
"version": "https://registry.npmjs.org/dottie/-/dottie-1.1.1.tgz",
"integrity": "sha1-RcKj9IvWUo7u0memmoSOqspvqmo="
},
"ecc-jsbn": { "ecc-jsbn": {
"version": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", "version": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
"integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
@ -962,6 +977,11 @@
"integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=",
"dev": true "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": { "errno": {
"version": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", "version": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz",
"integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=", "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=",
@ -1089,17 +1109,20 @@
"dev": true "dev": true
}, },
"file-loader": { "file-loader": {
"version": "https://registry.npmjs.org/file-loader/-/file-loader-0.11.1.tgz", "version": "0.11.2",
"integrity": "sha1-azKO4SNKcp5OR9Njdd1tNcDh24Q=", "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-0.11.2.tgz",
"integrity": "sha512-N+uhF3mswIFeziHQjGScJ/yHXYt3DiLBeC+9vWW+WjUBiClMSOlV1YrXQi+7KM2aA3Rn4Bybgv+uXFQbfkzpvg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"json5": { "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=", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
"dev": true "dev": true
}, },
"loader-utils": { "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=", "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
"dev": true "dev": true
} }
@ -1903,10 +1926,6 @@
"integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=",
"dev": true "dev": true
}, },
"generic-pool": {
"version": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.4.2.tgz",
"integrity": "sha1-iGvFvwvrfblugby7oHiBjeWmJoM="
},
"get-caller-file": { "get-caller-file": {
"version": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", "version": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
"integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=",
@ -2137,6 +2156,26 @@
"integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=",
"dev": true "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": { "ieee754": {
"version": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", "version": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
"integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=",
@ -2208,6 +2247,11 @@
"integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
"dev": true "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": { "is-buffer": {
"version": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", "version": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz",
"integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=",
@ -2531,8 +2575,9 @@
"dev": true "dev": true
}, },
"matrix-js-sdk": { "matrix-js-sdk": {
"version": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-0.7.8.tgz", "version": "0.7.10",
"integrity": "sha1-TUZSCMvzZCZzw2/l8ZFhHFkW8vk=" "resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-0.7.10.tgz",
"integrity": "sha1-VPo8xuOwqlkkMOmEnuVl2vfGQEk="
}, },
"media-typer": { "media-typer": {
"version": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "version": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@ -2779,11 +2824,6 @@
"integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=",
"dev": true "dev": true
}, },
"olm": {
"version": "https://matrix.org/packages/npm/olm/olm-2.2.1.tgz",
"integrity": "sha1-Xl21DQoUK3x6BlDZs9isw9N+aXs=",
"optional": true
},
"on-finished": { "on-finished": {
"version": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "version": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=" "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc="
@ -3815,12 +3855,39 @@
} }
}, },
"sequelize": { "sequelize": {
"version": "https://registry.npmjs.org/sequelize/-/sequelize-3.30.4.tgz", "version": "4.0.0",
"integrity": "sha1-vaLfHjGFSwmeQUmhEen8Clyh0aQ=", "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-4.0.0.tgz",
"integrity": "sha512-sKJHt36Leyzlhsy2g8b2Q5DQxM4F8aIVFOohjnIC6d6rtutWmLjhW/7H2ZEqG8n/3hbK1mfSBnKqZCO7+1sJYA==",
"dependencies": { "dependencies": {
"lodash": { "@types/node": {
"version": "https://registry.npmjs.org/lodash/-/lodash-4.12.0.tgz", "version": "6.0.78",
"integrity": "sha1-K9bcRqBA9Z5obJcu0h2T3FkFMlg=" "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=" "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
}, },
"shelljs": { "shelljs": {
"version": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.7.tgz", "version": "0.7.8",
"integrity": "sha1-svXHfvlxSPS09uImguELuoZnz/E=", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz",
"integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=",
"dev": true "dev": true
}, },
"shimmer": { "shimmer": {
@ -4018,17 +4086,20 @@
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
}, },
"style-loader": { "style-loader": {
"version": "https://registry.npmjs.org/style-loader/-/style-loader-0.18.1.tgz", "version": "0.18.2",
"integrity": "sha1-avyolTyEKDDl4tyEeWMJiAqX9+g=", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.18.2.tgz",
"integrity": "sha512-WPpJPZGUxWYHWIUMNNOYqql7zh85zGmr84FdTVWq52WTIkqlW9xSxD3QYWi/T31cqn9UNSsietVEgGn2aaSCzw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"json5": { "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=", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
"dev": true "dev": true
}, },
"loader-utils": { "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=", "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
"dev": true "dev": true
} }
@ -4126,9 +4197,18 @@
"dev": true "dev": true
}, },
"tslint": { "tslint": {
"version": "https://registry.npmjs.org/tslint/-/tslint-5.3.2.tgz", "version": "5.4.3",
"integrity": "sha1-5WRZ+wlacwfxA7hAUhdPXju+9u0=", "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.4.3.tgz",
"dev": true "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": { "tslint-loader": {
"version": "https://registry.npmjs.org/tslint-loader/-/tslint-loader-3.5.3.tgz", "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": { "tty-browserify": {
"version": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "version": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
"integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
@ -4175,8 +4250,9 @@
"integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=" "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA="
}, },
"typescript": { "typescript": {
"version": "https://registry.npmjs.org/typescript/-/typescript-2.3.3.tgz", "version": "2.3.4",
"integrity": "sha1-ljnzw7QBSOjKl/4IpR3RiRu2viI=", "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.3.4.tgz",
"integrity": "sha1-PTgyGCgjHkNPKHUUlZw3qCtin0I=",
"dev": true "dev": true
}, },
"uglify-js": { "uglify-js": {
@ -4291,10 +4367,6 @@
"integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=",
"dev": true "dev": true
}, },
"validator": {
"version": "https://registry.npmjs.org/validator/-/validator-5.7.0.tgz",
"integrity": "sha1-eoelgUa2laxIYHEUHAxJ1n2gXlw="
},
"vary": { "vary": {
"version": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz", "version": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz",
"integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc=" "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": { "wordwrap": {
"version": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", "version": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
"integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
@ -4535,8 +4603,9 @@
} }
}, },
"zone.js": { "zone.js": {
"version": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.11.tgz", "version": "0.8.12",
"integrity": "sha1-dCvvsX+8SaVxcSuMfYfljKJv2IY=", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.12.tgz",
"integrity": "sha1-hv9QU8mK7CkaC/S7rFAdaUoFz7s=",
"dev": true "dev": true
} }
} }

View file

@ -22,7 +22,7 @@
"express": "^4.15.2", "express": "^4.15.2",
"js-yaml": "^3.8.2", "js-yaml": "^3.8.2",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"matrix-js-sdk": "^0.7.8", "matrix-js-sdk": "^0.7.10",
"moment": "^2.18.1", "moment": "^2.18.1",
"random-string": "^0.2.0", "random-string": "^0.2.0",
"request": "^2.81.0", "request": "^2.81.0",
@ -31,19 +31,19 @@
"winston": "^2.3.1" "winston": "^2.3.1"
}, },
"devDependencies": { "devDependencies": {
"@angular/animations": "^4.1.3", "@angular/animations": "^4.2.0",
"@angular/common": "4.1.3", "@angular/common": "^4.2.1",
"@angular/compiler": "4.1.3", "@angular/compiler": "^4.2.1",
"@angular/core": "4.1.3", "@angular/core": "^4.2.1",
"@angular/forms": "4.1.3", "@angular/forms": "^4.2.1",
"@angular/http": "4.1.3", "@angular/http": "^4.2.1",
"@angular/platform-browser": "4.1.3", "@angular/platform-browser": "^4.2.1",
"@angular/platform-browser-dynamic": "4.1.3", "@angular/platform-browser-dynamic": "^4.2.1",
"@angular/router": "4.1.3", "@angular/router": "^4.2.1",
"@angularclass/hmr": "^1.2.2", "@angularclass/hmr": "^1.2.2",
"@angularclass/hmr-loader": "^3.0.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.18", "@types/node": "^7.0.29",
"angular2-modal": "^2.0.3", "angular2-modal": "^2.0.3",
"angular2-template-loader": "^0.6.2", "angular2-template-loader": "^0.6.2",
"angular2-toaster": "^4.0.0", "angular2-toaster": "^4.0.0",
@ -53,10 +53,10 @@
"codelyzer": "^3.0.1", "codelyzer": "^3.0.1",
"copy-webpack-plugin": "^4.0.1", "copy-webpack-plugin": "^4.0.1",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"css-loader": "^0.28.0", "css-loader": "^0.28.4",
"cssnano": "^3.10.0", "cssnano": "^3.10.0",
"extract-text-webpack-plugin": "^2.1.2", "extract-text-webpack-plugin": "^2.1.2",
"file-loader": "^0.11.1", "file-loader": "^0.11.2",
"html-loader": "^0.4.5", "html-loader": "^0.4.5",
"html-webpack-plugin": "^2.28.0", "html-webpack-plugin": "^2.28.0",
"jquery": "^3.2.1", "jquery": "^3.2.1",
@ -71,14 +71,14 @@
"rimraf": "^2.6.1", "rimraf": "^2.6.1",
"rxjs": "^5.2.0", "rxjs": "^5.2.0",
"sass-loader": "^6.0.3", "sass-loader": "^6.0.3",
"shelljs": "^0.7.0", "shelljs": "^0.7.8",
"style-loader": "^0.18.1", "style-loader": "^0.18.2",
"ts-helpers": "^1.1.2", "ts-helpers": "^1.1.2",
"tslint": "^5.2.0", "tslint": "^5.4.3",
"tslint-loader": "^3.4.3", "tslint-loader": "^3.4.3",
"typescript": "^2.2.2", "typescript": "^2.3.4",
"url-loader": "^0.5.8", "url-loader": "^0.5.8",
"webpack": "^2.3.2", "webpack": "^2.3.2",
"zone.js": "^0.8.5" "zone.js": "^0.8.12"
} }
} }

View file

@ -50,14 +50,15 @@ class DimensionApi {
var promises = []; var promises = [];
_.forEach(integrations, integration => { _.forEach(integrations, integration => {
promises.push(this._getIntegration(integration, roomId, scalarToken).then(builtIntegration => { promises.push(this._getIntegration(integration, roomId, scalarToken).then(builtIntegration => {
return builtIntegration.getUserId().then(userId => { return builtIntegration.getState().then(state => {
integration.userId = userId;
return builtIntegration.getState();
}).then(state => {
var keys = _.keys(state); var keys = _.keys(state);
for (var key of keys) { for (var key of keys) {
integration[key] = state[key]; integration[key] = state[key];
} }
return builtIntegration.getUserId();
}).then(userId => {
integration.userId = userId;
}); });
})); }));
}); });

View 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;

View file

@ -2,16 +2,21 @@ var log = require("../../util/LogService");
var StubbedFactory = require("./StubbedFactory"); var StubbedFactory = require("./StubbedFactory");
var SimpleBotFactory = require("./simple_bot/SimpleBotFactory"); var SimpleBotFactory = require("./simple_bot/SimpleBotFactory");
var RSSFactory = require("./rss/RSSFactory"); var RSSFactory = require("./rss/RSSFactory");
var IRCFactory = require("./irc/IRCFactory");
var mapping = { var mapping = {
"complex-bot": { "complex-bot": {
"rss": RSSFactory "rss": RSSFactory
},
"bridge": {
"irc": IRCFactory
} }
}; };
var defaultFactories = { var defaultFactories = {
"complex-bot": null, "complex-bot": null,
"bot": SimpleBotFactory "bot": SimpleBotFactory,
"bridge": null
}; };
module.exports = { module.exports = {

View 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;

View 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");
};

View 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;

View 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;

View file

@ -82,7 +82,7 @@ class VectorScalarClient {
} }
/** /**
* Gets information on * Gets information for an integration
* @param {string} type the type to lookup * @param {string} type the type to lookup
* @param {string} roomId the room ID to look in * @param {string} roomId the room ID to look in
* @param {string} scalarToken the scalar token * @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) { _do(method, endpoint, qs = null, body = null) {
var url = config.get("upstreams.vector") + endpoint; var url = config.get("upstreams.vector") + endpoint;

View file

@ -19,6 +19,7 @@ import { IntegrationService } from "./shared/integration.service";
import { BootstrapModalModule } from "angular2-modal/plugins/bootstrap"; import { BootstrapModalModule } from "angular2-modal/plugins/bootstrap";
import { ModalModule } from "angular2-modal"; import { ModalModule } from "angular2-modal";
import { RssConfigComponent } from "./configs/rss/rss-config.component"; import { RssConfigComponent } from "./configs/rss/rss-config.component";
import { IrcConfigComponent } from "./configs/irc/irc-config.component";
@NgModule({ @NgModule({
imports: [ imports: [
@ -40,6 +41,7 @@ import { RssConfigComponent } from "./configs/rss/rss-config.component";
IntegrationComponent, IntegrationComponent,
ScalarCloseComponent, ScalarCloseComponent,
RssConfigComponent, RssConfigComponent,
IrcConfigComponent,
// Vendor // Vendor
], ],
@ -53,6 +55,7 @@ import { RssConfigComponent } from "./configs/rss/rss-config.component";
bootstrap: [AppComponent], bootstrap: [AppComponent],
entryComponents: [ entryComponents: [
RssConfigComponent, RssConfigComponent,
IrcConfigComponent,
] ]
}) })
export class AppModule { export class AppModule {

View 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>

View file

@ -0,0 +1 @@
// component styles are encapsulated and only applied to their components

View 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;
}
}

View file

@ -3,9 +3,12 @@
<div class="title"> <div class="title">
<b>{{ integration.name }}</b> <b>{{ integration.name }}</b>
<div style="display: flex;"> <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> <ui-switch [checked]="integration.isEnabled" size="small" [disabled]="integration.isBroken" (change)="update()"></ui-switch>
</div> </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"> <div class="toolbar">
<i class="fa fa-question-circle text-info" ngbTooltip="{{integration.about}}" *ngIf="integration.about"></i> <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> <i class="fa fa-cog text-info config-icon" (click)="configureIntegration()" *ngIf="integration.isEnabled && integration.hasConfig"></i>

View file

@ -54,6 +54,26 @@ export class RiotComponent {
private updateIntegrationState(integration: Integration) { private updateIntegrationState(integration: Integration) {
integration.hasConfig = IntegrationService.hasConfig(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 => { return this.scalar.getMembershipState(this.roomId, integration.userId).then(payload => {
integration.isBroken = false; 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) { public updateIntegration(integration: Integration) {
let promise = null; let promise = null;

View file

@ -2,6 +2,7 @@ import { Injectable } from "@angular/core";
import { Integration } from "./models/integration"; import { Integration } from "./models/integration";
import { RssConfigComponent } from "../configs/rss/rss-config.component"; import { RssConfigComponent } from "../configs/rss/rss-config.component";
import { ContainerContent } from "angular2-modal"; import { ContainerContent } from "angular2-modal";
import { IrcConfigComponent } from "../configs/irc/irc-config.component";
@Injectable() @Injectable()
export class IntegrationService { export class IntegrationService {
@ -10,12 +11,18 @@ export class IntegrationService {
"bot": true, "bot": true,
"complex-bot": { "complex-bot": {
"rss": true "rss": true
},
"bridge": {
"irc": true
} }
}; };
private static components = { private static components = {
"complex-bot": { "complex-bot": {
"rss": RssConfigComponent "rss": RssConfigComponent
},
"bridge": {
"irc": IrcConfigComponent
} }
}; };

View file

@ -8,9 +8,16 @@ export interface Integration {
isEnabled: boolean; isEnabled: boolean;
isBroken: boolean; isBroken: boolean;
hasConfig: boolean; hasConfig: boolean;
requirements?: any; // nullable
bridgeError: string; // nullable
} }
export interface RSSIntegration extends Integration { export interface RSSIntegration extends Integration {
feeds: string[]; feeds: string[];
immutableFeeds: {url: string, ownerId: string}[]; immutableFeeds: {url: string, ownerId: string}[];
}
export interface IRCIntegration extends Integration {
availableNetworks: {name: string, id: string};
channels: {[networkId: string]: string[]};
} }

View file

@ -24,4 +24,10 @@ export interface MembershipStateResponse extends ScalarUserResponse {
avatar_url: string; avatar_url: string;
displayname: string; displayname: string;
}; };
}
export interface JoinRuleStateResponse extends ScalarRoomResponse {
response: {
join_rule: string;
};
} }

View file

@ -1,6 +1,6 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import * as randomString from "random-string"; import * as randomString from "random-string";
import { MembershipStateResponse, ScalarSuccessResponse } from "./models/scalar_responses"; import { MembershipStateResponse, ScalarSuccessResponse, JoinRuleStateResponse } from "./models/scalar_responses";
@Injectable() @Injectable()
export class ScalarService { 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 { public close(): void {
this.callAction("close_scalar", {}); this.callAction("close_scalar", {});
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View file

@ -122,7 +122,7 @@ module.exports = function () {
config.devServer = { config.devServer = {
contentBase: './web/public', contentBase: './web/public',
historyApiFallback: true, historyApiFallback: true,
quiet: true, quiet: false,
stats: 'minimal', stats: 'minimal',
proxy: { proxy: {
'/api': { '/api': {