Skip to content

Commit

Permalink
Set up generic AprilBot
Browse files Browse the repository at this point in the history
This boilerplate creates some basic AprilBot functionality for
connecting to slack and handling some basic types of interactions.

I had to create types for the event handlers for now, though I opened an
issue in the Slack sdk in the hopes they might add types [1] to their
events.

This also swaps out the class-methods-use-this lint rule for a smarter
variety, which is probably something that the upstream maintainer should
consider adding as well [2].

[1] slackapi/node-slack-sdk#1768
[2] iamturns/eslint-config-airbnb-typescript#344
  • Loading branch information
slifty committed Apr 2, 2024
1 parent bee9f6f commit 2975e83
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 4 deletions.
7 changes: 7 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
"import/prefer-default-export": "off",
"import/no-default-export": "error",
"@typescript-eslint/prefer-readonly-parameter-types": "off",
"class-methods-use-this": "off",
"@typescript-eslint/class-methods-use-this": [
"error",
{
"ignoreOverrideMethods": true
}
],
"import/order": [
"error",
{
Expand Down
36 changes: 33 additions & 3 deletions src/AprilBot.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,43 @@
import { GenericAprilBot } from './GenericAprilBot';
import { getLogger } from './logger';
import type {
SocketModeEventPayload,
ReactionAddedEvent,
ReactionRemovedEvent,
MessageEvent,
} from './types';

const logger = getLogger();

class AprilBot extends GenericAprilBot {
constructor(userToken: string, appToken: string) {
super(userToken, appToken);
logger.debug('AprilBot is constructed');
}

public async start() {
await super.start();
}
protected override handleMessage = ({
ack,
event,
}: SocketModeEventPayload<MessageEvent>) => {
ack();
logger.debug(event, 'handleMessage');
};

protected override handleReactionAdded = ({
ack,
event,
}: SocketModeEventPayload<ReactionAddedEvent>) => {
ack();
logger.debug(event, 'handleReactionAdded');
};

protected override handleReactionRemoved = ({
ack,
event,
}: SocketModeEventPayload<ReactionRemovedEvent>) => {
ack();
logger.debug(event, 'handleReactionRemoved');
};
}

export { AprilBot };
23 changes: 22 additions & 1 deletion src/GenericAprilBot.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { SocketModeClient } from '@slack/socket-mode';
import { WebClient } from '@slack/web-api';
import type {
SocketModeEventPayload,
ReactionAddedEvent,
ReactionRemovedEvent,
MessageEvent,
} from './types';

abstract class GenericAprilBot {
private readonly userToken: string;
Expand All @@ -19,9 +25,24 @@ abstract class GenericAprilBot {
this.webClient = new WebClient(this.userToken);
}

public async start() {
public async start(): Promise<void> {
this.socketModeClient.on('message', this.handleMessage);
this.socketModeClient.on('reaction_added', this.handleReactionAdded);
this.socketModeClient.on('reaction_removed', this.handleReactionRemoved);
await this.socketModeClient.start();
}

protected abstract handleMessage: (
args: SocketModeEventPayload<MessageEvent>,
) => void;

protected abstract handleReactionAdded: (
args: SocketModeEventPayload<ReactionAddedEvent>,
) => void;

protected abstract handleReactionRemoved: (
args: SocketModeEventPayload<ReactionRemovedEvent>,
) => void;
}

export { GenericAprilBot };
1 change: 1 addition & 0 deletions src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import pino from 'pino';

const getLogger = () =>
pino({
level: process.env.LOG_LEVEL ?? 'info',
transport: {
target: 'pino-pretty',
options: {
Expand Down
31 changes: 31 additions & 0 deletions src/types/MessageEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
interface MessageEvent {
user: string;
type: string;
ts: string;
client_msg_id: string;
text: string;
team: string;
thread_ts?: string;
blocks: MessageBlock[];
channel: string;
event_ts: string;
channel_type: string;
}

interface MessageBlock {
type: string;
block_id: string;
elements: BlockElement[];
}

interface BlockElement {
type: string[];
elements: BlockElementElement[];
}

interface BlockElementElement {
type: string;
text: string;
}

export { MessageEvent };
14 changes: 14 additions & 0 deletions src/types/ReactionAddedEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
interface ReactionAddedEvent {
type: string;
user: string;
reaction: string;
item: {
type: string;
channel: string;
ts: string;
};
item_user: string;
event_ts: string;
}

export { ReactionAddedEvent };
14 changes: 14 additions & 0 deletions src/types/ReactionRemovedEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
interface ReactionRemovedEvent {
type: string;
user: string;
reaction: string;
item: {
type: string;
channel: string;
ts: string;
};
item_user: string;
event_ts: string;
}

export { ReactionRemovedEvent };
6 changes: 6 additions & 0 deletions src/types/SocketModeEventPayload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
interface SocketModeEventPayload<T> {
event: T;
ack: () => void;
}

export { SocketModeEventPayload };
4 changes: 4 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './MessageEvent';
export * from './ReactionAddedEvent';
export * from './ReactionRemovedEvent';
export * from './SocketModeEventPayload';

0 comments on commit 2975e83

Please # to comment.