adds custom events for the websocket
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import { WebSocket } from "ws";
|
||||
import { DecodedToken } from "./decodedToken";
|
||||
import {WebSocket} from "ws";
|
||||
import {DecodedToken} from "./decodedToken";
|
||||
import {IUser} from "../db/models/user";
|
||||
|
||||
export interface ExtendedWebSocket extends WebSocket {
|
||||
payload: DecodedToken;
|
||||
isAlive: boolean;
|
||||
user:IUser;
|
||||
asyncUpdates?: NodeJS.Timeout
|
||||
payload: DecodedToken;
|
||||
isAlive: boolean;
|
||||
user: IUser;
|
||||
asyncUpdates?: NodeJS.Timeout
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import {ExtendedWebSocket} from "../../../interfaces/extendedWebsocket";
|
||||
|
||||
export abstract class CustomWebsocketEvent {
|
||||
abstract event: string;
|
||||
abstract handler: (data: any) => void;
|
||||
protected ws: ExtendedWebSocket;
|
||||
|
||||
public constructor(ws: ExtendedWebSocket) {
|
||||
this.ws = ws;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import {WebsocketEventType} from "./websocketEventType";
|
||||
import {CustomWebsocketEvent} from "./customWebsocketEvent";
|
||||
import {ExtendedWebSocket} from "../../../interfaces/extendedWebsocket";
|
||||
|
||||
export class ErrorEvent extends CustomWebsocketEvent {
|
||||
event: string = WebsocketEventType.ERROR;
|
||||
|
||||
constructor(ws: ExtendedWebSocket) {
|
||||
super(ws);
|
||||
}
|
||||
|
||||
handler = async (data: any) => {
|
||||
const {message, traceback} = data;
|
||||
console.warn("Error message received", message);
|
||||
console.warn("Traceback", traceback);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import {CustomWebsocketEvent} from "./customWebsocketEvent";
|
||||
import {WebsocketEventType} from "./websocketEventType";
|
||||
|
||||
export class GetSettingsEvent extends CustomWebsocketEvent {
|
||||
|
||||
event = WebsocketEventType.GET_SETTINGS;
|
||||
|
||||
handler = async () => {
|
||||
console.log("Getting settings");
|
||||
this.ws.send(JSON.stringify({
|
||||
type: "SETTINGS",
|
||||
payload: {
|
||||
timezone: this.ws.user.timezone,
|
||||
},
|
||||
}), {binary: false});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import {CustomWebsocketEvent} from "./customWebsocketEvent";
|
||||
import {WebsocketEventType} from "./websocketEventType";
|
||||
import {SpotifyTokenService} from "../../../db/services/spotifyTokenService";
|
||||
import {UserService} from "../../../db/services/db/UserService";
|
||||
import {getCurrentlyPlaying} from "../../../db/services/spotifyApiService";
|
||||
|
||||
export class GetSpotifyUpdatesEvent extends CustomWebsocketEvent {
|
||||
|
||||
event = WebsocketEventType.GET_SPOTIFY_UPDATES;
|
||||
|
||||
handler = async () => {
|
||||
console.log("Starting Spotify updates");
|
||||
// first execute the function once
|
||||
this.spotifyUpdates()
|
||||
.then(() => {
|
||||
// then set the interval
|
||||
this.ws.asyncUpdates = setInterval(() => {
|
||||
this.spotifyUpdates();
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
private async spotifyUpdates() {
|
||||
console.log("Checking Spotify")
|
||||
const user = this.ws.user;
|
||||
const spotifyConfig = user.spotifyConfig;
|
||||
if (!spotifyConfig) {
|
||||
console.log("No Spotify config found");
|
||||
// stop the interval
|
||||
this.ws.emit(WebsocketEventType.STOP_SPOTIFY_UPDATES, {});
|
||||
return;
|
||||
}
|
||||
if (Date.now() > spotifyConfig.expirationDate.getTime()) {
|
||||
console.log("Token expired");
|
||||
|
||||
const token = await new SpotifyTokenService().refreshToken(spotifyConfig.refreshToken);
|
||||
user.spotifyConfig = {
|
||||
// use old refresh token because you don't get a new one
|
||||
refreshToken: user.spotifyConfig!.refreshToken,
|
||||
accessToken: token.access_token,
|
||||
expirationDate: new Date(Date.now() + token.expires_in * 1000),
|
||||
scope: token.scope,
|
||||
};
|
||||
const userService = await UserService.create();
|
||||
await userService.updateUser(user);
|
||||
console.log("Token refreshed and database updated");
|
||||
}
|
||||
const musicData = await getCurrentlyPlaying(user.spotifyConfig!.accessToken);
|
||||
|
||||
this.ws.send(JSON.stringify({
|
||||
type: "SPOTIFY_UPDATE",
|
||||
payload: musicData,
|
||||
}), {binary: false});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import {CustomWebsocketEvent} from "./customWebsocketEvent";
|
||||
import {WebsocketEventType} from "./websocketEventType";
|
||||
|
||||
export class GetStateEvent extends CustomWebsocketEvent {
|
||||
event = WebsocketEventType.GET_STATE;
|
||||
|
||||
handler = async () => {
|
||||
console.log("Getting state");
|
||||
const messageToSend = {
|
||||
type: "STATE",
|
||||
payload: this.ws.user.lastState,
|
||||
};
|
||||
this.ws.send(JSON.stringify(messageToSend), {binary: false});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import {CustomWebsocketEvent} from "./customWebsocketEvent";
|
||||
import {WebsocketEventType} from "./websocketEventType";
|
||||
import {getCurrentWeather} from "../../../db/services/owmApiService";
|
||||
|
||||
export class GetWeatherUpdatesEvent extends CustomWebsocketEvent {
|
||||
|
||||
event = WebsocketEventType.GET_WEATHER_UPDATES;
|
||||
|
||||
handler = async () => {
|
||||
console.log("Starting weather updates");
|
||||
this.weatherUpdates().then(() => {
|
||||
this.ws.asyncUpdates = setInterval(() => {
|
||||
this.weatherUpdates();
|
||||
}, 1000 * 60);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private async weatherUpdates() {
|
||||
console.log("Checking weather")
|
||||
const user = this.ws.user;
|
||||
const weather = await getCurrentWeather(user.location);
|
||||
console.log(weather);
|
||||
|
||||
this.ws.send(JSON.stringify({
|
||||
type: "WEATHER_UPDATE",
|
||||
payload: weather,
|
||||
}), {binary: false});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import {CustomWebsocketEvent} from "./customWebsocketEvent";
|
||||
import {WebsocketEventType} from "./websocketEventType";
|
||||
|
||||
export class StopSpotifyUpdatesEvent extends CustomWebsocketEvent {
|
||||
|
||||
event = WebsocketEventType.STOP_SPOTIFY_UPDATES;
|
||||
|
||||
handler = async () => {
|
||||
if (this.ws.asyncUpdates) {
|
||||
clearInterval(this.ws.asyncUpdates);
|
||||
console.log("Spotify updates stopped");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import {CustomWebsocketEvent} from "./customWebsocketEvent";
|
||||
import {WebsocketEventType} from "./websocketEventType";
|
||||
|
||||
export class StopWeatherUpdatesEvent extends CustomWebsocketEvent {
|
||||
|
||||
event = WebsocketEventType.STOP_WEATHER_UPDATES;
|
||||
|
||||
handler = async () => {
|
||||
if (this.ws.asyncUpdates) {
|
||||
clearInterval(this.ws.asyncUpdates);
|
||||
console.log("Weather updates stopped");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import {WebsocketEventType} from "./websocketEventType";
|
||||
import {CustomWebsocketEvent} from "./customWebsocketEvent";
|
||||
import {IUser} from "../../../db/models/user";
|
||||
|
||||
export class UpdateUserEvent extends CustomWebsocketEvent {
|
||||
event = WebsocketEventType.UPDATE_USER;
|
||||
|
||||
handler = async (data: IUser) => {
|
||||
console.log("Updating user")
|
||||
if (data) {
|
||||
this.ws.user = data;
|
||||
console.log("User updated")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
export enum WebsocketEventType {
|
||||
GET_SETTINGS = "GET_SETTINGS",
|
||||
GET_STATE = "GET_STATE",
|
||||
GET_SPOTIFY_UPDATES = "GET_SPOTIFY_UPDATES",
|
||||
GET_WEATHER_UPDATES = "GET_WEATHER_UPDATES",
|
||||
STOP_SPOTIFY_UPDATES = "STOP_SPOTIFY_UPDATES",
|
||||
STOP_WEATHER_UPDATES = "STOP_WEATHER_UPDATES",
|
||||
ERROR = "ERROR",
|
||||
UPDATE_USER = "UPDATE_USER",
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import {ExtendedWebSocket} from "../../../interfaces/extendedWebsocket";
|
||||
import {GetSettingsEvent} from "./getSettingsEvent";
|
||||
import {ErrorEvent} from "./errorEvent";
|
||||
import {GetSpotifyUpdatesEvent} from "./getSpotifyUpdatesEvent";
|
||||
import {GetStateEvent} from "./getStateEvent";
|
||||
import {GetWeatherUpdatesEvent} from "./getWeatherUpdatesEvent";
|
||||
import {StopSpotifyUpdatesEvent} from "./stopSpotifyUpdatesEvent";
|
||||
import {StopWeatherUpdatesEvent} from "./stopWeatherUpdatesEvent";
|
||||
import {UpdateUserEvent} from "./updateUserEvent";
|
||||
import {CustomWebsocketEvent} from "./customWebsocketEvent";
|
||||
|
||||
export function getEventListeners(ws: ExtendedWebSocket): CustomWebsocketEvent[] {
|
||||
return [
|
||||
new GetSettingsEvent(ws),
|
||||
new ErrorEvent(ws),
|
||||
new GetSpotifyUpdatesEvent(ws),
|
||||
new GetStateEvent(ws),
|
||||
new GetWeatherUpdatesEvent(ws),
|
||||
new StopSpotifyUpdatesEvent(ws),
|
||||
new StopWeatherUpdatesEvent(ws),
|
||||
new UpdateUserEvent(ws)
|
||||
];
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
import {ExtendedWebSocket} from "../../interfaces/extendedWebsocket";
|
||||
import {getCurrentlyPlaying} from "../../db/services/spotifyApiService";
|
||||
import {SpotifyTokenService} from "../../db/services/spotifyTokenService";
|
||||
import {UserService} from "../../db/services/db/UserService";
|
||||
import {getCurrentWeather} from "../../db/services/owmApiService";
|
||||
import {CustomWebsocketEvent} from "./websocketCustomEvents/customWebsocketEvent";
|
||||
|
||||
export class WebsocketEventHandler {
|
||||
constructor(private webSocket: ExtendedWebSocket) {
|
||||
@@ -39,100 +36,15 @@ export class WebsocketEventHandler {
|
||||
const {type} = messageJson;
|
||||
console.log("Received message:", message);
|
||||
|
||||
if (type === "GET_SETTINGS") {
|
||||
this.webSocket.send(JSON.stringify({
|
||||
type: "SETTINGS",
|
||||
payload: {
|
||||
timezone: this.webSocket.user.timezone,
|
||||
},
|
||||
}), {binary: false});
|
||||
}
|
||||
|
||||
if (type === "GET_STATE") {
|
||||
const messageToSend = {
|
||||
type: "STATE",
|
||||
payload: this.webSocket.user.lastState,
|
||||
};
|
||||
this.webSocket.send(JSON.stringify(messageToSend), {binary: false});
|
||||
}
|
||||
|
||||
if (type === "GET_SPOTIFY_UPDATES") {
|
||||
console.log("Starting Spotify updates");
|
||||
// first execute the function once
|
||||
this.spotifyUpdates()
|
||||
.then(() => {
|
||||
// then set the interval
|
||||
this.webSocket.asyncUpdates = setInterval(() => {
|
||||
this.spotifyUpdates();
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
if (type === "GET_WEATHER_UPDATES") {
|
||||
console.log("Starting weather updates");
|
||||
this.weatherUpdates().then(() => {
|
||||
this.webSocket.asyncUpdates = setInterval(() => {
|
||||
this.weatherUpdates();
|
||||
}, 1000 * 60);
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (type === "STOP_SPOTIFY_UPDATES" || type === "STOP_WEATHER_UPDATES") {
|
||||
if (this.webSocket.asyncUpdates) {
|
||||
clearInterval(this.webSocket.asyncUpdates);
|
||||
console.log("Async updates stopped");
|
||||
}
|
||||
}
|
||||
|
||||
if (type === "ERROR") {
|
||||
const {message, traceback} = messageJson;
|
||||
console.warn("Error message received", message);
|
||||
console.warn("Traceback", traceback);
|
||||
}
|
||||
// emit event to the custom event handler
|
||||
this.webSocket.emit(type, messageJson);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private async spotifyUpdates() {
|
||||
console.log("Checking Spotify")
|
||||
const user = this.webSocket.user;
|
||||
const spotifyConfig = user.spotifyConfig;
|
||||
if (!spotifyConfig) {
|
||||
console.log("No Spotify config found");
|
||||
return;
|
||||
}
|
||||
if (Date.now() > spotifyConfig.expirationDate.getTime()) {
|
||||
console.log("Token expired");
|
||||
|
||||
const token = await new SpotifyTokenService().refreshToken(spotifyConfig.refreshToken);
|
||||
user.spotifyConfig = {
|
||||
// use old refresh token because you don't get a new one
|
||||
refreshToken: user.spotifyConfig!.refreshToken,
|
||||
accessToken: token.access_token,
|
||||
expirationDate: new Date(Date.now() + token.expires_in * 1000),
|
||||
scope: token.scope,
|
||||
};
|
||||
const userService = await UserService.create();
|
||||
await userService.updateUser(user);
|
||||
console.log("Token refreshed and database updated");
|
||||
}
|
||||
const musicData = await getCurrentlyPlaying(user.spotifyConfig!.accessToken);
|
||||
|
||||
this.webSocket.send(JSON.stringify({
|
||||
type: "SPOTIFY_UPDATE",
|
||||
payload: musicData,
|
||||
}), {binary: false});
|
||||
public registerCustomEvent(customWebsocketEvent:CustomWebsocketEvent ) {
|
||||
// bind needed?
|
||||
this.webSocket.on(customWebsocketEvent.event, customWebsocketEvent.handler.bind(customWebsocketEvent));
|
||||
}
|
||||
|
||||
private async weatherUpdates() {
|
||||
console.log("Checking weather")
|
||||
const user = this.webSocket.user;
|
||||
const weather = await getCurrentWeather(user.location);
|
||||
console.log(weather);
|
||||
|
||||
this.webSocket.send(JSON.stringify({
|
||||
type: "WEATHER_UPDATE",
|
||||
payload: weather,
|
||||
}), {binary: false});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export class WebsocketServerEventHandler {
|
||||
this.webSocketServer.on(
|
||||
"connection",
|
||||
async (ws: ExtendedWebSocket, request: ExtendedIncomingMessage) => {
|
||||
const user = await (await UserService.create()).getUserByUUID(request.payload.id);
|
||||
const user = await (await UserService.create()).getUserByUUID(request.payload.uuid);
|
||||
|
||||
ws.user = user!;
|
||||
|
||||
|
||||
+13
-5
@@ -6,6 +6,7 @@ import {DecodedToken} from "./interfaces/decodedToken";
|
||||
import {WebsocketServerEventHandler} from "./utils/websocket/websocketServerEventHandler";
|
||||
import {WebsocketEventHandler} from "./utils/websocket/websocketEventHandler";
|
||||
import {UserService} from "./db/services/db/UserService";
|
||||
import {getEventListeners} from "./utils/websocket/websocketCustomEvents/websocketEventUtils";
|
||||
|
||||
export class ExtendedWebSocketServer {
|
||||
private readonly _wss: WebSocketServer;
|
||||
@@ -59,19 +60,26 @@ export class ExtendedWebSocketServer {
|
||||
socketEventHandler.enablePongEvent();
|
||||
socketEventHandler.enableMessageEvent();
|
||||
|
||||
// Register custom events
|
||||
getEventListeners(ws).forEach((eventListener) => {
|
||||
socketEventHandler.registerCustomEvent(eventListener);
|
||||
});
|
||||
|
||||
const updateUserInterval = setInterval(async () => {
|
||||
console.log("Updating user")
|
||||
const userService = await UserService.create();
|
||||
const user = await userService.getUserByUUID(ws.payload.id);
|
||||
if (user) {
|
||||
ws.user = user;
|
||||
}
|
||||
const user = await userService.getUserByUUID(ws.payload.uuid);
|
||||
ws.emit("UPDATE_USER", user);
|
||||
}, 15000);
|
||||
|
||||
socketEventHandler.enableDisconnectEvent(() => {
|
||||
clearInterval(updateUserInterval);
|
||||
console.log("stopped updating user");
|
||||
});
|
||||
|
||||
// send initial state and settings
|
||||
// think about emitting the data needed directly to the event Handler
|
||||
ws.emit("GET_STATE", {});
|
||||
ws.emit("GET_SETTINGS", {});
|
||||
});
|
||||
|
||||
const interval = serverEventHandler.enableHeartbeat(30000);
|
||||
|
||||
Reference in New Issue
Block a user