Implements 2fa authentication
							parent
							
								
									e8f47fcc21
								
							
						
					
					
						commit
						32b3ad753c
					
				| 
						 | 
				
			
			@ -15,6 +15,7 @@ F95_PASSWORD = YOUR_PASSWORD
 | 
			
		|||
"use strict";
 | 
			
		||||
 | 
			
		||||
// Public modules from npm
 | 
			
		||||
import inquirer from "inquirer";
 | 
			
		||||
import dotenv from "dotenv";
 | 
			
		||||
 | 
			
		||||
// Modules from file
 | 
			
		||||
| 
						 | 
				
			
			@ -33,6 +34,24 @@ dotenv.config();
 | 
			
		|||
 | 
			
		||||
main();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Ask the user to enter the OTP code
 | 
			
		||||
 * necessary to authenticate on the server.
 | 
			
		||||
 */
 | 
			
		||||
async function insert2faCode(): Promise<number> {
 | 
			
		||||
  const questions = [
 | 
			
		||||
    {
 | 
			
		||||
      type: "input",
 | 
			
		||||
      name: "code",
 | 
			
		||||
      message: "Insert 2FA code:"
 | 
			
		||||
    }
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  // Prompt the user to insert the code
 | 
			
		||||
  const answers = await inquirer.prompt(questions);
 | 
			
		||||
  return answers.code as number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function main() {
 | 
			
		||||
  // Local variables
 | 
			
		||||
  const gameList = ["City of broken dreamers", "Seeds of chaos", "MIST"];
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +60,8 @@ async function main() {
 | 
			
		|||
  console.log("Authenticating...");
 | 
			
		||||
  const result = await login(
 | 
			
		||||
    process.env.F95_USERNAME,
 | 
			
		||||
    process.env.F95_PASSWORD
 | 
			
		||||
    process.env.F95_PASSWORD,
 | 
			
		||||
    insert2faCode
 | 
			
		||||
  );
 | 
			
		||||
  console.log(`Authentication result: ${result.message}\n`);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										38
									
								
								src/index.ts
								
								
								
								
							
							
						
						
									
										38
									
								
								src/index.ts
								
								
								
								
							| 
						 | 
				
			
			@ -8,7 +8,12 @@
 | 
			
		|||
// Modules from file
 | 
			
		||||
import shared from "./scripts/shared.js";
 | 
			
		||||
import search from "./scripts/search.js";
 | 
			
		||||
import { authenticate, urlExists, isF95URL } from "./scripts/network-helper.js";
 | 
			
		||||
import {
 | 
			
		||||
  authenticate,
 | 
			
		||||
  urlExists,
 | 
			
		||||
  isF95URL,
 | 
			
		||||
  send2faCode
 | 
			
		||||
} from "./scripts/network-helper.js";
 | 
			
		||||
import fetchLatestHandiworkURLs from "./scripts/fetch-data/fetch-latest.js";
 | 
			
		||||
import fetchPlatformData from "./scripts/fetch-data/fetch-platform-data.js";
 | 
			
		||||
import getHandiworkInformation from "./scripts/scrape-data/handiwork-parse.js";
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +35,7 @@ const USER_NOT_LOGGED = "User not authenticated, unable to continue";
 | 
			
		|||
//#endregion
 | 
			
		||||
 | 
			
		||||
//#region Re-export classes
 | 
			
		||||
 | 
			
		||||
export { default as Animation } from "./scripts/classes/handiwork/animation.js";
 | 
			
		||||
export { default as Asset } from "./scripts/classes/handiwork/asset.js";
 | 
			
		||||
export { default as Comic } from "./scripts/classes/handiwork/comic.js";
 | 
			
		||||
| 
						 | 
				
			
			@ -44,9 +50,11 @@ export { default as UserProfile } from "./scripts/classes/mapping/user-profile.j
 | 
			
		|||
export { default as HandiworkSearchQuery } from "./scripts/classes/query/handiwork-search-query.js";
 | 
			
		||||
export { default as LatestSearchQuery } from "./scripts/classes/query/latest-search-query.js";
 | 
			
		||||
export { default as ThreadSearchQuery } from "./scripts/classes/query/thread-search-query.js";
 | 
			
		||||
 | 
			
		||||
//#endregion Re-export classes
 | 
			
		||||
 | 
			
		||||
//#region Export properties
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Set the logger level for module debugging.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -60,6 +68,7 @@ shared.logger.level = "warn"; // By default log only the warn messages
 | 
			
		|||
export function isLogged(): boolean {
 | 
			
		||||
  return shared.isLogged;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//#endregion Export properties
 | 
			
		||||
 | 
			
		||||
//#region Export methods
 | 
			
		||||
| 
						 | 
				
			
			@ -68,10 +77,15 @@ export function isLogged(): boolean {
 | 
			
		|||
 * Log in to the F95Zone platform.
 | 
			
		||||
 *
 | 
			
		||||
 * This **must** be the first operation performed before accessing any other script functions.
 | 
			
		||||
 *
 | 
			
		||||
 * @param cb2fa
 | 
			
		||||
 * Callback used if two-factor authentication is required for the profile.
 | 
			
		||||
 * It must return he OTP code to use for the login.
 | 
			
		||||
 */
 | 
			
		||||
export async function login(
 | 
			
		||||
  username: string,
 | 
			
		||||
  password: string
 | 
			
		||||
  password: string,
 | 
			
		||||
  cb2fa?: () => Promise<number>
 | 
			
		||||
): Promise<LoginResult> {
 | 
			
		||||
  // Try to load a previous session
 | 
			
		||||
  await shared.session.load();
 | 
			
		||||
| 
						 | 
				
			
			@ -93,17 +107,25 @@ export async function login(
 | 
			
		|||
  await creds.fetchToken();
 | 
			
		||||
 | 
			
		||||
  shared.logger.trace(`Authentication for ${username}`);
 | 
			
		||||
  const result = await authenticate(creds);
 | 
			
		||||
  let result = await authenticate(creds);
 | 
			
		||||
  shared.setIsLogged(result.success);
 | 
			
		||||
 | 
			
		||||
  if (result.success) {
 | 
			
		||||
    // Load platform data
 | 
			
		||||
    await fetchPlatformData();
 | 
			
		||||
  // 2FA Authentication is required, fetch OTP
 | 
			
		||||
  if (result.message === "Two-factor authentication is needed to continue") {
 | 
			
		||||
    const code = await cb2fa();
 | 
			
		||||
    const response2fa = await send2faCode(code, creds.token);
 | 
			
		||||
    if (response2fa.isSuccess()) result = response2fa.value;
 | 
			
		||||
    else throw response2fa.value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (result.success) {
 | 
			
		||||
    // Recreate the session, overwriting the old one
 | 
			
		||||
    shared.session.create(username, password, creds.token);
 | 
			
		||||
    await shared.session.save();
 | 
			
		||||
 | 
			
		||||
    // Load platform data
 | 
			
		||||
    await fetchPlatformData();
 | 
			
		||||
 | 
			
		||||
    shared.logger.info("User logged in through the platform");
 | 
			
		||||
  } else shared.logger.warn(`Error during authentication: ${result.message}`);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -147,7 +169,7 @@ export async function checkIfHandiworkHasUpdate(
 | 
			
		|||
 */
 | 
			
		||||
export async function searchHandiwork<T extends IBasic>(
 | 
			
		||||
  query: HandiworkSearchQuery,
 | 
			
		||||
  limit = 10
 | 
			
		||||
  limit: number = 10
 | 
			
		||||
): Promise<T[]> {
 | 
			
		||||
  // Check if the user is logged
 | 
			
		||||
  if (!shared.isLogged) throw new UserNotLogged(USER_NOT_LOGGED);
 | 
			
		||||
| 
						 | 
				
			
			@ -203,7 +225,7 @@ export async function getUserData(): Promise<UserProfile> {
 | 
			
		|||
 */
 | 
			
		||||
export async function getLatestUpdates<T extends IBasic>(
 | 
			
		||||
  query: LatestSearchQuery,
 | 
			
		||||
  limit = 10
 | 
			
		||||
  limit: number = 10
 | 
			
		||||
): Promise<T[]> {
 | 
			
		||||
  // Check limit value
 | 
			
		||||
  if (limit <= 0) throw new Error("limit must be greater than 0");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue