Skip to content

Commit

Permalink
Merge branch 'develop' into release-1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Half-Shot committed Sep 25, 2019
2 parents 4bfb1a5 + 242bad1 commit e739f76
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 50 deletions.
1 change: 1 addition & 0 deletions changelog.d/260.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix bug where teams would start echoing messages after accepting an oauth request.
1 change: 1 addition & 0 deletions changelog.d/261.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix issue where using RTM on large deployments would trigger Slack to turn off Event subscriptions
16 changes: 12 additions & 4 deletions src/SlackEventHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,23 @@ export class SlackEventHandler extends BaseSlackHandler {
* Handles a slack event request.
* @param ISlackEventParams
*/
public async handle(event: ISlackEvent, teamId: string, response: EventHandlerCallback) {
public async handle(event: ISlackEvent, teamId: string, response: EventHandlerCallback, isEventAndUsingRtm: boolean) {
try {
log.debug("Received slack event:", event, teamId);

const endTimer = this.main.startTimer("remote_request_seconds");
// See https://api.slack.com/events-api#responding_to_events
// We must respond within 3 seconds or it will be sent again!
response(HTTP_OK, "OK");

if (isEventAndUsingRtm) {
// This is a special flag that is raised if the team is using the RTM
// API AND this event is from the Events API. Certain Events only come down
// that API, and we may have to handle those in the future. For now, all the events
// given below can be found on both APIs.
// If this flag is true, we should return early to avoid duplication.
return;
}
log.debug("Received slack event:", event, teamId);
const endTimer = this.main.startTimer("remote_request_seconds");

let err: Error|null = null;
try {
switch (event.type) {
Expand Down
84 changes: 40 additions & 44 deletions src/SlackHookHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,39 +99,10 @@ export class SlackHookHandler extends BaseSlackHandler {
const isEvent = req.headers["content-type"] === "application/json" && req.method === "POST";
try {
if (isEvent) {
const eventsResponse = (resStatus, resBody, resHeaders) => {
if (resHeaders) {
res.writeHead(resStatus, resHeaders);
} else {
res.writeHead(resStatus);
}
if (resBody) {
res.write(resBody);
}
res.end();
};
const eventPayload = JSON.parse(body) as ISlackEventPayload;
if (eventPayload.type === "url_verification") {
this.eventHandler.onVerifyUrl(eventPayload.challenge!, eventsResponse);
// If RTM is enabled for this team, do not handle the event.
// Slack will push us events for all connected teams to our bots,
// but this will cause duplicates if the team is ALSO using RTM.
} else if (eventPayload.type === "event_callback" &&
!this.main.teamIsUsingRtm(eventPayload.team_id.toUpperCase())
) {
this.eventHandler.handle(
// The event can take many forms.
// tslint:disable-next-line: no-any
eventPayload.event as any,
eventPayload.team_id,
eventsResponse,
).catch((ex) => {
log.error("Failed to handle event", ex);
});
}
this.handleEvent(body, res);
} else {
const params = qs.parse(body);
this.handle(req.method!, req.url!, params, res).catch((ex) => {
this.handleWebhook(req.method!, req.url!, params, res).catch((ex) => {
log.error("Failed to handle webhook event", ex);
});
}
Expand All @@ -146,6 +117,37 @@ export class SlackHookHandler extends BaseSlackHandler {
});
}

private handleEvent(jsonBodyStr: string, res: ServerResponse) {
const eventsResponse = (resStatus: number, resBody?: string, resHeaders?: {[key: string]: string}) => {
if (resHeaders) {
res.writeHead(resStatus, resHeaders);
} else {
res.writeHead(resStatus);
}
if (resBody) {
res.write(resBody);
}
res.end();
};
const eventPayload = JSON.parse(jsonBodyStr) as ISlackEventPayload;
if (eventPayload.type === "url_verification") {
this.eventHandler.onVerifyUrl(eventPayload.challenge!, eventsResponse);
} else if (eventPayload.type !== "event_callback") {
return; // We can't handle anything else.
}
const isUsingRtm = this.main.teamIsUsingRtm(eventPayload.team_id.toUpperCase());
this.eventHandler.handle(
// The event can take many forms.
// tslint:disable-next-line: no-any
eventPayload.event as any,
eventPayload.team_id,
eventsResponse,
isUsingRtm,
).catch((ex) => {
log.error("Failed to handle event", ex);
});
}

/**
* Handles a slack webhook request.
*
Expand All @@ -155,8 +157,8 @@ export class SlackHookHandler extends BaseSlackHandler {
* @param url The HTTP url for the incoming request
* @param params Parameters given in either the body or query string.
*/
private async handle(method: string, url: string, params: {[key: string]: string|string[]},
response: ServerResponse) {
private async handleWebhook(method: string, url: string, params: {[key: string]: string|string[]},
response: ServerResponse) {
log.info(`Received slack webhook ${method} ${url}: ${JSON.stringify(params)}`);
const endTimer = this.main.startTimer("remote_request_seconds");
const urlMatch = url.match(/^\/(.{32})(?:\/(.*))?$/);
Expand Down Expand Up @@ -335,17 +337,11 @@ export class SlackHookHandler extends BaseSlackHandler {
response.bot === undefined,
);
if (response.bot) {
const team: TeamEntry = {
bot_token: response.bot!.bot_access_token,
bot_id: response.bot!.bot_user_id,
user_id: "unknown",
id: response.team_id,
name: response.team_name,
scopes: access_scopes.join(","),
status: "ok",
domain: "not-set",
};
await this.main.datastore.upsertTeam(team);
// Rather than upsert the values we were given, use the
// access token to validate and make additional requests
await this.main.clientFactory.upsertTeamByToken(
response.bot.bot_access_token,
);
}
}
} catch (err) {
Expand Down
2 changes: 1 addition & 1 deletion src/SlackRTMHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export class SlackRTMHandler extends SlackEventHandler {
log.error("Cannot handle event, no active teamId!");
return;
}
await this.handle(event, rtm.activeTeamId! , () => {});
await this.handle(event, rtm.activeTeamId! , () => {}, false);
} catch (ex) {
log.error(`Failed to handle '${eventName}' event`);
}
Expand Down
20 changes: 19 additions & 1 deletion src/tests/integration/SlackToMatrixTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,32 @@ describe("SlackToMatrix", () => {
});

it("will drop slack events that have an unknown type", async () => {
let called = false;
await harness.eventHandler.handle({
type: "faketype",
channel: "fakechannel",
ts: "12345",
}, "12345", (status: number, body?: string) => {
called = true;
expect(status).to.equal(200);
expect(body).to.equal("OK");
});
}, false);
expect(harness.main.timerFinished.remote_request_seconds).to.be.equal("dropped");
expect(called).to.be.true;
});

it("will no-op slack events when using RTM API and is an Event API request", async () => {
let called = false;
await harness.eventHandler.handle({
type: "faketype",
channel: "fakechannel",
ts: "12345",
}, "12345", (status: number, body?: string) => {
called = true;
expect(status).to.equal(200);
expect(body).to.equal("OK");
}, true);
expect(harness.main.timerFinished.remote_request_seconds).to.be.undefined;
expect(called).to.be.true;
});
});

0 comments on commit e739f76

Please sign in to comment.