add spotify logic to send to the websocket the currently music data
This commit is contained in:
+27
-1
@@ -9,7 +9,8 @@ export interface IUser {
|
||||
uuid: string,
|
||||
id: ObjectId,
|
||||
config: UserConfig,
|
||||
lastState: MatrixState
|
||||
lastState: MatrixState,
|
||||
spotifyConfig: SpotifyConfig
|
||||
}
|
||||
|
||||
export interface UserConfig {
|
||||
@@ -41,6 +42,13 @@ export interface MatrixState {
|
||||
};
|
||||
}
|
||||
|
||||
export interface SpotifyConfig {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
expirationDate: Date;
|
||||
scope: string;
|
||||
}
|
||||
|
||||
const userSchema = new mongoose.Schema<IUser>({
|
||||
name: {
|
||||
type: String,
|
||||
@@ -116,6 +124,24 @@ const userSchema = new mongoose.Schema<IUser>({
|
||||
}
|
||||
}
|
||||
},
|
||||
spotifyConfig: {
|
||||
accessToken: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
refreshToken: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
expirationDate: {
|
||||
type: Date,
|
||||
required: true,
|
||||
},
|
||||
scope: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -15,10 +15,15 @@ export class UserService {
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
public async updateUser(id: string, user: Partial<IUser>): Promise<IUser | null> {
|
||||
public async updateUserById(id: string, user: Partial<IUser>): Promise<IUser | null> {
|
||||
return await UserModel.findByIdAndUpdate(id, user, {new: true}).exec();
|
||||
}
|
||||
|
||||
public async updateUser(user: IUser): Promise<IUser | null> {
|
||||
const {id, ...rest} = user;
|
||||
return this.updateUserById(id.toString(), rest);
|
||||
}
|
||||
|
||||
public async getAllUsers(): Promise<IUser[]> {
|
||||
return await UserModel.find().exec();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import axios from "axios";
|
||||
|
||||
export interface CurrentlyPlaying {
|
||||
timestamp: number;
|
||||
context?: {
|
||||
type: string;
|
||||
uri: string;
|
||||
};
|
||||
progress_ms?: number;
|
||||
item?: {
|
||||
name: string;
|
||||
artists: {
|
||||
name: string;
|
||||
uri: string;
|
||||
}[];
|
||||
album: {
|
||||
name: string;
|
||||
uri: string;
|
||||
};
|
||||
duration_ms: number;
|
||||
};
|
||||
is_playing: boolean;
|
||||
}
|
||||
|
||||
|
||||
export async function getCurrentlyPlaying(accessToken: string) {
|
||||
try {
|
||||
const response = await axios.get<CurrentlyPlaying>("https://api.spotify.com/v1/me/player/currently-playing", {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`
|
||||
}, params: {
|
||||
additional_types: "episode"
|
||||
}
|
||||
});
|
||||
|
||||
if (response.status === 204) {
|
||||
console.log("Es wird gerade nichts abgespielt.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error("Fehler bei der Anfrage:", error.response?.status, error.response?.data);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ const clientId = process.env.SPOTIFY_CLIENT_ID;
|
||||
const clientSecret = process.env.SPOTIFY_CLIENT_SECRET;
|
||||
|
||||
export class SpotifyTokenService {
|
||||
|
||||
public async refreshToken(refreshToken: string) {
|
||||
console.log("refreshToken")
|
||||
const response = await axios.post(
|
||||
|
||||
@@ -6,4 +6,5 @@ export interface ExtendedWebSocket extends WebSocket {
|
||||
payload: DecodedToken;
|
||||
isAlive: boolean;
|
||||
user:IUser;
|
||||
spotifyUpdate?: NodeJS.Timeout
|
||||
}
|
||||
|
||||
@@ -25,10 +25,11 @@ export class RestUser {
|
||||
});
|
||||
|
||||
router.put("/:id", async (req, res) => {
|
||||
console.log("PUT /user/:id")
|
||||
const userService = await UserService.create();
|
||||
const id = req.params.id;
|
||||
const user = req.body as IUser;
|
||||
const result = await userService.updateUser(id, user);
|
||||
const result = await userService.updateUserById(id, user);
|
||||
|
||||
result
|
||||
? res.status(200).send(result)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import express from "express";
|
||||
import {SpotifyTokenService} from "../db/services/spotifyTokenService";
|
||||
import {UserService} from "../db/services/db/UserService";
|
||||
|
||||
export class SpotifyTokenGenerator {
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import {ExtendedWebSocket} from "../../interfaces/extendedWebsocket";
|
||||
import {getCurrentlyPlaying} from "../../db/services/spotifyApiService";
|
||||
import {SpotifyTokenService} from "../../db/services/spotifyTokenService";
|
||||
import {UserService} from "../../db/services/db/UserService";
|
||||
|
||||
export class WebsocketEventHandler {
|
||||
constructor(private webSocket: ExtendedWebSocket) {
|
||||
@@ -20,17 +23,68 @@ export class WebsocketEventHandler {
|
||||
this.webSocket.onclose = (event) => {
|
||||
console.log("WebSocket closed:", event.code, event.reason, event.wasClean, event.type);
|
||||
console.log(`User: ${this.webSocket.payload.name} disconnected`);
|
||||
clearInterval(this.webSocket.spotifyUpdate);
|
||||
};
|
||||
}
|
||||
|
||||
public enableMessageEvent() {
|
||||
this.webSocket.on("message", (data) => {
|
||||
const message = data.toString();
|
||||
console.log("Received message:", message);
|
||||
const message = data.toString();
|
||||
console.log("Received message:", message);
|
||||
|
||||
if (message === "GET_STATE") {
|
||||
this.webSocket.send(JSON.stringify(this.webSocket.user.lastState), {binary: false});
|
||||
if (message === "GET_STATE") {
|
||||
const messageToSend = {
|
||||
type: "STATE",
|
||||
payload: this.webSocket.user.lastState,
|
||||
};
|
||||
this.webSocket.send(JSON.stringify(messageToSend), {binary: false});
|
||||
}
|
||||
|
||||
if (message === "GET_SPOTIFY_UPDATES") {
|
||||
console.log("Starting Spotify updates");
|
||||
// first execute the function once
|
||||
this.spotifyUpdates()
|
||||
.then(() => {
|
||||
// then set the interval
|
||||
this.webSocket.spotifyUpdate = setInterval(() => {
|
||||
this.spotifyUpdates();
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
if (message === "STOP_SPOTIFY_UPDATES") {
|
||||
if (this.webSocket.spotifyUpdate) {
|
||||
clearInterval(this.webSocket.spotifyUpdate);
|
||||
console.log("Spotify updates stopped");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
private async spotifyUpdates() {
|
||||
console.log("Checking Spotify")
|
||||
const user = this.webSocket.user;
|
||||
const spotifyConfig = user.spotifyConfig;
|
||||
if (Date.now() > spotifyConfig.expirationDate.getTime()) {
|
||||
console.log("Token expired");
|
||||
|
||||
const token = await new SpotifyTokenService().refreshToken(spotifyConfig.refreshToken);
|
||||
user.spotifyConfig = {
|
||||
accessToken: token.access_token,
|
||||
refreshToken: token.refresh_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});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export class WebsocketServerEventHandler {
|
||||
this.webSocketServer.on(
|
||||
"connection",
|
||||
async (ws: ExtendedWebSocket, request: ExtendedIncomingMessage) => {
|
||||
const user = await (await UserService.create()).getUserByUUID(ws.payload._id);
|
||||
const user = await (await UserService.create()).getUserByUUID(request.payload._id);
|
||||
|
||||
ws.user = user!;
|
||||
|
||||
|
||||
+55
-55
@@ -1,68 +1,68 @@
|
||||
import { Server } from "http";
|
||||
import { WebSocket, Server as WebSocketServer } from "ws";
|
||||
import { verifyClient } from "./utils/verifyClient";
|
||||
import { ExtendedWebSocket } from "./interfaces/extendedWebsocket";
|
||||
import { DecodedToken } from "./interfaces/decodedToken";
|
||||
import { WebsocketServerEventHandler } from "./utils/websocket/websocketServerEventHandler";
|
||||
import { WebsocketEventHandler } from "./utils/websocket/websocketEventHandler";
|
||||
import {Server} from "http";
|
||||
import {Server as WebSocketServer, WebSocket} from "ws";
|
||||
import {verifyClient} from "./utils/verifyClient";
|
||||
import {ExtendedWebSocket} from "./interfaces/extendedWebsocket";
|
||||
import {DecodedToken} from "./interfaces/decodedToken";
|
||||
import {WebsocketServerEventHandler} from "./utils/websocket/websocketServerEventHandler";
|
||||
import {WebsocketEventHandler} from "./utils/websocket/websocketEventHandler";
|
||||
|
||||
export class ExtendedWebSocketServer {
|
||||
private readonly _wss: WebSocketServer;
|
||||
private readonly _wss: WebSocketServer;
|
||||
|
||||
constructor(server: Server) {
|
||||
this._wss = new WebSocketServer({
|
||||
server,
|
||||
verifyClient: (info, callback) => verifyClient(info.req, callback),
|
||||
});
|
||||
constructor(server: Server) {
|
||||
this._wss = new WebSocketServer({
|
||||
server,
|
||||
verifyClient: (info, callback) => verifyClient(info.req, callback),
|
||||
});
|
||||
|
||||
this.setupWebSocket();
|
||||
}
|
||||
this.setupWebSocket();
|
||||
}
|
||||
|
||||
private setupWebSocket() {
|
||||
const serverEventHandler = new WebsocketServerEventHandler(this.wss);
|
||||
serverEventHandler.enableConnectionEvent((ws) => {
|
||||
const socketEventHandler = new WebsocketEventHandler(ws);
|
||||
private get wss(): WebSocketServer {
|
||||
return this._wss;
|
||||
}
|
||||
|
||||
console.log("WebSocket client connected");
|
||||
public broadcast(message: string) {
|
||||
this.wss.clients.forEach((client) => {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
client.send(message, {binary: false});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
socketEventHandler.enableErrorEvent();
|
||||
socketEventHandler.enableDisconnectEvent();
|
||||
socketEventHandler.enablePongEvent();
|
||||
socketEventHandler.enableMessageEvent();
|
||||
});
|
||||
public sendMessageToUser(_id: string, message: string) {
|
||||
this.wss.clients.forEach(
|
||||
(client: WebSocket & { payload?: DecodedToken }) => {
|
||||
if (
|
||||
client.payload?._id === _id &&
|
||||
client.readyState === WebSocket.OPEN
|
||||
) {
|
||||
client.send(message, {binary: false});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const interval = serverEventHandler.enableHeartbeat(30000);
|
||||
serverEventHandler.enableCloseEvent(() => {
|
||||
clearInterval(interval);
|
||||
});
|
||||
}
|
||||
public getConnectedClients(): Set<ExtendedWebSocket> {
|
||||
return this.wss.clients as Set<ExtendedWebSocket>;
|
||||
}
|
||||
|
||||
public broadcast(message: string) {
|
||||
this.wss.clients.forEach((client) => {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
client.send(message, { binary: false });
|
||||
}
|
||||
});
|
||||
}
|
||||
private setupWebSocket() {
|
||||
const serverEventHandler = new WebsocketServerEventHandler(this.wss);
|
||||
serverEventHandler.enableConnectionEvent((ws) => {
|
||||
const socketEventHandler = new WebsocketEventHandler(ws);
|
||||
|
||||
public sendMessageToUser(_id: string, message: string) {
|
||||
this.wss.clients.forEach(
|
||||
(client: WebSocket & { payload?: DecodedToken }) => {
|
||||
if (
|
||||
client.payload?._id === _id &&
|
||||
client.readyState === WebSocket.OPEN
|
||||
) {
|
||||
client.send(message, { binary: false });
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
console.log("WebSocket client connected");
|
||||
|
||||
public getConnectedClients(): Set<ExtendedWebSocket> {
|
||||
return this.wss.clients as Set<ExtendedWebSocket>;
|
||||
}
|
||||
socketEventHandler.enableErrorEvent();
|
||||
socketEventHandler.enableDisconnectEvent();
|
||||
socketEventHandler.enablePongEvent();
|
||||
socketEventHandler.enableMessageEvent();
|
||||
});
|
||||
|
||||
private get wss(): WebSocketServer {
|
||||
return this._wss;
|
||||
}
|
||||
const interval = serverEventHandler.enableHeartbeat(30000);
|
||||
serverEventHandler.enableCloseEvent(() => {
|
||||
clearInterval(interval);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user