TikTok-mGBA-Emulator/node/source/classes/controller/GameController.ts

208 lines
8.3 KiB
TypeScript

import { MgbaHttp } from "../modules/gba-api/MgbaHttp";
import { Message } from "../modules/tiktok/Message";
import { WebcastPushConnection } from "tiktok-live-connector";
import { User, FollowerRole } from "../modules/tiktok/User";
import { Gift } from "../modules/tiktok/Gift";
import { Logger, LogLevel } from "../modules/logging/Logging";
import { UserStorage } from "../modules/database/Storage";
const FREE_GAME_INPUT_MOVES = 5;
export class GameController {
private channelName: string;
private users: Map<string, User> = new Map<string, User>();
private webcastPushConnection: WebcastPushConnection;
private mgbaHttpClient: MgbaHttp = new MgbaHttp({});
private userStorage: UserStorage;
private availableGifts: Map<number, Gift> = new Map<number, Gift>();
/**
* Create a new GameController object
* @param {string} channelName The channel name for the TikTok Live connection
*/
constructor(channelName: string) {
// Initialize the UserStorage object
this.userStorage = new UserStorage();
// Set the channel name and create a new WebcastPushConnection
this.channelName = channelName;
this.webcastPushConnection = new WebcastPushConnection(
this.channelName
);
// Set the base URL for the mGBA API client
this.mgbaHttpClient = new MgbaHttp({
baseUrl: "http://localhost:5000",
});
// Load the available gifts from the TikTok platform and start the GameController
this.getAvailableGifts().then((gifts) => {
this.availableGifts = gifts;
this.startGameController();
});
}
/**
* Load the available gifts from the TikTok platform
* @returns {void}
*/
private async getAvailableGifts(): Promise<Map<number, Gift>> {
let returnGifts = new Map<number, Gift>();
let gifts = await this.getWebcastPushConnection().getAvailableGifts();
gifts.forEach((gift: any) => {
returnGifts.set(gift.id, new Gift({
giftId: gift.id,
costDiamonds: gift.diamond_count,
name: gift.name,
}));
});
return returnGifts;
}
/**
* Get the WebcastPushConnection object for the TikTok Live connection
* @returns {WebcastPushConnection} The WebcastPushConnection object for the TikTok Live connection
*/
private getWebcastPushConnection(): WebcastPushConnection {
return this.webcastPushConnection;
}
/**
* Get the users Map object, for all users in the TikTok Live that have done some action
* @returns {Map<string, User>} The Map of users in the TikTok Live
*/
private getUsers(): Map<string, User> {
return this.users;
}
/**
* Create a new User object if it does not exist, otherwise return the existing User object
* @param {string} userId The unique identifier for the user
* @param {any} data The data object for the user, can be null for searching existing users
* @returns {User} The User object for the user
*/
private newUser(userId: string, data: any): User {
if (this.getUsers().has(userId)) {
return this.getUsers().get(userId);
} else {
let newUser = new User({
userId: userId,
displayName: data.nickname,
username: data.uniqueId,
isFollower: data.followRole === FollowerRole.FOLLOWER,
isSubscriber: data.isSubscriber,
});
this.getUsers().set(userId, newUser);
return newUser;
}
}
/**
* Handle the chat event from the TikTok Live connection
* @param {any} data The data object for the chat event
*/
private chatHandler(data: any): void {
let user = this.newUser(data.userId, data);
let message = new Message({
messageId: data.msgId,
content: data.comment,
});
this.chatHandlerForGameInteraction(message, user);
}
/**
* Handle the game input from the chat event for game interaction
* @param message {Message}
* @param user {User}
*/
private chatHandlerForGameInteraction(message: Message, user: User): void {
let gameKeyInterpretation = message.getMessageGameKey();
if (gameKeyInterpretation !== false) {
this.getUserStorage().getUser(user.getUserId()).then((storageUser) => {
if (storageUser === undefined) {
this.getUserStorage().createUser(parseInt(user.getUserId()), FREE_GAME_INPUT_MOVES);
this.chatHandlerForGameInteraction(message, user);
return;
}
let hasBalance = storageUser.credits > 0 || this.getDeveloperUserIds().includes(parseInt(user.getUserId()));
if(hasBalance) {
// Subtract the credit from the user when the user is not a developer
if(!this.getDeveloperUserIds().includes(parseInt(user.getUserId()))) {
this.getUserStorage().updateUserCredits(user.getUserId(), storageUser.credits - 1).then(() => {
// Send the button tap to the mGBA API
this.mgbaHttpClient
.buttonTapCreate({
key: gameKeyInterpretation.toString(),
})
.then(() => {
Logger.logMessage(`Button tap for ${gameKeyInterpretation} created by ${user.getDisplayName()}`, "ChatInteraction");
});
});
} else {
// Send the button tap to the mGBA API
this.mgbaHttpClient
.buttonTapCreate({
key: gameKeyInterpretation.toString(),
})
.then(() => {
Logger.logMessage(`Button tap for ${gameKeyInterpretation} created by ${user.getDisplayName()}`, "ChatInteraction");
});
}
}
});
}
}
/**
* Get the user IDs for the developers
* @returns {number[]} The user IDs for the developers
*/
private getDeveloperUserIds(): number[] {
return [
234457066013368320 // Dennis
]
}
/**
* Handle the gift event from the TikTok Live connection
* @param {any} data The data object for the gift event
*/
private giftHandler(data: any): void {
let giftObject = this.availableGifts.get(data.giftId);
if(giftObject) {
Logger.logMessage(`Gift received: ${giftObject.getName()} from ${data.userId} with value ${giftObject.getCostDiamonds()}`, "ChatInteraction", LogLevel.INFO);
let giftValue = giftObject.getCostDiamonds();
this.getUserStorage().getUser(data.userId).then((storageUser) => {
if(storageUser === undefined) {
this.getUserStorage().createUser(parseInt(data.userId), giftValue + FREE_GAME_INPUT_MOVES);
return;
}
this.getUserStorage().updateUserCredits(data.userId, storageUser.credits + giftValue);
});
}
}
/**
* Get the database storage object for the users
* @returns {UserStorage} The UserStorage object for the GameController
*/
private getUserStorage(): UserStorage {
return this.userStorage;
}
/**
* Start the GameController and connect to the TikTok Live
* @returns {void}
*/
private startGameController(): void {
Logger.logMessage(`Trying to connect to the TikTok Livestram: ${this.channelName}`, "GameController", LogLevel.INFO);
this.getWebcastPushConnection()
.connect()
.then(() => {
Logger.logMessage("Connection successfully established", "WebcastConnection", LogLevel.INFO);
this.webcastPushConnection.on("chat", this.chatHandler.bind(this));
this.webcastPushConnection.on("gift", this.giftHandler.bind(this));
})
.catch((error) => {
Logger.logDebug(`The connection could not be established`, error, "WebcastConnection", LogLevel.ERROR);
});
}
}