1
0

🎨 Format code

This commit is contained in:
2021-06-20 00:51:50 +02:00
parent 6735ee5519
commit 71f7546165
6 changed files with 295 additions and 253 deletions

View File

@@ -7,10 +7,10 @@ import { start } from "./scheduledActions";
let chatClient: ChatClient;
export {
chatClient,
connect,
handleClientAction,
say
chatClient,
connect,
handleClientAction,
say
};
// TODO: clean/refactor code
@@ -18,76 +18,88 @@ export {
const LOG_PREFIX = "[ChatClient] ";
async function connect(channels: Array<any>): Promise<void> {
const authProvider = await getAuthProvider();
const authProvider = await getAuthProvider();
if (
if (
chatClient &&
(
chatClient.isConnecting ||
chatClient.isConnected
chatClient.isConnecting ||
chatClient.isConnected
)
) {
return;
}
return;
}
chatClient = new ChatClient(authProvider, { channels: channels });
chatClient = new ChatClient(authProvider, { channels: channels });
chatClient.onConnect(onConnect);
chatClient.onConnect(onConnect);
chatClient.onDisconnect((e: any) => {
console.log(`${LOG_PREFIX}Disconnected ${e.message}`);
});
chatClient.onDisconnect((e: any) => {
console.log(`${LOG_PREFIX}Disconnected ${e.message}`);
});
chatClient.onNoPermission((channel, message) => {
console.log(`${LOG_PREFIX}No permission on ${channel}: ${message}`);
});
chatClient.onNoPermission((channel, message) => {
console.log(`${LOG_PREFIX}No permission on ${channel}: ${message}`);
});
await chatClient.connect();
await chatClient.connect();
}
async function onConnect(): Promise<void> {
console.log(`${LOG_PREFIX}Connected`);
console.log(`${LOG_PREFIX}Connected`);
start();
start();
}
async function handleClientAction(action: any): Promise<void> {
if (action.channel && !isNaN(action.channel)) {
action.channel = await getUsernameFromId(parseInt(action.channel));
}
if (action.username && !isNaN(action.username)) {
action.username = await getUsernameFromId(parseInt(action.username));
}
if (
action.channel &&
!isNaN(action.channel)
) {
action.channel = await getUsernameFromId(parseInt(action.channel));
}
// TODO: create a interface for action messages
if (!action.channel) {
action.channel = "alexbcberio";
}
if (
action.username &&
!isNaN(action.username)
) {
action.username = await getUsernameFromId(parseInt(action.username));
}
// TODO: create a interface for action messages
if (!action.channel) {
action.channel = "alexbcberio";
}
switch (action.action) {
case "say":
say(action.channel, action.message);
break;
case "timeout":
await timeout(action.channel, action.username, action.time, action.reason);
break;
case "broadcast":
broadcast(action.message);
break;
case "addVip":
await addVip(action.channel, action.username);
break;
case "removeVip":
await removeVip(action.channel, action.username);
break;
default:
console.log(`${[LOG_PREFIX]}Couldn't handle action:`, action);
}
case "say":
say(action.channel, action.message);
break;
case "timeout":
await timeout(
action.channel,
action.username,
action.time,
action.reason
);
break;
case "broadcast":
broadcast(action.message);
break;
case "addVip":
await addVip(action.channel, action.username);
break;
case "removeVip":
await removeVip(action.channel, action.username);
break;
default:
console.log(`${[LOG_PREFIX]}Couldn't handle action:`, action);
}
}
// send a chat message
async function say(channel: string, message: string): Promise<void> {
await chatClient.say(channel, message);
await chatClient.say(channel, message);
}
// timeouts a user in a channel
@@ -97,37 +109,45 @@ async function timeout(
time?: number,
reason?: string
): Promise<void> {
if (!time) {
time = 60;
}
if (!time) {
time = 60;
}
if (!reason) {
reason = "";
}
if (!reason) {
reason = "";
}
try {
await chatClient.timeout(channel, username, time, reason);
} catch (e) {
// user cannot be timed out
}
try {
await chatClient.timeout(channel, username, time, reason);
} catch (e) {
// user cannot be timed out
}
}
// adds a user to vips
async function addVip(channel: string, username: string, message?: string): Promise<void> {
if (!message) {
message = `Otorgado VIP a @${username}.`;
}
async function addVip(
channel: string,
username: string,
message?: string
): Promise<void> {
if (!message) {
message = `Otorgado VIP a @${username}.`;
}
await chatClient.addVip(channel, username);
say(channel, message);
await chatClient.addVip(channel, username);
say(channel, message);
}
// removes a user from vips
async function removeVip(channel: string, username: string, message?: string): Promise<void> {
if (!message) {
message = `VIP de @${username} eliminado.`;
}
async function removeVip(
channel: string,
username: string,
message?: string
): Promise<void> {
if (!message) {
message = `VIP de @${username} eliminado.`;
}
await chatClient.removeVip(channel, username);
say(channel, message);
}
await chatClient.removeVip(channel, username);
say(channel, message);
}

View File

@@ -7,59 +7,59 @@ import { UserIdResolvable } from "twitch";
import { broadcast } from "./webServer";
export {
registerUserListener
}
registerUserListener
};
// TODO: clean/refactor code
const LOG_PREFIX = "[PubSub] ";
async function registerUserListener(user: UserIdResolvable) {
const apiClient = await getApiClient();
const apiClient = await getApiClient();
const pubSubClient = new PubSubClient();
const userId = await pubSubClient.registerUserListener(apiClient, user);
const pubSubClient = new PubSubClient();
const userId = await pubSubClient.registerUserListener(apiClient, user);
/*const listener = */ await pubSubClient.onRedemption(userId, onRedemption);
console.log(`${LOG_PREFIX}Connected & registered`);
console.log(`${LOG_PREFIX}Connected & registered`);
}
async function onRedemption(message: PubSubRedemptionMessage) {
console.log(
`${LOG_PREFIX}Reward: "${message.rewardName}" (${message.rewardId}) redeemed by ${message.userDisplayName}`
);
// @ts-ignore
const reward = message._data.data.redemption.reward;
// @ts-ignore
const reward = message._data.data.redemption.reward;
const msg = {
id: message.id,
channelId: message.channelId,
rewardId: message.rewardId,
rewardName: message.rewardName,
const msg = {
id: message.id,
channelId: message.channelId,
rewardId: message.rewardId,
rewardName: message.rewardName,
rewardImage: message.rewardImage
? message.rewardImage.url_4x
: "https://static-cdn.jtvnw.net/custom-reward-images/default-4.png",
message: message.message,
userDisplayName: message.userDisplayName,
// non directly available values from PubSubRedemptionMessage
backgroundColor: reward.background_color
};
message: message.message,
userDisplayName: message.userDisplayName,
// non directly available values from PubSubRedemptionMessage
backgroundColor: reward.background_color
};
switch (msg.rewardId) {
// robar vip
case "ac750bd6-fb4c-4259-b06d-56953601243b":
if (await stealVip(msg)) {
msg.message = `@${msg.userDisplayName} ha robado el VIP a @${msg.message}.`;
// robar vip
case "ac750bd6-fb4c-4259-b06d-56953601243b":
if (await stealVip(msg)) {
msg.message = `@${msg.userDisplayName} ha robado el VIP a @${msg.message}.`;
broadcast(JSON.stringify(msg));
}
break;
default:
console.log(LOG_PREFIX, msg);
broadcast(JSON.stringify(msg));
}
break;
default:
console.log(LOG_PREFIX, msg);
broadcast(JSON.stringify(msg));
break;
}
broadcast(JSON.stringify(msg));
break;
}
}
// TODO: extract methods
@@ -70,61 +70,69 @@ async function stealVip(msg: {
userDisplayName: string;
message: string;
}): Promise<boolean> {
const channel = await getUsernameFromId(parseInt(msg.channelId));
const channel = await getUsernameFromId(parseInt(msg.channelId));
if (!channel) {
console.log(`${LOG_PREFIX}No channel found`);
if (!channel) {
console.log(`${LOG_PREFIX}No channel found`);
return false;
}
return false;
}
const addVipUser = msg.userDisplayName;
const removeVipUser = msg.message;
const addVipUser = msg.userDisplayName;
const removeVipUser = msg.message;
if (await hasVip(channel, removeVipUser)) {
await removeVip(channel, removeVipUser);
await addVip(channel, addVipUser);
if (await hasVip(channel, removeVipUser)) {
await removeVip(channel, removeVipUser);
await addVip(channel, addVipUser);
const scheduledRemoveVipIndex = scheduledActions.findIndex(
s => s.action === "removeVip" && s.username === removeVipUser
);
if (scheduledRemoveVipIndex > -1) {
scheduledActions[scheduledRemoveVipIndex].username = addVipUser;
saveScheduledActions();
}
if (scheduledRemoveVipIndex > -1) {
scheduledActions[scheduledRemoveVipIndex].username = addVipUser;
saveScheduledActions();
}
return true;
}
return true;
}
return false;
return false;
}
// adds a user to vips
async function addVip(channel: string, username: string, message?: string): Promise<void> {
if (!message) {
message = `Otorgado VIP a @${username}.`;
}
async function addVip(
channel: string,
username: string,
message?: string
): Promise<void> {
if (!message) {
message = `Otorgado VIP a @${username}.`;
}
await chatClient.addVip(channel, username);
say(channel, message);
await chatClient.addVip(channel, username);
say(channel, message);
}
async function hasVip(channel: string, username: string): Promise<boolean> {
if (!username) {
return false;
}
if (!username) {
return false;
}
const vips = await chatClient.getVips(channel);
return vips.includes(username);
const vips = await chatClient.getVips(channel);
return vips.includes(username);
}
// removes a user from vips
async function removeVip(channel: string, username: string, message?: string): Promise<void> {
if (!message) {
message = `Se ha acabado el chollo, VIP de @${username} eliminado.`;
}
async function removeVip(
channel: string,
username: string,
message?: string
): Promise<void> {
if (!message) {
message = `Se ha acabado el chollo, VIP de @${username} eliminado.`;
}
await chatClient.removeVip(channel, username);
say(channel, message);
}
await chatClient.removeVip(channel, username);
say(channel, message);
}

View File

@@ -3,10 +3,10 @@ import { handleClientAction } from "./chatClient";
import { resolve } from "path";
export {
start,
scheduledActions,
checkScheduledActions,
saveScheduledActions
start,
scheduledActions,
checkScheduledActions,
saveScheduledActions
};
const LOG_PREFIX = "[Scheduled] ";
@@ -44,7 +44,7 @@ async function start(): Promise<void> {
async function checkScheduledActions(): Promise<void> {
if (checkingScheduled) {
return;
};
}
checkingScheduled = true;

View File

@@ -1,5 +1,5 @@
import { TokenData } from "../../interfaces/TokenData";
import {promises as fs} from "fs";
import { promises as fs } from "fs";
import { resolve } from "path";
const TOKENS_FILE = "tokens.json";
@@ -8,44 +8,45 @@ const LOG_PREFIX = "[TokenData] ";
export {
getTokenData,
saveTokenData
}
};
function getTokenDataFilePath(): string {
return resolve(process.cwd(), TOKENS_FILE);
return resolve(process.cwd(), TOKENS_FILE);
}
async function getTokenData(): Promise<TokenData> {
const tokenDataFilePath = getTokenDataFilePath();
let buffer: Buffer;
const tokenDataFilePath = getTokenDataFilePath();
let buffer: Buffer;
try {
buffer = await fs.readFile(tokenDataFilePath);
} catch (e) {
console.error(`${LOG_PREFIX}${TOKENS_FILE} not found on ${tokenDataFilePath}.`);
process.exit(1);
}
try {
buffer = await fs.readFile(tokenDataFilePath);
} catch (e) {
console.error(
`${LOG_PREFIX}${TOKENS_FILE} not found on ${tokenDataFilePath}.`
);
process.exit(1);
}
const tokenData = await JSON.parse(buffer.toString());
const tokenData = await JSON.parse(buffer.toString());
checkTokenData(tokenData);
checkTokenData(tokenData);
return tokenData;
return tokenData;
}
async function saveTokenData(tokenData: TokenData): Promise<void> {
const tokenDataFilePath = getTokenDataFilePath();
const jsonTokenData = JSON.stringify(tokenData);
const tokenDataFilePath = getTokenDataFilePath();
const jsonTokenData = JSON.stringify(tokenData);
await fs.writeFile(tokenDataFilePath, jsonTokenData);
console.log(`${LOG_PREFIX}Token data saved`);
await fs.writeFile(tokenDataFilePath, jsonTokenData);
console.log(`${LOG_PREFIX}Token data saved`);
}
function checkTokenData(tokenData: TokenData): void {
if (
!tokenData.access_token ||
!tokenData.refresh_token
) {
console.error(`${LOG_PREFIX}Missing refresh_token or access_token in ${TOKENS_FILE}.`);
process.exit(1);
}
}
if (!tokenData.access_token || !tokenData.refresh_token) {
console.error(
`${LOG_PREFIX}Missing refresh_token or access_token in ${TOKENS_FILE}.`
);
process.exit(1);
}
}

View File

@@ -1,4 +1,8 @@
import { AccessToken, RefreshableAuthProvider, StaticAuthProvider } from "twitch-auth";
import {
AccessToken,
RefreshableAuthProvider,
StaticAuthProvider
} from "twitch-auth";
import { getTokenData, saveTokenData } from "./tokenData";
import { ApiClient } from "twitch";
@@ -13,92 +17,91 @@ export {
getAuthProvider,
getApiClient,
getUsernameFromId
}
};
function getClientCredentials(): ClientCredentials {
if (
!process.env.TWITCH_CLIENT_ID ||
!process.env.TWITCH_CLIENT_SECRET
) {
if (
!process.env.TWITCH_CLIENT_ID ||
!process.env.TWITCH_CLIENT_SECRET
) {
console.error(
`${LOG_PREFIX}Missing environment parameters TWITCH_CLIENT_ID or TWITCH_CLIENT_SECRET`
);
process.exit(1);
}
process.exit(1);
}
return {
clientId: process.env.TWITCH_CLIENT_ID,
clientSecret: process.env.TWITCH_CLIENT_SECRET
};
return {
clientId: process.env.TWITCH_CLIENT_ID,
clientSecret: process.env.TWITCH_CLIENT_SECRET
};
}
async function createStaticAuthProvider(): Promise<StaticAuthProvider> {
let tokenData = await getTokenData();
const credentials = getClientCredentials();
let tokenData = await getTokenData();
const credentials = getClientCredentials();
return new StaticAuthProvider(
credentials.clientId,
tokenData.access_token
);
return new StaticAuthProvider(credentials.clientId, tokenData.access_token);
}
async function getAuthProvider(): Promise<RefreshableAuthProvider> {
if (refreshAuthProvider) {
return refreshAuthProvider;
}
if (refreshAuthProvider) {
return refreshAuthProvider;
}
let tokenData = await getTokenData();
let tokenData = await getTokenData();
const staticAuthProvider = await createStaticAuthProvider();
const credentials = getClientCredentials();
const staticAuthProvider = await createStaticAuthProvider();
const credentials = getClientCredentials();
const expiry = tokenData.expiryTimestamp === null
? null
: new Date(tokenData.expiryTimestamp);
const expiry =
tokenData.expiryTimestamp === null
? null
: new Date(tokenData.expiryTimestamp);
refreshAuthProvider = new RefreshableAuthProvider(
staticAuthProvider,
{
clientSecret: credentials.clientSecret,
refreshToken: tokenData.refresh_token,
expiry,
onRefresh: onRefresh
}
);
refreshAuthProvider = new RefreshableAuthProvider(staticAuthProvider, {
clientSecret: credentials.clientSecret,
refreshToken: tokenData.refresh_token,
expiry,
onRefresh: onRefresh
});
return refreshAuthProvider;
return refreshAuthProvider;
}
async function onRefresh(refreshData: AccessToken): Promise<void> {
const { accessToken, refreshToken, expiryDate } = refreshData;
console.log(`${LOG_PREFIX}Tokens refreshed`);
const {
accessToken,
refreshToken,
expiryDate
} = refreshData;
console.log(`${LOG_PREFIX}Tokens refreshed`);
const expiryTimestamp = expiryDate === null
? 0
: expiryDate.getTime()
const expiryTimestamp = expiryDate === null
? 0
: expiryDate.getTime();
const newTokenData: TokenData = {
access_token: accessToken,
refresh_token: refreshToken,
expiryTimestamp
};
const newTokenData: TokenData = {
access_token: accessToken,
refresh_token: refreshToken,
expiryTimestamp
};
await saveTokenData(newTokenData);
await saveTokenData(newTokenData);
}
async function getApiClient(): Promise<ApiClient> {
const authProvider = await getAuthProvider();
const authProvider = await getAuthProvider();
return await new ApiClient({ authProvider });
return await new ApiClient({ authProvider });
}
async function getUsernameFromId(userId: number): Promise<string | null> {
const apiClient = await getApiClient();
const user = await apiClient.helix.users.getUserById(userId);
const apiClient = await getApiClient();
const user = await apiClient.helix.users.getUserById(userId);
if (!user) {
return null;
}
if (!user) {
return null;
}
return user.displayName;
}
return user.displayName;
}

View File

@@ -22,40 +22,44 @@ const wsServer = new WebSocket.Server({
let server: Server;
export {
listen,
listen,
broadcast
}
};
wsServer.on("connection", onConnection);
app.use(express.static(join(process.cwd(), "client")));
function listen() {
if (server) {
console.log(`${LOG_PREFIX_HTTP}Server is already running`);
return;
}
if (server) {
console.log(`${LOG_PREFIX_HTTP}Server is already running`);
return;
}
server = app.listen(!isDevelopment ? 8080 : 8081, "0.0.0.0");
server = app.listen(!isDevelopment ? 8080 : 8081, "0.0.0.0");
server.on("listening", onListening);
server.on("upgrade", onUpgrade);
server.on("listening", onListening);
server.on("upgrade", onUpgrade);
}
function onListening() {
console.log(
`${LOG_PREFIX_HTTP}Listening on port ${(server.address() as AddressInfo).port}`
);
console.log(
`${LOG_PREFIX_HTTP}Listening on port ${
(server.address() as AddressInfo).port
}`
);
}
function onUpgrade(req: IncomingMessage, socket: Socket, head: Buffer) {
wsServer.handleUpgrade(req, socket, head, socket => {
wsServer.emit("connection", socket, req);
});
wsServer.handleUpgrade(req, socket, head, socket => {
wsServer.emit("connection", socket, req);
});
}
function onConnection(socket: WebSocket, req: IncomingMessage) {
console.log(`${LOG_PREFIX_WS}${req.socket.remoteAddress} New connection established`);
console.log(
`${LOG_PREFIX_WS}${req.socket.remoteAddress} New connection established`
);
sockets.push(socket);
socket.send(
JSON.stringify({
@@ -69,11 +73,11 @@ function onConnection(socket: WebSocket, req: IncomingMessage) {
// broadcast a message to all clients
function broadcast(msg: string, socket?: any) {
const filteredSockets = socket
? sockets.filter(s => s !== socket)
: sockets;
const filteredSockets = socket
? sockets.filter(s => s !== socket)
: sockets;
filteredSockets.forEach(s => s.send(msg));
filteredSockets.forEach(s => s.send(msg));
}
async function onMessage(msg: string) {
@@ -81,7 +85,10 @@ async function onMessage(msg: string) {
const socket = this as WebSocket;
const data = JSON.parse(msg);
if (!data.actions || data.actions.length === 0) {
if (
!data.actions ||
data.actions.length === 0
) {
broadcast(msg, socket);
return;
}
@@ -96,14 +103,17 @@ async function onMessage(msg: string) {
}
}
console.log(`${LOG_PREFIX_WS}Received message with ${data.actions.length} actions:`, data);
console.log(
`${LOG_PREFIX_WS}Received message with ${data.actions.length} actions:`,
data
);
}
function onClose() {
// @ts-ignore
const socket: WebSocket = this as WebSocket;
const socketIdx = sockets.indexOf(socket);
sockets.splice(socketIdx, 1);
console.log(`${LOG_PREFIX_WS}Connection closed`);
}
const socketIdx = sockets.indexOf(socket);
sockets.splice(socketIdx, 1);
console.log(`${LOG_PREFIX_WS}Connection closed`);
}