-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathexample_max.js
142 lines (110 loc) · 3.46 KB
/
example_max.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Node system module
const fs = require("fs");
// Max module passed by Max Node environment
const maxApi = require("max-api");
// Modules installed with npm
const parseDataUrl = require("parse-data-url");
const Websocket = require("ws");
// Set up Websocket endpoint
const url = "wss://data.elektron.art";
// Establish Websocket connection
const ws = new Websocket(url);
// Keep websocket connection alive
const interval = setInterval(() => ws.ping(() => {}), 15 * 1000);
interval.unref();
// Handle the "subscribe" message from Max
maxApi.addHandler("subscribe", (channel, type, userId = null) => {
// Output the status from the first outlet of node.script object in Max
maxApi.outlet("status", { status: "subscribed" });
// Handle the incoming websocket message
ws.on("message", (data) => {
// Parse the message that can be text or a binary into a JSON
const parsedData = safeJsonParse(data);
// Check it the message is matching the conditions we passed to it
if (
parsedData &&
parsedData.channel === channel &&
type === parsedData.type &&
(userId ? userId === parsedData.userId : true)
) {
// Special handling for images: we decode them, save
// as temporary files and pass the file path to Max
if (parsedData.type === "IMAGE") {
const image = parseDataUrl(parsedData.value);
const filename = `${__dirname}/images/${parsedData.userId}.jpg`;
fs.writeFileSync(filename, image.toBuffer());
parsedData.value = filename;
}
// Output the message from the second outlet of node.script object in Max
maxApi.outlet("message", parsedData);
}
});
});
// Handle the "publish" message from Max
maxApi.addHandler(
"publish",
(channel, userid, username, type, value, store = false) => {
// Special handling for images: on "IMAGE" type we read the image path
// passed from Max ("value"), read the file and encode it as DataURL
if (type === "IMAGE") {
const encoding = "base64";
const data = fs.readFileSync(`${__dirname}/${value}`).toString(encoding);
const mimeType = "image/jpeg";
const dataUrl = `data:${mimeType};${encoding},${data}`;
value = dataUrl;
}
// Send websocket message
ws.send(
createMessage({
channel: channel,
type,
value,
userid,
username,
store: !!store,
})
);
}
);
require("dotenv").config();
const { Configuration, OpenAIApi } = require("openai");
const apiKey = process.env.OPENAI_API_KEY;
const configuration = new Configuration({
apiKey,
});
const openai = new OpenAIApi(configuration);
maxApi.addHandler("gptinput", async (gptinput) => {
const completion = await openai.createCompletion({
model: "text-davinci-003",
prompt: gptinput,
});
const gptoutput = completion.data.choices[0].text;
maxApi.post({ gptoutput });
maxApi.outlet("gptoutput", { gptoutput });
});
// Helper function to create a websocket message
const createMessage = (message) => {
const id = "abcdefghijklmnopqrstuvwxyz"
.split("")
.sort(() => Math.random() - 0.5)
.slice(0, 16)
.join("");
return JSON.stringify({
id,
datetime: new Date().toISOString(),
type: "",
channel: "",
userid: "",
username: "",
value: "",
...message,
});
};
// Helper function to parse string and binaries into JSON
const safeJsonParse = (str) => {
try {
return JSON.parse(str);
} catch (err) {
return null;
}
};