Skip to content

Commit

Permalink
POC Slack App bridging
Browse files Browse the repository at this point in the history
  • Loading branch information
tadzik committed Jun 6, 2024
1 parent 55d8e65 commit 81c3e6f
Show file tree
Hide file tree
Showing 7 changed files with 938 additions and 8 deletions.
7 changes: 7 additions & 0 deletions config/config.sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ rtm:
#
log_level: "silent"

# Use a Slack App for bridging. This is the recommended method.
#
# slack_app:
# appToken: 'xapp-foo-bar-baz'
# botToken: 'xoxb-baz-bar-foo'
# signingSecret: 'feed1337beefcafe'

# Port for incoming Slack requests from webhooks and event API messages
# Optional if using RTM API, required otherwise.
#
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"homepage": "https://github.com/matrix-org/matrix-appservice-slack#readme",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"@slack/bolt": "^3.18.0",
"@slack/logger": "^3.0.0",
"@slack/rtm-api": "^6.0.0",
"@slack/web-api": "^6.7.2",
Expand Down
3 changes: 1 addition & 2 deletions src/BridgedRoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,6 @@ export class BridgedRoom {
}
const body: ISlackChatMessagePayload = {
...matrixToSlackResult,
as_user: false,
username: (await user.getDisplaynameForRoom(message.room_id)) || matrixToSlackResult.username,
};
const text = body.text;
Expand Down Expand Up @@ -524,7 +523,7 @@ export class BridgedRoom {
// Webhooks don't give us any ID, so we can't store this.
return true;
}
if (puppetedClient) {
if (puppetedClient && this.main.teamIsUsingRtm(this.SlackTeamId!)) {
body.as_user = true;
delete body.username;
}
Expand Down
6 changes: 6 additions & 0 deletions src/IConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ export interface IConfig {
log_level?: string;
};

slack_app?: {
appToken: string,
botToken: string,
signingSecret: string,
};

slack_hook_port?: number;
slack_client_opts?: WebClientOptions;
slack_proxy?: string;
Expand Down
15 changes: 12 additions & 3 deletions src/Main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { UserAdminRoom } from "./rooms/UserAdminRoom";
import { TeamSyncer } from "./TeamSyncer";
import { SlackGhostStore } from "./SlackGhostStore";
import { AllowDenyList, DenyReason } from "./AllowDenyList";
import { SlackAppHandler } from "./SlackAppHandler";

const log = new Logger("Main");

Expand Down Expand Up @@ -146,6 +147,7 @@ export class Main {
public readonly allowDenyList: AllowDenyList;

public slackRtm?: SlackRTMHandler;
public slackAppHandler?: SlackAppHandler;
private slackHookHandler?: SlackHookHandler;

private provisioner: Provisioner;
Expand Down Expand Up @@ -175,9 +177,12 @@ export class Main {

this.matrixUsersById = new QuickLRU({ maxSize: config.caching.matrixUserCache });

if ((!config.rtm || !config.rtm.enable) && (!config.slack_hook_port || !config.inbound_uri_prefix)) {
throw Error("Neither rtm.enable nor slack_hook_port|inbound_uri_prefix is defined in the config." +
"The bridge must define a listener in order to run");
if (
(!config.rtm || !config.rtm.enable)
&& (!config.slack_hook_port || !config.inbound_uri_prefix)
&& !config.slack_app
) {
throw Error("No RTM, no webhooks and no Slack App configured. The bridge must define a listener in order to run");
}

if ((!config.rtm?.enable || !config.oauth2) && config.puppeting?.enabled) {
Expand Down Expand Up @@ -1264,6 +1269,10 @@ export class Main {
await this.bridgeBlocker?.checkLimits(this.bridge.opts.controller.userActivityTracker.countActiveUsers().allUsers);
}

if (this.config.slack_app) {
this.slackAppHandler = await SlackAppHandler.create(this, this.config.slack_app);
}

log.info("Bridge initialised");
this.ready = true;
return port;
Expand Down
73 changes: 73 additions & 0 deletions src/SlackAppHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { App, KnownEventFromType, ReactionAddedEvent, ReactionRemovedEvent, SlackEvent } from "@slack/bolt";
import { Main, METRIC_RECEIVED_MESSAGE } from "./Main";
import { SlackEventHandler } from "./SlackEventHandler";
import { Logger } from "matrix-appservice-bridge";
import { ISlackEvent } from "./BaseSlackHandler";

const log = new Logger("SlackAppHandler");

interface Config {
appToken: string,
signingSecret: string,
botToken: string,
}

export class SlackAppHandler extends SlackEventHandler {
private constructor(
main: Main,
private app: App,
private teamId: string
) {
super(main);
this.app.message(async ({ message }) => this.handleSlackAppEvent(message));
this.app.event(new RegExp('^reaction'), ({ event }) => this.handleSlackAppEvent(event));
}

public static async create(main: Main, config: Config) {
const app = new App({
appToken: config.appToken,
token: config.botToken,
signingSecret: config.signingSecret,
socketMode: true,
});
await app.start();
const teamId = await main.clientFactory.upsertTeamByToken(config.botToken);
log.info(`Slack App listening for events for team ${teamId}`);
return new SlackAppHandler(main, app, teamId);
}

private async handleSlackAppEvent(ev: SlackEvent) {
try {
switch (ev.type) {
case 'message': return this.onMessage(ev);
case 'reaction_added':
case 'reaction_removed':
return this.onReaction(ev);
default:
log.warn(`Ignoring event of type ${ev.type}`);
}
} catch (err) {
log.error(`Failed to handle Slack App event ${JSON.stringify(ev, undefined, 2)}: ${err}`);
}
}

private async onMessage(msg: KnownEventFromType<'message'>) {
log.debug("Received a message:", msg);

this.main.incCounter(METRIC_RECEIVED_MESSAGE, { side: "remote" });

switch (msg.subtype) {
case undefined: // regular message
return this.handleEvent({
...msg,
user_id: msg.user,
}, this.teamId);
default:
log.warn(`Unhandled message subtype ${msg.subtype}`);
}
}

async onReaction(ev: ReactionAddedEvent|ReactionRemovedEvent): Promise<void> {
return this.handleEvent(ev as unknown as ISlackEvent, this.teamId);
}
}
Loading

0 comments on commit 81c3e6f

Please sign in to comment.