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 = new Map(); private webcastPushConnection: WebcastPushConnection; private mgbaHttpClient: MgbaHttp = new MgbaHttp({}); private userStorage: UserStorage; private availableGifts: Map = new Map(); /** * 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> { let returnGifts = new Map(); 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} The Map of users in the TikTok Live */ private getUsers(): Map { 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) => { console.log(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); } // 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()}`, "GameInput"); }); } }); } } /** * 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) { 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); }); } }