Skip to content

Commit

Permalink
Feature/46 event sub follow (#85)
Browse files Browse the repository at this point in the history
* Add `FollowEvent` table to DB

* Add `FollowEventHandler` event sub to bot
  • Loading branch information
itanex authored Mar 5, 2024
1 parent 443bff5 commit a0322c5
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 0 deletions.
13 changes: 13 additions & 0 deletions bot/chat-bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { EventSubWsListener } from '@twurple/eventsub-ws';
import {
EventSubChannelBanEvent,
EventSubChannelCheerEvent,
EventSubChannelFollowEvent,
EventSubChannelModeratorEvent,
EventSubChannelRedemptionAddEvent,
EventSubChannelUnbanEvent,
Expand All @@ -36,6 +37,7 @@ import {
BanEventHandler,
ChannelPointEventHandler,
CheerEventHandler,
FollowerEventHandler,
ModeratorEventHandler,
StreamEventHandler,
} from './event-sub-handlers';
Expand All @@ -53,6 +55,7 @@ export default class ChatBot {
@inject(BanEventHandler) private banEventHandler: BanEventHandler,
@inject(ChannelPointEventHandler) private channelPointEventHandler: ChannelPointEventHandler,
@inject(CheerEventHandler) private cheerEventHandler: CheerEventHandler,
@inject(FollowerEventHandler) private followerEventHandler: FollowerEventHandler,
@inject(ModeratorEventHandler) private moderatorEventHandler: ModeratorEventHandler,
@inject(StreamEventHandler) private streamEventHandler: StreamEventHandler,
@inject(InjectionTypes.Logger) private logger: winston.Logger,
Expand Down Expand Up @@ -129,6 +132,16 @@ export default class ChatBot {
},
);

// Requires that a moderator with permission is part of the subscription
// Using the broadcaster as that user
this.eventSubWsListener.onChannelFollow(
environment.twitchBot.broadcaster.id,
environment.twitchBot.broadcaster.id,
(event: EventSubChannelFollowEvent): void => {
this.followerEventHandler.follow(event);
},
);

this.eventSubWsListener.onChannelModeratorAdd(
environment.twitchBot.broadcaster.id,
(event: EventSubChannelModeratorEvent): void => {
Expand Down
28 changes: 28 additions & 0 deletions bot/event-sub-handlers/follow-event.handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { EventSubChannelFollowEvent } from '@twurple/eventsub-base';
import { inject, injectable } from 'inversify';
import winston from 'winston';
import InjectionTypes from '../../dependency-management/types';
import { FollowEvent, ModeratorEvent } from '../../database';

@injectable()
export default class FollowerEventHandler {
constructor(
@inject(InjectionTypes.Logger) private logger: winston.Logger,
) {
}

/**
* Records the event of a user being added as a moderator to the channel
* @param event Moderator Event
*/
async follow(event: EventSubChannelFollowEvent): Promise<void> {
return FollowEvent
.follow(event)
.catch((reason: any) => {
this.logger.error(`Unable to store Follow event in DB >> ${reason}`);
})
.then(() => {
this.logger.info(`User ${event.userDisplayName}: followed ${event.broadcasterDisplayName} on ${(new Date()).toDateString()}`);
});
}
}
2 changes: 2 additions & 0 deletions bot/event-sub-handlers/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import BanEventHandler from './ban-event.handler';
import ChannelPointEventHandler from './channel-point-event.handler';
import CheerEventHandler from './cheer-event.handler';
import FollowerEventHandler from './follow-event.handler';
import ModeratorEventHandler from './moderator-event.handler';
import StreamEventHandler from './stream-event.handler';

export {
BanEventHandler,
ChannelPointEventHandler,
CheerEventHandler,
FollowerEventHandler,
ModeratorEventHandler,
StreamEventHandler,
};
2 changes: 2 additions & 0 deletions database/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ChannelPointRedeem,
CheerEvent,
DeathCounts,
FollowEvent,
LurkingUsers,
ModeratorEvent,
Raiders,
Expand Down Expand Up @@ -45,6 +46,7 @@ const pgConfig: SequelizeOptions = {
ChannelPointRedeem,
CheerEvent,
DeathCounts,
FollowEvent,
LurkingUsers,
ModeratorEvent,
Raiders,
Expand Down
2 changes: 2 additions & 0 deletions database/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import BanEvent from './models/banEvent.dbo';
import ChannelPointRedeem from './models/channelPointRedeem.dbo';
import CheerEvent from './models/cheerEvent.dbo';
import DeathCounts from './models/deathCountRecord.dbo';
import FollowEvent from './models/follow-event.dbo';
import LurkingUsers from './models/lurkingUser.dbo';
import ModeratorEvent from './models/moderatorEvent.dbo';
import Raiders from './models/raiders.dbo';
Expand All @@ -15,6 +16,7 @@ export {
ChannelPointRedeem,
CheerEvent,
DeathCounts,
FollowEvent,
LurkingUsers,
ModeratorEvent,
Raiders,
Expand Down
87 changes: 87 additions & 0 deletions database/models/follow-event.dbo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { EventSubChannelFollowEvent } from '@twurple/eventsub-base';
import { Column, DataType, Model, Table } from 'sequelize-typescript';

@Table({
// consider placing this in a schema to
// cover all Event Sub data
// schema: 'EventSubData',
tableName: 'FollowEvent',
paranoid: true,
})
export default class FollowEvent extends Model {
/**
* The date when the user followed.
*/
@Column({
type: DataType.DATE,
})
followDate: Date;

/**
* The ID of the broadcaster.
*/
@Column({
type: DataType.STRING(20),
})
broadcasterId: string;

/**
* The name of the broadcaster.
*/
@Column({
type: DataType.STRING(40),
})
broadcasterName: string;

/**
* The display name of the broadcaster.
*/
@Column({
type: DataType.STRING(40),
})
broadcasterDisplayName: string;

/**
* The ID of the following user.
*/
@Column({
type: DataType.STRING(20),
})
userId: string;

/**
* The name of the following user.
*/
@Column({
type: DataType.STRING(40),
})
userName: string;

/**
* The display name of the following user.
*/
@Column({
type: DataType.STRING(40),
})
userDisplayName: string;

/**
* Records the event of a user following the specific channel (broadcaster)
* @param event Follow Event
* @returns Follow Event Record
*/
static async follow(event: EventSubChannelFollowEvent): Promise<FollowEvent> {
const record: Partial<FollowEvent> = {
followDate: event.followDate,
broadcasterId: event.broadcasterId,
broadcasterName: event.broadcasterName,
broadcasterDisplayName: event.broadcasterDisplayName,
userId: event.userId,
userName: event.userName,
userDisplayName: event.userDisplayName,
};

return this
.create(record);
}
}
2 changes: 2 additions & 0 deletions dependency-management/inversify.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
BanEventHandler,
ChannelPointEventHandler,
CheerEventHandler,
FollowerEventHandler,
ModeratorEventHandler,
StreamEventHandler,
} from '../bot/event-sub-handlers';
Expand Down Expand Up @@ -114,6 +115,7 @@ SAContainer.bind<ICommandHandler>(InjectionTypes.CommandHandlers).to(WishListCom
SAContainer.bind(BanEventHandler).toSelf();
SAContainer.bind(ChannelPointEventHandler).toSelf();
SAContainer.bind(CheerEventHandler).toSelf();
SAContainer.bind(FollowerEventHandler).toSelf();
SAContainer.bind(ModeratorEventHandler).toSelf();
SAContainer.bind(StreamEventHandler).toSelf();

Expand Down

0 comments on commit a0322c5

Please sign in to comment.