Cleanup and go Live
parent
0b91c0dcf5
commit
a17d520eea
14
src/index.ts
14
src/index.ts
|
@ -4,12 +4,9 @@ dotenv.config();
|
|||
import WorkflowOrchestrator from './workflows/WorkflowOrchestrator';
|
||||
import rDramaSession from './rdrama/session/SessionManager';
|
||||
import redditSession from './reddit/session/SessionManager';
|
||||
import { CommentParser } from './rdrama/services/CommentParser';
|
||||
import { DatabaseInitializer } from './db/initializeDatabase';
|
||||
import { DatabaseService } from './db/services/Database';
|
||||
import { DatabaseMaintenanceService } from './db/services/DatabaseMaintenance';
|
||||
import { CommentProcessor } from './rdrama/services/CommentProcessor';
|
||||
import { CommentPoster } from './rdrama/services/CommentPoster';
|
||||
// Import other necessary services or configurations
|
||||
|
||||
async function startApplication() {
|
||||
|
@ -41,17 +38,8 @@ async function startApplication() {
|
|||
console.log('Reddit Session Start')
|
||||
await redditSession.getInstance()
|
||||
|
||||
// Initialize services with any required dependencies
|
||||
const commentFetcher = new CommentProcessor();
|
||||
const commentParser = new CommentParser();
|
||||
const commentPoster = new CommentPoster()
|
||||
|
||||
// Initialize and start your workflow
|
||||
const workflowOrchestrator = new WorkflowOrchestrator(
|
||||
commentFetcher,
|
||||
commentParser,
|
||||
commentPoster
|
||||
);
|
||||
const workflowOrchestrator = new WorkflowOrchestrator();
|
||||
await workflowOrchestrator.executeWorkflow();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
import { Comment } from '../models/Comment';
|
||||
|
||||
export class CommentParser {
|
||||
private regexPattern: RegExp = /(^|\s|\\r\\n|\\t|[".,;(){}\[\]!?@#])(\/?u\/[a-zA-Z0-9_]+)/g;
|
||||
|
||||
/**
|
||||
* Extracts Reddit usernames from the body of a single comment.
|
||||
* @param comment A single Comment object to be processed.
|
||||
* @returns An array of unique Reddit usernames found in the comment.
|
||||
*/
|
||||
public extractUsernames(comment: Comment): string[] {
|
||||
public static extractUsernames(comment: Comment): string[] {
|
||||
const regexPattern: RegExp = /(^|\s|\\r\\n|\\t|[".,;(){}\[\]!?@#])(\/?u\/[a-zA-Z0-9_]+)/g;
|
||||
const foundUsernames: Set<string> = new Set();
|
||||
|
||||
const matches = comment.body.match(this.regexPattern);
|
||||
const matches = comment.body.match(regexPattern);
|
||||
if (matches) {
|
||||
matches.forEach(match => {
|
||||
// Ensure the username is captured in a standardized format
|
||||
|
|
|
@ -3,11 +3,6 @@ import { Comment } from '../models/Comment';
|
|||
import FormData from 'form-data';
|
||||
|
||||
export class CommentPoster {
|
||||
private sessionManager: SessionManager;
|
||||
|
||||
constructor() {
|
||||
this.sessionManager = SessionManager.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts a comment as a reply to a given rdrama comment.
|
||||
|
@ -16,13 +11,14 @@ export class CommentPoster {
|
|||
* @param body The body of the comment to post.
|
||||
* @returns A promise resolving to the Axios response.
|
||||
*/
|
||||
public async postComment(parentId: string, body: string): Promise<Comment> {
|
||||
public static async postComment(parentId: string, body: string): Promise<Comment> {
|
||||
const sessionManager = SessionManager.getInstance();
|
||||
const formData = new FormData();
|
||||
formData.append('parent_fullname', parentId);
|
||||
formData.append('body', body);
|
||||
|
||||
try {
|
||||
const response = await this.sessionManager.axiosInstance.post('/comment', formData, {
|
||||
const response = await sessionManager.axiosInstance.post('/comment', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
|
|
|
@ -8,16 +8,6 @@ import { CommentFetcher } from './CommentFetcher';
|
|||
* with DatabaseService for checking the existence and persisting new comments.
|
||||
*/
|
||||
export class CommentProcessor {
|
||||
private maxPages: number;
|
||||
|
||||
/**
|
||||
* Creates an instance of CommentProcessor.
|
||||
* @param {DatabaseService} databaseService - The service for database operations.
|
||||
* @param {number} maxPages - The maximum number of pages to fetch from the r/Drama API. Defaults to 10.
|
||||
*/
|
||||
constructor(maxPages: number = 10) {
|
||||
this.maxPages = maxPages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches comments from the r/Drama API across multiple pages, up to the specified maximum.
|
||||
|
@ -27,11 +17,11 @@ export class CommentProcessor {
|
|||
* @returns {Promise<Comment[]>} A promise that resolves to an array of comments fetched from the API.
|
||||
* @throws {Error} Throws an error if there is a failure in fetching comments from the API.
|
||||
*/
|
||||
async processComments(): Promise<Comment[]> {
|
||||
static async processComments(maxPages: number = 10): Promise<Comment[]> {
|
||||
let comments: Comment[] = [];
|
||||
let stopFetching = false;
|
||||
|
||||
for (let page = 1; page <= this.maxPages && !stopFetching; page++) {
|
||||
for (let page = 1; page <= maxPages && !stopFetching; page++) {
|
||||
const newComments = await CommentFetcher.fetchComments(page)
|
||||
|
||||
// Check each new comment against the database and existing comments in this batch
|
||||
|
|
|
@ -1,17 +1,26 @@
|
|||
import axios, { AxiosError } from 'axios';
|
||||
import SessionManager from '../session/SessionManager';
|
||||
import { RedditUser } from '../model/User';
|
||||
import RedditSessionManager from '../session/SessionManager';
|
||||
|
||||
/**
|
||||
* Provides services for interacting with Reddit user data and sending messages.
|
||||
*/
|
||||
export class RedditService {
|
||||
private sessionManager: SessionManager;
|
||||
|
||||
constructor(sessionManager: SessionManager) {
|
||||
this.sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
async getUserInfo(username: string): Promise<RedditUser> {
|
||||
/**
|
||||
* Retrieves information about a Reddit user.
|
||||
*
|
||||
* @param {string} username - The username of the Reddit user to retrieve information for.
|
||||
* @returns {Promise<RedditUser>} A promise that resolves with the RedditUser object containing user information.
|
||||
* @throws {Error} Throws an error if fetching user information fails.
|
||||
* @example
|
||||
* RedditService.getUserInfo('exampleUser')
|
||||
* .then(userInfo => console.log(userInfo))
|
||||
* .catch(error => console.error(error));
|
||||
*/
|
||||
static async getUserInfo(username: string): Promise<RedditUser> {
|
||||
try {
|
||||
const response = await this.sessionManager.axiosInstance.get(`/user/${username}/about`);
|
||||
const redditSession = await RedditSessionManager.getInstance()
|
||||
const response = await redditSession.axiosInstance.get(`/user/${username}/about`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching user info:', error);
|
||||
|
@ -19,15 +28,37 @@ export class RedditService {
|
|||
}
|
||||
}
|
||||
|
||||
async sendMessage(username: string, subject: string, message: string): Promise<void> {
|
||||
/**
|
||||
* Sends a private message to a Reddit user.
|
||||
*
|
||||
* @param {string} username - The username of the recipient Reddit user.
|
||||
* @param {string} subject - The subject of the message.
|
||||
* @param {string} message - The body text of the message.
|
||||
* @returns {Promise<void>} A promise that resolves when the message is successfully sent.
|
||||
* @throws {Error} Throws an error if sending the message fails.
|
||||
* @example
|
||||
* RedditService.sendMessage('exampleUser', 'Hello', 'This is a test message.')
|
||||
* .then(() => console.log('Message sent successfully.'))
|
||||
* .catch(error => console.error('Error sending message:', error));
|
||||
*/
|
||||
static async sendMessage(username: string, subject: string, message: string): Promise<void> {
|
||||
try {
|
||||
console.log(`await this.sessionManager.axiosInstance.post('/api/compose', {\n\tapi_type: 'json',\n\tto: ${username},\n\tsubject: ${subject},\n\ttext: ${message},\n});`)
|
||||
//await this.sessionManager.axiosInstance.post('/api/compose', {
|
||||
// api_type: 'json',
|
||||
// to: username,
|
||||
// subject: subject,
|
||||
// text: message,
|
||||
//});
|
||||
const redditSession = await RedditSessionManager.getInstance();
|
||||
// Create a URLSearchParams object with your data
|
||||
const params = new URLSearchParams();
|
||||
params.append('api_type', 'json');
|
||||
params.append('to', `u/${username}`);
|
||||
params.append('subject', subject);
|
||||
params.append('text', message);
|
||||
|
||||
// Use the params object directly in the data field
|
||||
await redditSession.axiosInstance.post('/api/compose', params, {
|
||||
headers: {
|
||||
// Ensure the content type is set to application/x-www-form-urlencoded
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
});
|
||||
console.log(JSON.stringify(params, null, 4))
|
||||
console.log(`Message sent to ${username}`);
|
||||
} catch (error) {
|
||||
console.error('Error sending message:', error);
|
||||
|
|
|
@ -5,8 +5,46 @@ import { DatabaseService } from '../db/services/Database';
|
|||
// Load environment variables from .env file
|
||||
dotenv.config();
|
||||
|
||||
export async function shouldNotifyUser(username: string, redditService: RedditService): Promise<boolean> {
|
||||
const userInfo = await redditService.getUserInfo(username);
|
||||
/**
|
||||
* Determines whether a user should be notified based on various criteria.
|
||||
*
|
||||
* This function checks a Reddit user's information against a set of conditions
|
||||
* defined by environment variables and user attributes. These conditions include
|
||||
* whether the user is a moderator, an employee, accepts private messages, has
|
||||
* karma below a certain threshold, and has not been notified before.
|
||||
*
|
||||
* @param {string} username - The Reddit username of the user to check.
|
||||
* @returns {Promise<boolean>} - A promise that resolves to `true` if the user meets
|
||||
* the criteria for notification, otherwise `false`.
|
||||
*
|
||||
* @throws {Error} Throws an error if there's a problem fetching user information
|
||||
* from Reddit or checking the user's notification status in the database.
|
||||
*
|
||||
* @example
|
||||
* // Example of checking if a user should be notified
|
||||
* shouldNotifyUser('exampleUser').then(shouldNotify => {
|
||||
* if (shouldNotify) {
|
||||
* console.log('User should be notified.');
|
||||
* } else {
|
||||
* console.log('User should not be notified.');
|
||||
* }
|
||||
* }).catch(error => {
|
||||
* console.error('Error checking if user should be notified:', error);
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* // Environment variables used in this function
|
||||
* // .env file
|
||||
* EXCLUDE_MODS=true
|
||||
* EXCLUDE_EMPLOYEES=true
|
||||
* KARMA_THRESHOLD=100000
|
||||
*
|
||||
* These environment variables control the behavior of the function, such as whether
|
||||
* to exclude moderators or employees from notifications, and the karma threshold for
|
||||
* notifications.
|
||||
*/
|
||||
export async function shouldNotifyUser(username: string): Promise<boolean> {
|
||||
const userInfo = await RedditService.getUserInfo(username);
|
||||
if (!userInfo) return false;
|
||||
|
||||
const { is_mod, is_employee, accept_pms, total_karma } = userInfo.data;
|
||||
|
|
|
@ -4,71 +4,97 @@ import { CommentPoster } from "../rdrama/services/CommentPoster";
|
|||
import { MessageService } from "../utils/MessageService";
|
||||
import { DatabaseService } from "../db/services/Database";
|
||||
import { RedditService } from "../reddit/services/Reddit";
|
||||
import RedditSessionManager from "../reddit/session/SessionManager";
|
||||
import { shouldNotifyUser } from "../utils/ShouldNotify";
|
||||
import { Comment } from "../rdrama/models/Comment";
|
||||
|
||||
class WorkflowOrchestrator {
|
||||
constructor(
|
||||
private commentProcessor: CommentProcessor,
|
||||
private commentParser: CommentParser,
|
||||
private commentPoster: CommentPoster,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Executes the defined workflow for processing comments.
|
||||
*/
|
||||
async executeWorkflow() {
|
||||
try {
|
||||
// Fetch comments from the source
|
||||
const comments = await this.commentProcessor.processComments();
|
||||
console.log(`Fetched ${comments.length} comments`);
|
||||
|
||||
// Extract and deduplicate usernames from comments
|
||||
const allUsernames = comments.flatMap(comment => this.commentParser.extractUsernames(comment));
|
||||
const uniqueUsernames = [...new Set(allUsernames)];
|
||||
console.log(`Extracted ${uniqueUsernames.length} unique usernames`);
|
||||
const comments = await this.fetchAndLogComments();
|
||||
|
||||
for (const comment of comments) {
|
||||
const redditUsers = this.commentParser.extractUsernames(comment)
|
||||
if (redditUsers.length === 0) continue
|
||||
console.log('found:', redditUsers)
|
||||
const placeholdersRdrama = {
|
||||
author_name: comment.author_name,
|
||||
};
|
||||
for (const redditUser of redditUsers) {
|
||||
const userMentionExists = await DatabaseService.userMentionExists(redditUser)
|
||||
if (userMentionExists) continue
|
||||
const commentResponseRdrama = MessageService.getRandomRdramaMessage(placeholdersRdrama)
|
||||
if (!commentResponseRdrama) throw new Error('No comments for Rdrama found')
|
||||
const postedComment = await this.commentPoster.postComment(`c_${comment.id}`, `##### TEST MESSAGE NO REDDITOR PINGED (YET...)\n${commentResponseRdrama}`)
|
||||
//const postedComment = await this.commentPoster.postComment(`c_${comment.id}`, ${commentResponse}`) //TODO uncomment after golive
|
||||
console.log(`Sent Comment to`, JSON.stringify(postedComment, null, 4))
|
||||
const redditSession = await RedditSessionManager.getInstance()
|
||||
const redditService = new RedditService(redditSession)
|
||||
const resultshouldNotifyUser = await shouldNotifyUser(redditUser, redditService)
|
||||
if (!resultshouldNotifyUser) continue
|
||||
const placeholdersReddit = {
|
||||
author_name: comment.author_name,
|
||||
username: redditUser,
|
||||
permalink: comment.permalink
|
||||
};
|
||||
const redditMessage = MessageService.getRandomRedditMessage(placeholdersReddit)
|
||||
if (!redditMessage) throw new Error('No comments for Reddit found')
|
||||
await DatabaseService.insertUserMention({
|
||||
rdrama_comment_id: comment.id,
|
||||
username: redditUser,
|
||||
message: redditMessage,
|
||||
})
|
||||
await redditService.sendMessage(redditUser, 'Crosstalk PM Notification', redditMessage)
|
||||
return;
|
||||
await this.processComment(comment);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
console.log('Workflow executed successfully.');
|
||||
} catch (error) {
|
||||
console.error('An error occurred during workflow execution:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches comments and logs the count.
|
||||
* @returns {Promise<Array>} The fetched comments.
|
||||
*/
|
||||
async fetchAndLogComments(): Promise<Comment[]> {
|
||||
const comments = await CommentProcessor.processComments();
|
||||
console.log(`Fetched ${comments.length} comments`);
|
||||
return comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a single comment, including posting responses and sending notifications.
|
||||
* @param {Object} comment The comment to process.
|
||||
*/
|
||||
async processComment(comment: Comment) {
|
||||
const redditUsers = CommentParser.extractUsernames(comment);
|
||||
if (redditUsers.length === 0) return;
|
||||
console.log('found:', redditUsers);
|
||||
|
||||
for (const redditUser of redditUsers) {
|
||||
await this.handleUserMention(comment, redditUser);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a mention of a user in a comment, including checking for previous mentions, posting a response, and sending a notification.
|
||||
* @param {Object} comment The comment mentioning the user.
|
||||
* @param {string} redditUser The mentioned Reddit user's username.
|
||||
*/
|
||||
async handleUserMention(comment: Comment, redditUser: string) {
|
||||
const userMentionExists = await DatabaseService.userMentionExists(redditUser);
|
||||
if (userMentionExists) return;
|
||||
|
||||
const placeholdersRdrama = { author_name: comment.author_name };
|
||||
const commentResponseRdrama = MessageService.getRandomRdramaMessage(placeholdersRdrama);
|
||||
if (!commentResponseRdrama) throw new Error('No comments for Rdrama found');
|
||||
|
||||
await this.postCommentAndNotify(comment, redditUser, commentResponseRdrama);
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts a comment response and sends a notification if the user should be notified.
|
||||
* @param {Object} comment The original comment.
|
||||
* @param {string} redditUser The Reddit user to notify.
|
||||
* @param {string} commentResponseRdrama The response to post.
|
||||
*/
|
||||
async postCommentAndNotify(comment: Comment, redditUser: string, commentResponseRdrama: string) {
|
||||
// Placeholder for posting a comment. Uncomment and implement as needed.
|
||||
const postedComment = await CommentPoster.postComment(`c_${comment.id}`, `${commentResponseRdrama}`);
|
||||
console.log(`Sent Comment to`, JSON.stringify(postedComment, null, 4));
|
||||
|
||||
const resultshouldNotifyUser = await shouldNotifyUser(redditUser);
|
||||
if (!resultshouldNotifyUser) return;
|
||||
|
||||
const placeholdersReddit = {
|
||||
author_name: comment.author_name,
|
||||
username: redditUser,
|
||||
permalink: comment.permalink
|
||||
};
|
||||
const redditMessage = MessageService.getRandomRedditMessage(placeholdersReddit);
|
||||
if (!redditMessage) throw new Error('No comments for Reddit found');
|
||||
|
||||
await DatabaseService.insertUserMention({
|
||||
rdrama_comment_id: comment.id,
|
||||
username: redditUser,
|
||||
message: redditMessage,
|
||||
});
|
||||
await RedditService.sendMessage(redditUser, 'Crosstalk PM Notification', redditMessage);
|
||||
}
|
||||
}
|
||||
|
||||
export default WorkflowOrchestrator;
|
Loading…
Reference in New Issue