updated database service

master
j 2024-03-26 22:53:00 -04:00
parent 0087c3523d
commit d1aa19e4c3
1 changed files with 47 additions and 127 deletions

View File

@ -1,6 +1,6 @@
import { Database } from 'sqlite';
import { Comment } from '../../rdrama/models/Comment';
import { DatabaseInitializer } from '../initializeDatabase';
import GameState from '../../game/gameState';
/**
* Service for interacting with the SQLite database for operations related to comments and user mentions.
@ -27,136 +27,56 @@ export class DatabaseService {
}
/**
* Inserts a new user mention into the database.
* This static method adds a record of a user being mentioned in a comment.
*
* Loads an existing game state for a given author from the database.
* If an existing game state is found, it returns a new GameState instance initialized with the stored values.
* If no game state exists for the given author, it returns null, indicating that a new game state needs to be initialized.
*
* @example
* await DatabaseService.insertUserMention({
* rdrama_comment_id: 456,
* username: 'mentionedUser',
* message: 'You were mentioned in a comment.'
* });
*
* @param {Object} mention - The user mention object to insert.
* @param {number} mention.rdrama_comment_id - The ID of the comment from the r/Drama platform.
* @param {string} mention.username - The mentioned Reddit username.
* @param {string} [mention.message] - The content of the message sent to the mentioned user.
* @throws {Error} Will throw an error if the insert operation fails.
* const gameState = await DatabaseService.loadGameState(authorId);
* if (gameState) {
* console.log('Game state loaded successfully.');
* } else {
* console.log('No existing game state found. Initializing a new game.');
* }
*
* @param {number} authorId - The unique identifier for the author of the game.
* @returns {Promise<GameState | null>} A promise that resolves to a GameState instance if found, or null if no existing game state is present.
* @throws {Error} Will throw an error if the database operation fails.
*/
public static async insertUserMention(mention: { rdrama_comment_id: number; username: string; message?: string }): Promise<void> {
const db = await DatabaseService.getDatabase()
const sql = `INSERT INTO user_mentions (rdrama_comment_id, username, message) VALUES (?, ?, ?)`;
await db.run(sql, [mention.rdrama_comment_id, mention.username, mention.message]);
}
/**
* Queries the database to check if a username has been mentioned.
*
* @example
* const mentioned = await DatabaseService.userMentionExists('exampleUser');
* console.log(mentioned ? 'User has been mentioned.' : 'User has not been mentioned.');
*
* @param {string} username - The username to search for.
* @returns {Promise<boolean>} A boolean indicating whether the username has been mentioned.
* @throws {Error} Will throw an error if the query operation fails.
*/
public static async userMentionExists(username: string): Promise<boolean> {
const db = await DatabaseService.getDatabase()
const sql = `SELECT 1 FROM user_mentions WHERE username = ?`;
const result = await db.get(sql, [username]);
return !!result;
}
/**
* Inserts or updates the OAuth token in the database for a specific service.
*
* @example
* await DatabaseService.upsertOAuthToken('https://oauth.reddit.com', {
* access_token: 'abc123',
* token_type: 'bearer',
* expires_in: 3600,
* scope: 'read'
* });
*
* @param {string} token_identifier - A unique identifier for the token, typically the service's base URL.
* @param {Object} tokenData - The OAuth token data including access_token, token_type, expires_in, and scope.
* @throws {Error} Will throw an error if the upsert operation fails.
*/
public static async upsertOAuthToken(token_identifier: string, tokenData: any) {
const db = await DatabaseService.getDatabase()
const { access_token, token_type, expires_in, scope } = tokenData;
const expiryTimestamp = Math.floor(Date.now() / 1000) + expires_in;
console.log('token_identifier', token_identifier)
console.log('access_token', `${access_token.substring(0, 5)}XXXXX`)
console.log('token_type', token_type)
console.log('expires_in', expires_in)
console.log('scope', scope)
await db.run(`
INSERT INTO oauth_tokens (token_identifier, access_token, token_type, expires_in, expiry_timestamp, scope)
VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT(token_identifier) DO UPDATE SET
access_token = excluded.access_token,
token_type = excluded.token_type,
expires_in = excluded.expires_in,
expiry_timestamp = excluded.expiry_timestamp,
scope = excluded.scope
`, [token_identifier, access_token, token_type, expires_in, expiryTimestamp, scope]);
}
/**
* Retrieves the current, unexpired OAuth token for a specific service.
*
* @example
* const token = await DatabaseService.getCurrentOAuthToken('https://oauth.reddit.com');
* console.log(token ? `Current token: ${token.access_token}` : 'No valid token found.');
*
* @param {string} token_identifier - The unique identifier for the token, typically the service's base URL.
* @returns {Promise<Object|null>} The current OAuth token data or null if expired or not found.
* @throws {Error} Will throw an error if the query operation fails.
*/
public static async getCurrentOAuthToken(token_identifier: string) {
const db = await DatabaseService.getDatabase()
const tokenRow = await db.get(`
SELECT access_token, token_type, scope, expiry_timestamp FROM oauth_tokens
WHERE token_identifier = ?
`, token_identifier);
return tokenRow || null;
}
/**
* Checks if the cooldown period has passed since the last notification was sent, allowing for a new notification to be sent.
*
* @example
* const canSend = await DatabaseService.canSendNotification();
* console.log(canSend ? 'Can send a new notification.' : 'Still in cooldown period.');
*
* @returns {Promise<boolean>} True if the cooldown period has passed, allowing new notifications to be sent.
* @throws {Error} Will throw an error if the check operation fails.
*/
public static async canSendNotification(): Promise<boolean> {
const db = await DatabaseService.getDatabase()
const cooldownHours = process.env.NOTIFICATION_COOLDOWN_HOURS || 4;
const sql = `
SELECT MAX(sent_time) as last_notification_time
FROM user_mentions
`;
const result = await db.get(sql);
if (!result || !result.last_notification_time) {
// No notifications have been sent yet, or unable to retrieve the last sent time.
return true;
public static async loadGameState(authorId: number): Promise<GameState | null> {
const db = await DatabaseService.getDatabase();
const sql = `SELECT data FROM game_state WHERE authorId = ?`;
const row = await db.get(sql, [authorId]);
if (row) {
return JSON.parse(row.data) as GameState; // Assuming GameState constructor can take authorId and a partial state object
} else {
return null; // Or return a new GameState with defaults
}
}
const lastNotificationTime = new Date(result.last_notification_time).getTime();
const currentTime = new Date(new Date().toISOString().slice(0, 19).replace('T', ' ')).getTime();
const timeElapsed = currentTime - lastNotificationTime;
//console.log('timeElapsed', timeElapsed)
const cooldownPeriod = +cooldownHours * 60 * 60 * 1000; // Convert hours to milliseconds
//console.log('cooldownPeriod', cooldownPeriod)
return timeElapsed >= cooldownPeriod;
/**
* Saves the current game state for a given author to the database.
* If an existing game state for the author exists, it updates the stored values. If no game state exists, it inserts a new record.
* This method ensures the game state is persisted between sessions, allowing players to resume their game at any time.
*
* @example
* await DatabaseService.saveGameState(authorId, gameState);
* console.log('Game state saved successfully.');
*
* @param {number} authorId - The unique identifier for the author of the game.
* @param {GameState} gameState - The current game state to be saved.
* @returns {Promise<void>} A promise that resolves when the game state is successfully saved.
* @throws {Error} Will throw an error if the save operation fails.
*/
public static async saveGameState(authorId: number, gameState: GameState): Promise<void> {
const db = await DatabaseService.getDatabase();
const data = JSON.stringify(gameState);
const sql = `
INSERT INTO game_state (authorId, data) VALUES (?, ?)
ON CONFLICT(authorId) DO UPDATE SET
data = excluded.data;
`;
await db.run(sql, [authorId, data]);
}
}