mirror of
https://github.com/turt2live/matrix-dimension.git
synced 2024-08-31 06:02:09 +00:00
Add the frontend for Gitter bridging
Fixes https://github.com/turt2live/matrix-dimension/issues/4 Fixes https://github.com/turt2live/matrix-dimension/issues/7
This commit is contained in:
parent
2e844a707f
commit
edbbd3b8c0
61
src/api/dimension/DimensionGitterService.ts
Normal file
61
src/api/dimension/DimensionGitterService.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import { DELETE, GET, Path, PathParam, POST, QueryParam } from "typescript-rest";
|
||||
import { ScalarService } from "../scalar/ScalarService";
|
||||
import { ApiError } from "../ApiError";
|
||||
import { BridgedRoom, GitterBridge } from "../../bridges/GitterBridge";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
|
||||
interface BridgeRoomRequest {
|
||||
gitterRoomName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* API for interacting with the Gitter bridge
|
||||
*/
|
||||
@Path("/api/v1/dimension/gitter")
|
||||
export class DimensionGitterService {
|
||||
|
||||
@GET
|
||||
@Path("room/:roomId/link")
|
||||
public async getLink(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string): Promise<BridgedRoom> {
|
||||
const userId = await ScalarService.getTokenOwner(scalarToken);
|
||||
|
||||
try {
|
||||
const gitter = new GitterBridge(userId);
|
||||
return gitter.getLink(roomId);
|
||||
} catch (e) {
|
||||
LogService.error("DimensionGitterService", e);
|
||||
throw new ApiError(400, "Error getting bridge info");
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("room/:roomId/link")
|
||||
public async bridgeRoom(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string, request: BridgeRoomRequest): Promise<BridgedRoom> {
|
||||
const userId = await ScalarService.getTokenOwner(scalarToken);
|
||||
|
||||
try {
|
||||
const gitter = new GitterBridge(userId);
|
||||
await gitter.requestLink(roomId, request.gitterRoomName);
|
||||
return gitter.getLink(roomId);
|
||||
} catch (e) {
|
||||
LogService.error("DimensionGitterService", e);
|
||||
throw new ApiError(400, "Error bridging room");
|
||||
}
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("room/:roomId/link")
|
||||
public async unbridgeRoom(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string): Promise<any> {
|
||||
const userId = await ScalarService.getTokenOwner(scalarToken);
|
||||
|
||||
try {
|
||||
const gitter = new GitterBridge(userId);
|
||||
const link = await gitter.getLink(roomId);
|
||||
await gitter.removeLink(roomId, link.gitterRoomName);
|
||||
return {}; // 200 OK
|
||||
} catch (e) {
|
||||
LogService.error("DimensionGitterService", e);
|
||||
throw new ApiError(400, "Error unbridging room");
|
||||
}
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ export class GitterBridge {
|
||||
const bridge = await this.getDefaultBridge();
|
||||
|
||||
if (bridge.upstreamId) {
|
||||
const info = await this.doUpstreamRequest<ModularGitterResponse<GetBotUserIdResponse>>(bridge, "POST", "/bridges/gitter/_matrix/provision/getbotid");
|
||||
const info = await this.doUpstreamRequest<ModularGitterResponse<GetBotUserIdResponse>>(bridge, "POST", "/bridges/gitter/_matrix/provision/getbotid/", null, {});
|
||||
if (!info || !info.replies || !info.replies[0] || !info.replies[0].response) {
|
||||
throw new Error("Invalid response from Modular for Gitter bot user ID");
|
||||
}
|
||||
@ -59,7 +59,7 @@ export class GitterBridge {
|
||||
try {
|
||||
if (bridge.upstreamId) {
|
||||
delete requestBody["user_id"];
|
||||
const link = await this.doUpstreamRequest<ModularGitterResponse<BridgedRoomResponse>>(bridge, "POST", "/bridge/gitter/_matrix/provision/getlink", null, requestBody);
|
||||
const link = await this.doUpstreamRequest<ModularGitterResponse<BridgedRoomResponse>>(bridge, "POST", "/bridges/gitter/_matrix/provision/getlink", null, requestBody);
|
||||
if (!link || !link.replies || !link.replies[0] || !link.replies[0].response) {
|
||||
// noinspection ExceptionCaughtLocallyJS
|
||||
throw new Error("Invalid response from Modular for Gitter list links in " + roomId);
|
||||
@ -76,6 +76,7 @@ export class GitterBridge {
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.status === 404) return null;
|
||||
LogService.error("GitterBridge", e);
|
||||
throw e;
|
||||
}
|
||||
@ -147,9 +148,10 @@ export class GitterBridge {
|
||||
LogService.error("GitterBridge", "There is no response for " + url);
|
||||
reject(new Error("No response provided - is the service online?"));
|
||||
} else if (res.statusCode !== 200) {
|
||||
if (typeof(res.body) === "string") res.body = JSON.parse(res.body);
|
||||
LogService.error("GitterBridge", "Got status code " + res.statusCode + " when calling " + url);
|
||||
LogService.error("GitterBridge", res.body);
|
||||
reject(new Error("Request failed"));
|
||||
reject({body: res.body, status: res.statusCode});
|
||||
} else {
|
||||
if (typeof(res.body) === "string") res.body = JSON.parse(res.body);
|
||||
resolve(res.body);
|
||||
@ -179,9 +181,10 @@ export class GitterBridge {
|
||||
LogService.error("GitterBridge", "There is no response for " + url);
|
||||
reject(new Error("No response provided - is the service online?"));
|
||||
} else if (res.statusCode !== 200) {
|
||||
if (typeof(res.body) === "string") res.body = JSON.parse(res.body);
|
||||
LogService.error("GitterBridge", "Got status code " + res.statusCode + " when calling " + url);
|
||||
LogService.error("GitterBridge", res.body);
|
||||
reject(new Error("Request failed"));
|
||||
reject({body: res.body, status: res.statusCode});
|
||||
} else {
|
||||
if (typeof(res.body) === "string") res.body = JSON.parse(res.body);
|
||||
resolve(res.body);
|
||||
|
@ -5,12 +5,14 @@ import { PortalInfo, PuppetInfo } from "../bridges/TelegramBridge";
|
||||
import { WebhookConfiguration } from "../bridges/models/webhooks";
|
||||
import { BridgedRoom } from "../bridges/GitterBridge";
|
||||
|
||||
const PRIVATE_ACCESS_SUPPORTED_BRIDGES = ["webhooks", "gitter"];
|
||||
|
||||
export class Bridge extends Integration {
|
||||
constructor(bridge: BridgeRecord, public config: any) {
|
||||
super(bridge);
|
||||
this.category = "bridge";
|
||||
|
||||
if (bridge.type === "webhooks") this.requirements = [];
|
||||
if (PRIVATE_ACCESS_SUPPORTED_BRIDGES.indexOf(bridge.type) !== -1) this.requirements = [];
|
||||
else this.requirements = [{
|
||||
condition: "publicRoom",
|
||||
expectedValue: true,
|
||||
|
@ -93,6 +93,8 @@ import { WebhooksBridgeConfigComponent } from "./configs/bridge/webhooks/webhook
|
||||
import { AdminGitterBridgeComponent } from "./admin/bridges/gitter/gitter.component";
|
||||
import { AdminGitterBridgeManageSelfhostedComponent } from "./admin/bridges/gitter/manage-selfhosted/manage-selfhosted.component";
|
||||
import { AdminGitterApiService } from "./shared/services/admin/admin-gitter-api.service";
|
||||
import { GitterBridgeConfigComponent } from "./configs/bridge/gitter/gitter.bridge.component";
|
||||
import { GitterApiService } from "./shared/services/integrations/gitter-api.service";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -170,6 +172,7 @@ import { AdminGitterApiService } from "./shared/services/admin/admin-gitter-api.
|
||||
WebhooksBridgeConfigComponent,
|
||||
AdminGitterBridgeComponent,
|
||||
AdminGitterBridgeManageSelfhostedComponent,
|
||||
GitterBridgeConfigComponent,
|
||||
|
||||
// Vendor
|
||||
],
|
||||
@ -194,6 +197,7 @@ import { AdminGitterApiService } from "./shared/services/admin/admin-gitter-api.
|
||||
AdminWebhooksApiService,
|
||||
WebhooksApiService,
|
||||
AdminGitterApiService,
|
||||
GitterApiService,
|
||||
{provide: Window, useValue: window},
|
||||
|
||||
// Vendor
|
||||
|
@ -32,6 +32,7 @@ import { TelegramBridgeConfigComponent } from "./configs/bridge/telegram/telegra
|
||||
import { AdminWebhooksBridgeComponent } from "./admin/bridges/webhooks/webhooks.component";
|
||||
import { WebhooksBridgeConfigComponent } from "./configs/bridge/webhooks/webhooks.bridge.component";
|
||||
import { AdminGitterBridgeComponent } from "./admin/bridges/gitter/gitter.component";
|
||||
import { GitterBridgeConfigComponent } from "./configs/bridge/gitter/gitter.bridge.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{path: "", component: HomeComponent},
|
||||
@ -194,6 +195,11 @@ const routes: Routes = [
|
||||
component: WebhooksBridgeConfigComponent,
|
||||
data: {breadcrumb: "Webhook Bridge Configuration", name: "Webhook Bridge Configuration"},
|
||||
},
|
||||
{
|
||||
path: "gitter",
|
||||
component: GitterBridgeConfigComponent,
|
||||
data: {breadcrumb: "Gitter Bridge Configuration", name: "Gitter Bridge Configuration"},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
27
web/app/configs/bridge/gitter/gitter.bridge.component.html
Normal file
27
web/app/configs/bridge/gitter/gitter.bridge.component.html
Normal file
@ -0,0 +1,27 @@
|
||||
<my-bridge-config [bridgeComponent]="this">
|
||||
<ng-template #bridgeParamsTemplate>
|
||||
<my-ibox [isCollapsible]="false">
|
||||
<h5 class="my-ibox-title">
|
||||
Bridge to Gitter
|
||||
</h5>
|
||||
<div class="my-ibox-content">
|
||||
<div *ngIf="isBridged">
|
||||
This room is bridged to "{{ bridge.config.link.gitterRoomName }}" on Gitter.
|
||||
<button type="button" class="btn btn-sm btn-danger" [disabled]="isBusy" (click)="unbridgeRoom()">
|
||||
Unbridge
|
||||
</button>
|
||||
</div>
|
||||
<div *ngIf="!isBridged">
|
||||
<label class="label-block">
|
||||
Gitter Room
|
||||
<input title="room name" type="text" class="form-control form-control-sm col-md-3"
|
||||
[(ngModel)]="gitterRoomName" [disabled]="isBusy" placeholder="my-org/room" />
|
||||
</label>
|
||||
<button type="button" class="btn btn-sm btn-primary" [disabled]="isBusy" (click)="bridgeRoom()">
|
||||
Bridge
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</my-ibox>
|
||||
</ng-template>
|
||||
</my-bridge-config>
|
@ -0,0 +1,4 @@
|
||||
.actions-col {
|
||||
width: 120px;
|
||||
text-align: center;
|
||||
}
|
66
web/app/configs/bridge/gitter/gitter.bridge.component.ts
Normal file
66
web/app/configs/bridge/gitter/gitter.bridge.component.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { BridgeComponent } from "../bridge.component";
|
||||
import { FE_GitterLink } from "../../../shared/models/gitter";
|
||||
import { GitterApiService } from "../../../shared/services/integrations/gitter-api.service";
|
||||
import { ScalarClientApiService } from "../../../shared/services/scalar/scalar-client-api.service";
|
||||
|
||||
interface GitterConfig {
|
||||
botUserId: string;
|
||||
link: FE_GitterLink;
|
||||
}
|
||||
|
||||
@Component({
|
||||
templateUrl: "gitter.bridge.component.html",
|
||||
styleUrls: ["gitter.bridge.component.scss"],
|
||||
})
|
||||
export class GitterBridgeConfigComponent extends BridgeComponent<GitterConfig> {
|
||||
|
||||
public gitterRoomName: string;
|
||||
public isBusy: boolean;
|
||||
|
||||
constructor(private gitter: GitterApiService, private scalar: ScalarClientApiService) {
|
||||
super("gitter");
|
||||
}
|
||||
|
||||
public get isBridged(): boolean {
|
||||
return this.bridge.config.link && !!this.bridge.config.link.gitterRoomName;
|
||||
}
|
||||
|
||||
public async bridgeRoom(): Promise<any> {
|
||||
this.isBusy = true;
|
||||
|
||||
try {
|
||||
await this.scalar.inviteUser(this.roomId, this.bridge.config.botUserId);
|
||||
} catch (e) {
|
||||
if (!e.response || !e.response.error || !e.response.error._error ||
|
||||
e.response.error._error.message.indexOf("already in the room") === -1) {
|
||||
this.isBusy = false;
|
||||
this.toaster.pop("error", "Error inviting bridge");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.gitter.bridgeRoom(this.roomId, this.gitterRoomName).then(link => {
|
||||
this.bridge.config.link = link;
|
||||
this.isBusy = false;
|
||||
this.toaster.pop("success", "Bridge requested");
|
||||
}).catch(error => {
|
||||
this.isBusy = false;
|
||||
console.error(error);
|
||||
this.toaster.pop("error", "Error requesting bridge");
|
||||
});
|
||||
}
|
||||
|
||||
public unbridgeRoom(): void {
|
||||
this.isBusy = true;
|
||||
this.gitter.unbridgeRoom(this.roomId).then(() => {
|
||||
this.bridge.config.link = null;
|
||||
this.isBusy = false;
|
||||
this.toaster.pop("success", "Bridge removed");
|
||||
}).catch(error => {
|
||||
this.isBusy = false;
|
||||
console.error(error);
|
||||
this.toaster.pop("error", "Error removing bridge");
|
||||
});
|
||||
}
|
||||
}
|
@ -4,3 +4,8 @@ export interface FE_GitterBridge {
|
||||
provisionUrl?: string;
|
||||
isEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface FE_GitterLink {
|
||||
roomId: string;
|
||||
gitterRoomName: string;
|
||||
}
|
@ -19,6 +19,7 @@ export class IntegrationsRegistry {
|
||||
"irc": {},
|
||||
"telegram": {},
|
||||
"webhooks": {},
|
||||
"gitter": {},
|
||||
},
|
||||
"widget": {
|
||||
"custom": {
|
||||
|
26
web/app/shared/services/integrations/gitter-api.service.ts
Normal file
26
web/app/shared/services/integrations/gitter-api.service.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Http } from "@angular/http";
|
||||
import { AuthedApi } from "../authed-api";
|
||||
import { FE_GitterLink } from "../../models/gitter";
|
||||
|
||||
@Injectable()
|
||||
export class GitterApiService extends AuthedApi {
|
||||
constructor(http: Http) {
|
||||
super(http);
|
||||
}
|
||||
|
||||
public bridgeRoom(roomId: string, gitterRoomName: string): Promise<FE_GitterLink> {
|
||||
return this.authedPost("/api/v1/dimension/gitter/room/" + roomId + "/link", {gitterRoomName})
|
||||
.map(r => r.json()).toPromise();
|
||||
}
|
||||
|
||||
public unbridgeRoom(roomId: string): Promise<any> {
|
||||
return this.authedDelete("/api/v1/dimension/gitter/room/" + roomId + "/link")
|
||||
.map(r => r.json()).toPromise();
|
||||
}
|
||||
|
||||
public getLink(roomId: string): Promise<FE_GitterLink> {
|
||||
return this.authedGet("/api/v1/dimension/gitter/room/" + roomId + "/link")
|
||||
.map(r => r.json()).toPromise();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user