|
|
|
|
const WebSocket = require("ws");
|
|
|
|
|
const { Client } = require("node-osc");
|
|
|
|
|
|
|
|
|
|
const ws = new WebSocket(
|
|
|
|
|
"wss://relaxed-werewolf.reticulum.io/socket/websocket?vsn=2.0.0"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const STATE_STARTING = "starting";
|
|
|
|
|
const STATE_JOINING = "joining";
|
|
|
|
|
const STATE_CONNECTED = "connected";
|
|
|
|
|
|
|
|
|
|
let state = STATE_STARTING;
|
|
|
|
|
let hubId = "qPxWfFA";
|
|
|
|
|
let fullHubId = `hub:${hubId}`;
|
|
|
|
|
let receiveId = 1;
|
|
|
|
|
let sendId = 1;
|
|
|
|
|
let botSessionId;
|
|
|
|
|
let vapidPublicKey;
|
|
|
|
|
let avatarId = "qPxWfFA";
|
|
|
|
|
let displayName = "000";
|
|
|
|
|
// Members keyed by session ID.
|
|
|
|
|
let members = {};
|
|
|
|
|
let chatStates = {};
|
|
|
|
|
|
|
|
|
|
const oscClient = new Client("127.0.0.1", 3333);
|
|
|
|
|
|
|
|
|
|
let dialogOptions = [
|
|
|
|
|
["START", /(hello|hi)/, "Welcome to the attic, you are getting close.", "START"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
["START", /(shell)/, "One correct!", "SHELL"],
|
|
|
|
|
["START", /(mirror)/, "One correct!", "MIRROR"],
|
|
|
|
|
["START", /(computer)/, "One correct!", "COMPUTER"],
|
|
|
|
|
|
|
|
|
|
["SHELL", /(shell)/, "You already said that!!", "SHELL"],
|
|
|
|
|
["SHELL", /(mirror)/, "Two correct!", "SHELL_MIRROR"],
|
|
|
|
|
["SHELL", /(computer)/, "Two correct!", "SHELL_COMPUTER"],
|
|
|
|
|
|
|
|
|
|
[ "SHELL_COMPUTER", /(shell|computer)/, "You already said that!", "SHELL_COMPUTER"],
|
|
|
|
|
[ "SHELL_COMPUTER", /(mirror)/, "Ahhhhh...... the number of the portal is... 32!", "WIN"],
|
|
|
|
|
[ "SHELL_MIRROR", /(shell|mirror)/, "You already said that!", "SHELL_MIRROR"],
|
|
|
|
|
[ "SHELL_MIRROR", /(computer)/, "Ahhhhh...... the number of the portal is... 32!", "WIN"],
|
|
|
|
|
|
|
|
|
|
["MIRROR", /(mirror)/, "You already said that!!", "MIRROR"],
|
|
|
|
|
["MIRROR", /(shell)/, "Two correct!", "MIRROR_SHELL"],
|
|
|
|
|
["MIRROR", /(computer)/, "Two correct!", "MIRROR_COMPUTER"],
|
|
|
|
|
|
|
|
|
|
[ "MIRROR_COMPUTER", /(mirror|computer)/, "You already said that!", "MIRROR_COMPUTER"],
|
|
|
|
|
[ "MIRROR_COMPUTER", /(shell)/, "Ahhhhh...... the number of the portal is... 32!", "WIN"],
|
|
|
|
|
[ "MIRROR_SHELL", /(shell|mirror)/, "You already said that!", "MIRROR_COMPUTER"],
|
|
|
|
|
[ "MIRROR_SHELL", /(computer)/, "Ahhhhh...... the number of the portal is... 32!", "WIN"],
|
|
|
|
|
|
|
|
|
|
["COMPUTER", /(comptuer)/, "You already said that!!", "COMPUTER"],
|
|
|
|
|
["COMPUTER", /(mirror)/, "Two correct!", "COMPUTER_MIRROR"],
|
|
|
|
|
["COMPUTER", /(shell)/, "Two correct!", "COMPUTER_SHELL"],
|
|
|
|
|
|
|
|
|
|
[ "COMPUTER_SHELL", /(shell|computer)/, "You already said that!", "COMPUTER_SHELL"],
|
|
|
|
|
[ "COMPUTER_SHELL", /(mirror)/, "Ahhhhh...... the number of the portal is... 32!", "WIN"],
|
|
|
|
|
[ "COMPUTER_MIRROR", /(computer|mirror)/, "You already said that!", "COMPUTER_MIRROR"],
|
|
|
|
|
[ "COMPUTER_MIRROR", /(shell)/, "Ahhhhh...... the number of the portal is... 32!", "WIN"],
|
|
|
|
|
|
|
|
|
|
// Guessing game
|
|
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function sendMessage(roomId, command, body) {
|
|
|
|
|
const message = JSON.stringify([receiveId, sendId, roomId, command, body]);
|
|
|
|
|
ws.send(message);
|
|
|
|
|
sendId++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function receiveMessage(data) {
|
|
|
|
|
const [n1, n2, channel, command, body] = JSON.parse(data);
|
|
|
|
|
if (Number.isInteger(n1)) {
|
|
|
|
|
receiveId = n1;
|
|
|
|
|
}
|
|
|
|
|
if (command === "phx_reply" && state === STATE_STARTING) {
|
|
|
|
|
if (body.status === "ok") {
|
|
|
|
|
console.log("Joining Hubs...");
|
|
|
|
|
state = STATE_JOINING;
|
|
|
|
|
botSessionId = body.response.session_id;
|
|
|
|
|
vapidPublicKey = body.response.vapid_public_key;
|
|
|
|
|
sendMessage(fullHubId, "phx_join", {
|
|
|
|
|
profile: { avatarId, displayName },
|
|
|
|
|
auth_token: null,
|
|
|
|
|
perms_token: null,
|
|
|
|
|
context: { mobile: false, embed: false },
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
console.log(`ERROR WHILE STARTING: ${JSON.stringify(body)}`);
|
|
|
|
|
}
|
|
|
|
|
} else if (command === "phx_reply" && state == STATE_JOINING) {
|
|
|
|
|
if (body.status === "ok") {
|
|
|
|
|
const hub = body.response.hubs[0];
|
|
|
|
|
console.log(`Connected to ${hub.name}.`);
|
|
|
|
|
state = STATE_CONNECTED;
|
|
|
|
|
setInterval(sendHeartbeat, 30000);
|
|
|
|
|
} else {
|
|
|
|
|
console.log(`ERROR WHILE JOINING: ${JSON.stringify(body)}`);
|
|
|
|
|
}
|
|
|
|
|
} else if (command === "message" && state === STATE_CONNECTED) {
|
|
|
|
|
console.log(body);
|
|
|
|
|
handleChatMessage(body);
|
|
|
|
|
} else if (command === "presence_diff") {
|
|
|
|
|
for (const sessionId of Object.keys(body.joins)) {
|
|
|
|
|
if (sessionId === botSessionId) continue;
|
|
|
|
|
const meta =body.joins[sessionId].metas[0];
|
|
|
|
|
if (meta.presence !== 'room') continue;
|
|
|
|
|
const displayName = meta.profile.displayName;
|
|
|
|
|
const message = `Welcome, dear ${displayName}, to my attic..I like to listen here for any sounds from the outside.. So few come in… Stay here among the paintings for a while, you will see there is no way out..But let’s play a game! In each of these images, there is one element which you must name… For each image, this will be the most central object. But beware! Name all of the elements, and I shall steal your voice. Lo, I shall join the other world. You shall also be set free, for I shall tell you the secret of the portal...`;
|
|
|
|
|
sendMessage(fullHubId, "message", { body: message, type: "chat" });
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
//console.log(`Unknown command ${command}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleChatMessage(message) {
|
|
|
|
|
if (message.type !== "chat") return;
|
|
|
|
|
// This is the user that sent the message.
|
|
|
|
|
const sessionId = message.session_id;
|
|
|
|
|
// Ignore messages we sent ourselves.
|
|
|
|
|
if (sessionId === botSessionId) return;
|
|
|
|
|
const body = message.body.trim();
|
|
|
|
|
let chatState = chatStates[sessionId] || "START";
|
|
|
|
|
// Find a suitable dialog option.
|
|
|
|
|
for (const [startState, input, output, endState] of dialogOptions) {
|
|
|
|
|
if (startState !== chatState) continue;
|
|
|
|
|
if (body.match(input)) {
|
|
|
|
|
console.log(sessionId, chatState, output);
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
sendMessage(fullHubId, "message", { body: output, type: "chat" });
|
|
|
|
|
chatState = endState;
|
|
|
|
|
chatStates[sessionId] = chatState;
|
|
|
|
|
if (chatState === 'WIN') {
|
|
|
|
|
oscClient.send("/win");
|
|
|
|
|
chatStates[sessionId] = 'START';
|
|
|
|
|
}
|
|
|
|
|
}, 500 + Math.random() * 1000);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function sendHeartbeat() {
|
|
|
|
|
sendMessage("phoenix", "heartbeat", {});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ws.on("open", function () {
|
|
|
|
|
sendMessage("ret", "phx_join", { hub_id: hubId });
|
|
|
|
|
});
|
|
|
|
|
ws.on("message", receiveMessage);
|