Skip to content

Commit

Permalink
Analyzers were reworked into DirectMessageProcessors. Any time that P…
Browse files Browse the repository at this point in the history
…hil receives a direct message, it is sent to a DirectMessageDispatcher, which finds the first processor that is willing to accept it. That processor is run, and then that's the end of processing that message. If no processors are active or willing to accept the message, then the message is discarded without action being taken. Direct messages will not be processed as commands any longer.

Fixed a bug where SubmissionSession.getActiveSession was not returning the correct results because it wasn't taking into account the time portion of NOW, just the date portion.
  • Loading branch information
ahlec committed Jun 1, 2018
1 parent b1f156d commit c17bc52
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 142 deletions.
11 changes: 0 additions & 11 deletions src/analyzers/@types.ts

This file was deleted.

12 changes: 0 additions & 12 deletions src/analyzers/index.ts

This file was deleted.

28 changes: 0 additions & 28 deletions src/analyzers/timezone-questionnaire.ts

This file was deleted.

12 changes: 12 additions & 0 deletions src/direct-message-processors/@base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Phil } from '../phil/phil';
import { DiscordMessage } from '../phil/discord-message';

export interface IProcessorActiveToken { // TODO: come up with a better name for this
readonly isActive : boolean;
}

export interface DirectMessageProcessor {
readonly handle : string;
canProcess(phil : Phil, message : DiscordMessage) : Promise<IProcessorActiveToken>;
process(phil : Phil, message : DiscordMessage, token : IProcessorActiveToken) : Promise<void>;
}
37 changes: 37 additions & 0 deletions src/direct-message-processors/timezone-questionnaire.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { DirectMessageProcessor, IProcessorActiveToken } from './@base';
import { Phil } from '../phil/phil';
import { DiscordMessage } from '../phil/discord-message';
import { TimezoneQuestionnaire } from '../phil/timezone-questionnaire';

interface TimezoneQuestionnaireToken extends IProcessorActiveToken {
readonly currentStage? : TimezoneQuestionnaire.Stage;
}

export class TimezoneQuestionnaireProcessor implements DirectMessageProcessor {
readonly handle = 'timezone-questionnaire';

async canProcess(phil : Phil, message : DiscordMessage) : Promise<TimezoneQuestionnaireToken> {
const currentStage = await TimezoneQuestionnaire.getStageForUser(phil.db, message.userId);
if (!currentStage) {
return {
isActive: false
};
}

if (!TimezoneQuestionnaire.isCurrentlyDoingQuestionnaire(currentStage.stage)) {
return {
isActive: false
};
}

return {
isActive: true,
currentStage: currentStage
}
}

async process(phil : Phil, message : DiscordMessage, rawToken : IProcessorActiveToken) {
const token = rawToken as TimezoneQuestionnaireToken;
token.currentStage.processInput(phil, message);
}
}
48 changes: 0 additions & 48 deletions src/phil/analyzer-manager.ts

This file was deleted.

50 changes: 50 additions & 0 deletions src/phil/direct-message-dispatcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { DirectMessageProcessor, IProcessorActiveToken } from '../direct-message-processors/@base';
import { TimezoneQuestionnaireProcessor } from '../direct-message-processors/timezone-questionnaire';

import { Phil } from './phil';
import { DiscordMessage } from './discord-message';
import { DiscordPromises } from '../promises/discord';
import { ServerConfig } from './server-config';
const util = require('util');

export class DirectMessageDispatcher {
private readonly processorsInPriorityOrder : DirectMessageProcessor[] = [
new TimezoneQuestionnaireProcessor()
];

constructor(private readonly phil : Phil) {
}

async process(message : DiscordMessage) {
for (let processor of this.processorsInPriorityOrder) {
try {
const token = await processor.canProcess(this.phil, message);
if (token.isActive) {
await processor.process(this.phil, message, token);
return;
}

} catch (err) {
this.reportError(err, message.serverConfig, processor);
return;
}
}
}

private reportError(err : Error, serverConfig : ServerConfig, processor : DirectMessageProcessor) {
console.error(err);

if (typeof(err) !== 'string') {
err = util.inspect(err);
}

DiscordPromises.sendEmbedMessage(this.phil.bot, serverConfig.botControlChannel.id, {
color: 0xCD5555,
title: ':no_entry: Processor Error',
description: err,
footer: {
text: 'processor: ' + processor.handle
}
});
}
};
37 changes: 25 additions & 12 deletions src/phil/discord-message.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use strict';

import { Client as DiscordIOClient, Server as DiscordIOServer, User as DiscordIOUser } from 'discord.io';
import { Phil } from './phil';
import { Server as DiscordIOServer, User as DiscordIOUser } from 'discord.io';
import { OfficialDiscordMessage, OfficialDiscordPayload } from 'official-discord';
import { ServerConfig } from './server-config';

Expand All @@ -15,13 +14,13 @@ export class DiscordMessage {
public readonly user : DiscordIOUser;
public readonly userId : string;
public readonly content : string;
public readonly isDirectMessage : boolean;
public readonly server : DiscordIOServer;
public readonly server? : DiscordIOServer;
public readonly mentions : DiscordMessageMention[];

constructor(event : OfficialDiscordPayload<OfficialDiscordMessage>,
bot : DiscordIOClient,
public readonly serverConfig : ServerConfig) {
private constructor(event : OfficialDiscordPayload<OfficialDiscordMessage>,
phil : Phil,
public readonly serverConfig : ServerConfig,
public readonly isDirectMessage : boolean) {

this.mentions = [];
for (let mention of event.d.mentions) {
Expand All @@ -35,9 +34,23 @@ export class DiscordMessage {
this.id = event.d.id;
this.channelId = event.d.channel_id;
this.userId = event.d.author.id;
this.user = bot.users[this.userId];
this.user = phil.bot.users[this.userId];
this.content = event.d.content;
this.server = this.serverConfig.server;
this.isDirectMessage = (event.d.channel_id in bot.directMessages);

if (!isDirectMessage) {
this.server = this.serverConfig.server;
}
}
};

static async parse(event : OfficialDiscordPayload<OfficialDiscordMessage>, phil : Phil)
: Promise<DiscordMessage> {
const isDirectMessage = (event.d.channel_id in phil.bot.directMessages);
var serverConfig : ServerConfig;
if (!isDirectMessage) {
var server = phil.getServerFromChannelId(event.d.channel_id);
serverConfig = await phil.serverDirectory.getServerConfig(server);
}

return new DiscordMessage(event, phil, serverConfig, isDirectMessage);
}
}
37 changes: 13 additions & 24 deletions src/phil/phil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const assert = require('assert');
import { Client as DiscordIOClient, Member as DiscordIOMember, Server as DiscordIOServer } from 'discord.io';
import { CommandRunner } from './command-runner';
import { ChronoManager } from './chrono-manager';
import { AnalyzerManager } from './analyzer-manager';
import { DirectMessageDispatcher } from './direct-message-dispatcher';
import { ReactableProcessor } from './reactables/processor';
import { greetNewMember } from './greeting';
import { DiscordMessage } from './discord-message';
Expand All @@ -20,19 +20,19 @@ function ignoreDiscordCode(code : number) {

export class Phil {
readonly bot : DiscordIOClient;
readonly serverDirectory : ServerDirectory;
private readonly _commandRunner : CommandRunner;
private readonly _chronoManager : ChronoManager;
private readonly _analyzerManager : AnalyzerManager;
private readonly _directMessageDispatcher : DirectMessageDispatcher;
private readonly _reactableProcessor : ReactableProcessor;
private readonly _serverDirectory : ServerDirectory;
private _shouldSendDisconnectedMessage : boolean;

constructor(public readonly db: Database, public readonly globalConfig : GlobalConfig) {
this.bot = new DiscordIOClient({ token: globalConfig.discordBotToken, autorun: true });
this._serverDirectory = new ServerDirectory(this);
this.serverDirectory = new ServerDirectory(this);
this._commandRunner = new CommandRunner(this, this.bot, this.db);
this._chronoManager = new ChronoManager(this, this._serverDirectory);
this._analyzerManager = new AnalyzerManager(this);
this._chronoManager = new ChronoManager(this, this.serverDirectory);
this._directMessageDispatcher = new DirectMessageDispatcher(this);
this._reactableProcessor = new ReactableProcessor(this);
}

Expand Down Expand Up @@ -82,7 +82,7 @@ export class Phil {
}

private async _onMessage(user : string, userId : string, channelId : string, msg : string, event : OfficialDiscordPayload<OfficialDiscordMessage>) {
const message = await this.getDiscordMessage(event);
const message = await DiscordMessage.parse(event, this);

if (this.isMessageFromPhil(message)) {
this._handleOwnMessage(event);
Expand All @@ -93,31 +93,20 @@ export class Phil {
return;
}

if (message.isDirectMessage) {
this._directMessageDispatcher.process(message);
return;
}

if (this._chronoManager) {
this._chronoManager.recordNewMessageInChannel(channelId);
}

if (this._commandRunner.isCommand(message)) {
this._commandRunner.runMessage(message);
} else {
this._analyzerManager.analyzeMessage(message);
}
}

private async getDiscordMessage(event : OfficialDiscordPayload<OfficialDiscordMessage>) : Promise<DiscordMessage> {
var server = this.getServerFromChannelId(event.d.channel_id);
if (!server) {
server = this.getServerFromChannelId(process.env.HIJACK_CHANNEL_ID); // TODO: Temp for v13 to allow direct messaging. v14 we remove direct messaging altogether.
}

const serverConfig = await this._serverDirectory.getServerConfig(server);
if (!serverConfig) {
return;
}

return new DiscordMessage(event, this.bot, serverConfig);
}

private isMessageFromPhil(message : DiscordMessage) : boolean {
return (message.userId === this.bot.id);
}
Expand Down Expand Up @@ -168,7 +157,7 @@ export class Phil {
const server = this.bot.servers[serverId];
assert(server);

const serverConfig = await this._serverDirectory.getServerConfig(server);
const serverConfig = await this.serverDirectory.getServerConfig(server);
await greetNewMember(this, serverConfig, member);
}

Expand Down
4 changes: 1 addition & 3 deletions src/phil/prompts/submission-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ export class SubmissionSession {
}

static async getActiveSession(phil : Phil, userId : string) : Promise<SubmissionSession | null> {
const now = new Date();
const utcNow = now.getUTCFullYear() + '-' + (now.getUTCMonth() + 1) +
'-' + now.getUTCDate();
const utcNow = momentModule.utc();
const results = await phil.db.query(`SELECT pss.*
FROM prompt_submission_sessions pss
LEFT JOIN prompt_buckets pb
Expand Down
2 changes: 1 addition & 1 deletion src/phil/server-directory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class ServerDirectory {

async getServerConfig(server : DiscordIOServer) : Promise<ServerConfig> {
if (!server) {
throw new Error('Unknown how we\'ll handle DMs. Also, need to support new servers that aren\'t configured yet.');
throw new Error('Server was not provided to this function!');
}

const cached = this._configCache[server.id];
Expand Down
Loading

0 comments on commit c17bc52

Please sign in to comment.