Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement separate bot users per service #573

Merged
merged 54 commits into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
f2d5e49
Add service bots config
justinbot Nov 3, 2022
3399c2b
Add joined rooms manager and keep track of joined rooms
justinbot Nov 7, 2022
6dbc696
Add bot users manager and ensure registration and profiles
justinbot Nov 7, 2022
74ae91a
Improve joined rooms manager and set up already joined rooms
justinbot Nov 14, 2022
d1d1634
Handle invites with service bots
justinbot Nov 15, 2022
a31960b
Handle messages with service bots
justinbot Nov 15, 2022
f90201e
Use service bots for connections
justinbot Nov 17, 2022
50fb801
Use service bots in widget and provisioning APIs
justinbot Nov 17, 2022
569c4a4
Use service bots in setup connections
justinbot Nov 17, 2022
9b906c7
Use service bots for feed connections
justinbot Nov 17, 2022
306bb48
Handle admin rooms for service bots
justinbot Nov 18, 2022
907a158
Fix confused event type and service type in provisioning and widget APIs
justinbot Nov 28, 2022
fd2ff9d
Fix generic webhooks service name
justinbot Nov 29, 2022
a5fe181
Fix enabled services config
justinbot Nov 29, 2022
d71332e
Handle power level change
justinbot Nov 29, 2022
e5c22f2
Create widgets with service scope
justinbot Nov 29, 2022
41d10b6
Use service bots for gitlab repo connections
justinbot Nov 29, 2022
c600f08
Use service bots for gitlab issue connections
justinbot Nov 29, 2022
f3fb256
Use service bots for generic webhook connections
justinbot Nov 30, 2022
36b4d30
Use service bots for figma file connections
justinbot Dec 1, 2022
ef27a8e
Use service bots when verifying state events
justinbot Dec 1, 2022
cbfba73
Use service bots for github repo connections
justinbot Dec 1, 2022
865fdde
Use service bots for github discussion connections
justinbot Dec 1, 2022
9c84e1e
Use service bots for github discussion space connections
justinbot Dec 1, 2022
0ee691e
Use service bots for github project connections
justinbot Dec 1, 2022
ada3b0d
Use service bots for github issue connections
justinbot Dec 1, 2022
bc0dbc7
Use service bots for github user space connections
justinbot Dec 1, 2022
9b3e2b1
Use service bots for jira connections
justinbot Dec 1, 2022
e40ee98
Make sure ghost users are invited for gitlab issue comments
justinbot Dec 1, 2022
179ebc8
Configure one service per service bot
justinbot Dec 1, 2022
734b31c
Add changelog
justinbot Dec 1, 2022
c7880b0
Update tests
justinbot Dec 2, 2022
87a185d
Fix up following rebase
justinbot Dec 9, 2022
c94845d
Fix comment
justinbot Dec 9, 2022
312110c
Use getter for enabled services
justinbot Dec 21, 2022
3588ea4
Ensure homeserver can be reached before registering bots
justinbot Dec 21, 2022
fb20fa4
Add intent getter on bot user
justinbot Dec 21, 2022
99c5d06
Update config comment
justinbot Dec 21, 2022
9b49c10
Merge joined rooms manager with bot users manager
justinbot Dec 21, 2022
0b8555b
Remove unused localpart from bot user class
justinbot Dec 21, 2022
6bb6294
Refactor to pass in bot users manager
justinbot Dec 21, 2022
9108414
Improve priority sort function
justinbot Jan 3, 2023
7aba6c1
Fix priority sort
justinbot Jan 3, 2023
8cfb8a9
Add debug log when invites are rejected
justinbot Jan 3, 2023
6bb6f43
Use different state key for scoped setup widgets
justinbot Jan 3, 2023
919447b
Use different subtitles to differentiate service bots setup widgets
justinbot Jan 3, 2023
855ac0a
Refactor bot user setup into bot users manager
justinbot Jan 3, 2023
5085167
Refactor to reduce duplication in widget API
justinbot Jan 3, 2023
ad953b2
Consistent room ID and intent args order
justinbot Jan 3, 2023
f46dd64
Merge branch 'main' into justinbot/service-bots-again
justinbot Jan 10, 2023
a32860d
Add docs and update changelog
justinbot Jan 10, 2023
478035a
Merge branch 'main' into justinbot/service-bots-again
justinbot Jan 11, 2023
0b858db
Add overrideUserId deprecation warning
justinbot Jan 12, 2023
5750323
Add service bots link
Half-Shot Jan 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/573.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for additional bot users called "service bots" which handle a particular connection type, so that different services can be used through different bot users.
10 changes: 9 additions & 1 deletion config.sample.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,16 @@ passFile:
bot:
# (Optional) Define profile information for the bot user
#
displayname: GitHub Bot
displayname: Hookshot Bot
avatar: mxc://half-shot.uk/2876e89ccade4cb615e210c458e2a7a6883fe17d
serviceBots:
# (Optional) Define additional bot users for specific services
#
- localpart: feeds
displayname: Feeds
avatar: mxc://half-shot.uk/2876e89ccade4cb615e210c458e2a7a6883fe17d
prefix: "!feeds"
service: feeds
metrics:
# (Optional) Prometheus metrics support
#
Expand Down
28 changes: 28 additions & 0 deletions docs/advanced/service_bots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Service Bots

Hookshot supports additional bot users called "service bots" which handle a particular connection type
(in addition to the default bot user which can handle any connection type).
These bots can coexist in a room, each handling a different service.

## Configuration

Service bots can be given a different localpart, display name, avatar, and command prefix.
They will only handle connections for the specified service, which can be one of:
* `feeds` - [Feeds](../setup/feeds.md)
* `figma` - [Figma](../setup/figma.md)
* `generic` - [Webhooks](../setup/webhooks.md)
* `github` - [GitHub](../setup/github.md)
* `gitlab` - [GitLab](../setup/gitlab.md)
* `jira` - [Jira](../setup/jira.md)

For example with this configuration:
```yaml
serviceBots:
- localpart: feeds
displayname: Feeds
avatar: mxc://half-shot.uk/2876e89ccade4cb615e210c458e2a7a6883fe17d
prefix: "!feeds"
service: feeds
```

There will be a bot user `@feeds:example.com` which responds to commands prefixed with `!feeds`, and only handles feeds connections.
5 changes: 4 additions & 1 deletion src/App/BridgeApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ListenerService } from "../ListenerService";
import { Logger } from "matrix-appservice-bridge";
import { LogService } from "matrix-bot-sdk";
import { getAppservice } from "../appservice";
import BotUsersManager from "../Managers/BotUsersManager";

Logger.configure({console: "info"});
const log = new Logger("App");
Expand Down Expand Up @@ -35,7 +36,9 @@ async function start() {
userNotificationWatcher.start();
}

const bridgeApp = new Bridge(config, listener, appservice, storage);
const botUsersManager = new BotUsersManager(config, appservice);

const bridgeApp = new Bridge(config, listener, appservice, storage, botUsersManager);

process.once("SIGTERM", () => {
log.error("Got SIGTERM");
Expand Down
358 changes: 219 additions & 139 deletions src/Bridge.ts

Large diffs are not rendered by default.

53 changes: 44 additions & 9 deletions src/Config/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export class BridgeConfigGitHub {

@configKey("Prefix used when creating ghost users for GitHub accounts.", true)
readonly userIdPrefix: string;

@configKey("URL for enterprise deployments. Does not include /api/v3", true)
private enterpriseUrl?: string;

Expand Down Expand Up @@ -129,12 +129,12 @@ export interface BridgeConfigJiraYAML {
}
export class BridgeConfigJira implements BridgeConfigJiraYAML {
static CLOUD_INSTANCE_NAME = "api.atlassian.com";

@configKey("Webhook settings for JIRA")
readonly webhook: {
secret: string;
};

// To hide the undefined for now
@hideKey()
@configKey("URL for the instance if using on prem. Ignore if targetting cloud (atlassian.net)", true)
Expand Down Expand Up @@ -411,6 +411,14 @@ interface BridgeConfigEncryption {
storagePath: string;
}

export interface BridgeConfigServiceBot {
localpart: string;
displayname?: string;
avatar?: string;
prefix: string;
service: string;
}

export interface BridgeConfigProvisioning {
bindAddress?: string;
port?: number;
Expand All @@ -425,6 +433,7 @@ export interface BridgeConfigMetrics {

export interface BridgeConfigRoot {
bot?: BridgeConfigBot;
serviceBots?: BridgeConfigServiceBot[];
bridge: BridgeConfigBridge;
experimentalEncryption?: BridgeConfigEncryption;
figma?: BridgeConfigFigma;
Expand Down Expand Up @@ -478,6 +487,8 @@ export class BridgeConfig {
public readonly feeds?: BridgeConfigFeeds;
@configKey("Define profile information for the bot user", true)
public readonly bot?: BridgeConfigBot;
@configKey("Define additional bot users for specific services", true)
public readonly serviceBots?: BridgeConfigServiceBot[];
@configKey("EXPERIMENTAL support for complimentary widgets", true)
public readonly widgets?: BridgeWidgetConfig;
@configKey("Provisioning API for integration managers", true)
Expand Down Expand Up @@ -513,6 +524,7 @@ export class BridgeConfig {
this.provisioning = configData.provisioning;
this.passFile = configData.passFile;
this.bot = configData.bot;
this.serviceBots = configData.serviceBots;
this.metrics = configData.metrics;
this.queue = configData.queue || {
monolithic: true,
Expand All @@ -525,7 +537,7 @@ export class BridgeConfig {
}

this.widgets = configData.widgets && new BridgeWidgetConfig(configData.widgets);

// To allow DEBUG as well as debug
this.logging.level = this.logging.level.toLowerCase() as "debug"|"info"|"warn"|"error"|"trace";
if (!ValidLogLevelStrings.includes(this.logging.level)) {
Expand All @@ -547,7 +559,7 @@ For more details, see https://github.com/matrix-org/matrix-hookshot/issues/594.
}];
this.bridgePermissions = new BridgePermissions(this.permissions);

if (!configData.permissions) {
if (!configData.permissions) {
log.warn(`You have not configured any permissions for the bridge, which by default means all users on ${this.bridge.domain} have admin levels of control. Please adjust your config.`);
}

Expand All @@ -574,7 +586,7 @@ For more details, see https://github.com/matrix-org/matrix-hookshot/issues/594.
});
log.warn("The `webhook` configuration still specifies a port/bindAddress. This should be moved to the `listeners` config.");
}

if (configData.widgets?.port) {
this.listeners.push({
resources: ['widgets'],
Expand All @@ -590,7 +602,7 @@ For more details, see https://github.com/matrix-org/matrix-hookshot/issues/594.
})
log.warn("The `provisioning` configuration still specifies a port/bindAddress. This should be moved to the `listeners` config.");
}

if (this.metrics?.port) {
this.listeners.push({
resources: ['metrics'],
Expand All @@ -599,7 +611,7 @@ For more details, see https://github.com/matrix-org/matrix-hookshot/issues/594.
})
log.warn("The `metrics` configuration still specifies a port/bindAddress. This should be moved to the `listeners` config.");
}

if (configData.widgets?.port) {
this.listeners.push({
resources: ['widgets'],
Expand Down Expand Up @@ -637,7 +649,7 @@ For more details, see https://github.com/matrix-org/matrix-hookshot/issues/594.
const membership = await client.getJoinedRoomMembers(await client.resolveRoom(roomEntry));
membership.forEach(userId => this.bridgePermissions.addMemberToCache(roomEntry, userId));
log.debug(`Found ${membership.length} users for ${roomEntry}`);
}
}
}

public addMemberToCache(roomId: string, userId: string) {
Expand All @@ -656,6 +668,29 @@ For more details, see https://github.com/matrix-org/matrix-hookshot/issues/594.
return this.bridgePermissions.checkAction(mxid, service, BridgePermissionLevel[permission]);
}

public get enabledServices(): string[] {
const services = [];
if (this.feeds && this.feeds.enabled) {
services.push("feeds");
}
if (this.figma) {
services.push("figma");
}
if (this.generic && this.generic.enabled) {
services.push("generic");
}
if (this.github) {
services.push("github");
}
if (this.gitlab) {
services.push("gitlab");
}
if (this.jira) {
services.push("jira");
}
return services;
}

public getPublicConfigForService(serviceName: string): Record<string, unknown> {
let config: undefined|Record<string, unknown>;
switch (serviceName) {
Expand Down
13 changes: 11 additions & 2 deletions src/Config/Defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const DefaultConfig = new BridgeConfig({
url: "http://localhost:8008",
mediaUrl: "http://example.com",
port: 9993,
bindAddress: "127.0.0.1",
bindAddress: "127.0.0.1",
},
queue: {
monolithic: true,
Expand Down Expand Up @@ -44,9 +44,18 @@ export const DefaultConfig = new BridgeConfig({
},
},
bot: {
displayname: "GitHub Bot",
displayname: "Hookshot Bot",
avatar: "mxc://half-shot.uk/2876e89ccade4cb615e210c458e2a7a6883fe17d"
},
serviceBots: [
{
localpart: "feeds",
displayname: "Feeds",
avatar: "mxc://half-shot.uk/2876e89ccade4cb615e210c458e2a7a6883fe17d",
prefix: "!feeds",
service: "feeds",
},
],
github: {
auth: {
id: 123,
Expand Down
Loading