holosphere 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +211 -149
- package/package.json +1 -2
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import h3 from 'h3-js';
|
|
1
|
+
import * as h3 from 'h3-js';
|
|
2
2
|
import OpenAI from 'openai';
|
|
3
3
|
import Gun from 'gun'
|
|
4
4
|
import Ajv2019 from 'ajv/dist/2019.js'
|
|
@@ -8,14 +8,15 @@ class HoloSphere {
|
|
|
8
8
|
constructor(appname, openaikey = null) {
|
|
9
9
|
this.validator = new Ajv2019({ allErrors: false, strict: false });
|
|
10
10
|
this.gun = Gun({
|
|
11
|
-
peers: ['https://59.src.eco/gun'],
|
|
11
|
+
peers: [ 'http://localhost:8765','http://gun.holons.io', 'https://gun-eu.herokuapp.com/gun','https://59.src.eco/gun'],
|
|
12
12
|
axe: false,
|
|
13
13
|
// uuid: (content) => { // generate a unique id for each node
|
|
14
14
|
// console.log('uuid', content);
|
|
15
15
|
// return content;}
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
-
this.gun = gun.get(appname)
|
|
18
|
+
this.gun = this.gun.get(appname)
|
|
19
|
+
|
|
19
20
|
if (openaikey != null) {
|
|
20
21
|
this.openai = new OpenAI({
|
|
21
22
|
apiKey: openaikey,
|
|
@@ -24,123 +25,142 @@ class HoloSphere {
|
|
|
24
25
|
|
|
25
26
|
//this.bot.command('sethex', async (ctx) => { this.setHex(ctx) }) TODO: MOVE HERE FROM SETTINGS
|
|
26
27
|
|
|
27
|
-
this.bot.command('resethex', async (ctx) => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
this.bot.command('get', async (ctx) => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
this.bot.command('gethex', async (ctx) => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
this.bot.command('compute', async (ctx) => {
|
|
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
|
-
this.bot.command('cast', async (ctx) => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
this.bot.command('publish', async (ctx) => {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
});
|
|
28
|
+
// this.bot.command('resethex', async (ctx) => {
|
|
29
|
+
// let chatID = ctx.message.chat.id;
|
|
30
|
+
// let hex = (await this.db.get('settings', chatID)).hex
|
|
31
|
+
// this.delete(hex, ctx.message.text.split(' ')[1])
|
|
32
|
+
// })
|
|
33
|
+
|
|
34
|
+
// this.bot.command('get', async (ctx) => {
|
|
35
|
+
// const chatID = ctx.message.chat.id;
|
|
36
|
+
// const lense = ctx.message.text.split(' ')[1];
|
|
37
|
+
// if (!lense) {
|
|
38
|
+
// return ctx.reply('Please specify a tag.');
|
|
39
|
+
// }
|
|
40
|
+
// let hex = (await this.db.get('settings', chatID)).hex
|
|
41
|
+
// //let hex = settings.hex
|
|
42
|
+
// console.log('hex', hex)
|
|
43
|
+
|
|
44
|
+
// let data = await this.get(ctx, hex, lense)
|
|
45
|
+
|
|
46
|
+
// })
|
|
47
|
+
|
|
48
|
+
// this.bot.command('gethex', async (ctx) => {
|
|
49
|
+
// let settings = await this.db.get('settings', chatID)
|
|
50
|
+
// let id = settings.hex ? settings.hex : 'Hex not set, use /sethex'
|
|
51
|
+
// ctx.reply(id)
|
|
52
|
+
// })
|
|
53
|
+
|
|
54
|
+
// this.bot.command('compute', async (ctx) => {
|
|
55
|
+
// const chatID = ctx.message.chat.id;
|
|
56
|
+
// let operation = ctx.message.text.split(' ')[1];
|
|
57
|
+
// if (operation != 'sum') {
|
|
58
|
+
// ctx.reply('Operation not implemented')
|
|
59
|
+
// return
|
|
60
|
+
// }
|
|
61
|
+
// let lense = ctx.message.text.split(' ')[2]
|
|
62
|
+
// if (!lense) {
|
|
63
|
+
// ctx.reply('Please specify a lense where to perform the operation ')
|
|
64
|
+
// return
|
|
65
|
+
// }
|
|
66
|
+
// let hex = (await this.db.get('settings', chatID)).hex
|
|
67
|
+
// await this.compute(hex, lense, operation)
|
|
68
|
+
|
|
69
|
+
// // let parentInfo = await this.getCellInfo(parent)
|
|
70
|
+
// // parentInfo.wisdom[id] = report
|
|
71
|
+
// // //update summary
|
|
72
|
+
// // let summary = await this.summarize(Object.values(parentInfo.wisdom).join('\n'))
|
|
73
|
+
// // parentInfo.summary = summary
|
|
74
|
+
|
|
75
|
+
// // await this.db.put('cell', parentInfo)
|
|
76
|
+
// // return parentInfo
|
|
77
|
+
|
|
78
|
+
// // let content = await this.db.getAll(hex+'/tags')
|
|
79
|
+
// })
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
// this.bot.command('cast', async (ctx) => {
|
|
85
|
+
// if (!ctx.message.reply_to_message) {
|
|
86
|
+
// return ctx.reply('Please reply to a message you want to tag.');
|
|
87
|
+
// }
|
|
88
|
+
// const tags = ctx.message.text.split(' ').slice(1);
|
|
89
|
+
// if (tags.length === 0) {
|
|
90
|
+
// return ctx.reply('Please provide at least one tag.');
|
|
91
|
+
// }
|
|
92
|
+
|
|
93
|
+
// const messageID = ctx.message.reply_to_message.message_id;
|
|
94
|
+
// const chatID = ctx.message.chat.id;
|
|
95
|
+
// const messageContent = ctx.message.reply_to_message.text;
|
|
96
|
+
// let settings = await this.db.get('settings', chatID)
|
|
97
|
+
// let id = settings.hex ? settings.hex : 'Hex not set, use /sethex'
|
|
98
|
+
// //create root node for the item
|
|
99
|
+
// let node = await this.gun.get(chatID + '/' + messageID).put({ id: chatID + '/' + messageID, content: messageContent })
|
|
100
|
+
// for (let tag of tags) {
|
|
101
|
+
// await this.gun.get(id).get(tag).set(node)
|
|
102
|
+
// this.upcast(id, tag, node)
|
|
103
|
+
// }
|
|
104
|
+
// })
|
|
105
|
+
|
|
106
|
+
// this.bot.command('publish', async (ctx) => {
|
|
107
|
+
// console.log(ctx.message)
|
|
108
|
+
// if (!ctx.message.reply_to_message) {
|
|
109
|
+
// return ctx.reply('Please reply to a message you want to tag.');
|
|
110
|
+
// }
|
|
111
|
+
// const tags = ctx.message.text.split(' ').slice(1);
|
|
112
|
+
// if (tags.length === 0) {
|
|
113
|
+
// return ctx.reply('Please provide at least one tag.');
|
|
114
|
+
// }
|
|
115
|
+
|
|
116
|
+
// const messageID = ctx.message.reply_to_message.message_id;
|
|
117
|
+
// const chatID = ctx.message.chat.id;
|
|
118
|
+
// const messageContent = ctx.message.reply_to_message.text;
|
|
119
|
+
// let settings = await this.db.get('settings', chatID)
|
|
120
|
+
// let hex = settings.hex
|
|
121
|
+
|
|
122
|
+
// for (let tag of tags) {
|
|
123
|
+
// let node = await this.gun.get(chatID + '/' + messageID).put({ id: chatID + '/' + messageID, content: messageContent })
|
|
124
|
+
// await this.put(hex, tag, node)
|
|
125
|
+
// }
|
|
126
|
+
|
|
127
|
+
// ctx.reply('Tag published.');
|
|
128
|
+
// });
|
|
128
129
|
|
|
129
130
|
}
|
|
130
131
|
|
|
131
132
|
async setSchema(lense, schema) {
|
|
132
133
|
return new Promise((resolve, reject) => {
|
|
133
|
-
this.
|
|
134
|
+
this.gun.get(lense).get('schema').put(JSON.stringify(schema), ack => {
|
|
134
135
|
if (ack.err) {
|
|
135
136
|
resolve(new Error('Failed to add schema: ' + ack.err));
|
|
136
137
|
} else {
|
|
137
|
-
console.log('Schema added successfully under
|
|
138
|
+
console.log('Schema added successfully under lense:', lense);
|
|
138
139
|
resolve(ack);
|
|
139
140
|
}
|
|
140
141
|
})
|
|
141
142
|
})
|
|
142
143
|
}
|
|
143
144
|
|
|
145
|
+
async getSchema(lense) {
|
|
146
|
+
return new Promise((resolve) => {
|
|
147
|
+
this.gun.get(lense).get('schema').once(data => {
|
|
148
|
+
if (data) {
|
|
149
|
+
let parsed;
|
|
150
|
+
try {
|
|
151
|
+
parsed = JSON.parse(data);
|
|
152
|
+
}
|
|
153
|
+
catch (e) {
|
|
154
|
+
resolve(null)
|
|
155
|
+
}
|
|
156
|
+
resolve(parsed);
|
|
157
|
+
} else {
|
|
158
|
+
resolve(null);
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
144
164
|
async setHex(ctx) {
|
|
145
165
|
const chatID = ctx.message.chat.id;
|
|
146
166
|
const hex = ctx.message.text.split(' ')[1];
|
|
@@ -164,27 +184,14 @@ class HoloSphere {
|
|
|
164
184
|
|
|
165
185
|
async put(hex, lense, content) {
|
|
166
186
|
// Retrieve the schema for the lense
|
|
167
|
-
let
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
} catch (error) {
|
|
179
|
-
console.error(`Error fetching schema for "${lense}": ${error}`);
|
|
180
|
-
return null;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Validate the content against the schema
|
|
184
|
-
const valid = this.validator.validate(schemaData, content);
|
|
185
|
-
if (!valid) {
|
|
186
|
-
console.error('Invalid content:', this.validator.errors);
|
|
187
|
-
return null;
|
|
187
|
+
let schema = await this.getSchema(lense)
|
|
188
|
+
if (schema) {
|
|
189
|
+
// Validate the content against the schema
|
|
190
|
+
const valid = this.validator.validate(schema, content);
|
|
191
|
+
if (!valid) {
|
|
192
|
+
console.error('Not committing invalid content:', this.validator.errors);
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
188
195
|
}
|
|
189
196
|
|
|
190
197
|
// Create a node for the content
|
|
@@ -217,20 +224,11 @@ class HoloSphere {
|
|
|
217
224
|
async get(hex, lense) {
|
|
218
225
|
// Wrap the GunDB operation in a promise
|
|
219
226
|
//retrieve lense schema
|
|
220
|
-
const
|
|
221
|
-
this.gun.get(lense).get('schema').once(data => {
|
|
222
|
-
if (data) {
|
|
223
|
-
resolve(data);
|
|
224
|
-
} else {
|
|
225
|
-
resolve(null);
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
})
|
|
229
|
-
)
|
|
227
|
+
const schema = await this.getSchema(lense);
|
|
230
228
|
|
|
231
|
-
if (!
|
|
229
|
+
if (!schema) {
|
|
232
230
|
console.log('The schema for "' + lense + '" is not defined');
|
|
233
|
-
return null;
|
|
231
|
+
// return null; // No schema found, return null if strict about it
|
|
234
232
|
}
|
|
235
233
|
|
|
236
234
|
return new Promise(async (resolve, reject) => {
|
|
@@ -239,11 +237,10 @@ class HoloSphere {
|
|
|
239
237
|
this.gun.get(hex.toString()).get(lense).once((data, key) => {
|
|
240
238
|
if (data) {
|
|
241
239
|
const maplenght = Object.keys(data).length - 1
|
|
240
|
+
console.log('Map length:', maplenght)
|
|
242
241
|
this.gun.get(hex.toString()).get(lense).map().once(async (itemdata, key) => {
|
|
243
242
|
counter += 1
|
|
244
243
|
if (itemdata) {
|
|
245
|
-
|
|
246
|
-
|
|
247
244
|
// if (itemdata._["#"]) {
|
|
248
245
|
// // If the data is a reference, fetch the actual content
|
|
249
246
|
// itemdata = await this.gun.get(itemdata._['#']).then();
|
|
@@ -254,14 +251,20 @@ class HoloSphere {
|
|
|
254
251
|
parsed = JSON.parse(itemdata);
|
|
255
252
|
} catch (e) {
|
|
256
253
|
console.log('Invalid JSON:', itemdata);
|
|
254
|
+
parsed = itemdata //return the raw data
|
|
257
255
|
}
|
|
258
256
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
257
|
+
if (schema) {
|
|
258
|
+
let valid = this.validator.validate(schema, parsed);
|
|
259
|
+
if (!valid || parsed == null || parsed == undefined) {
|
|
260
|
+
console.log('Removing Invalid content:', this.validator.errors);
|
|
261
|
+
this.gun.get(hex).get(lense).get(key).put(null);
|
|
263
262
|
|
|
264
|
-
|
|
263
|
+
} else {
|
|
264
|
+
output.push(parsed);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
265
268
|
output.push(parsed);
|
|
266
269
|
}
|
|
267
270
|
}
|
|
@@ -364,15 +367,18 @@ class HoloSphere {
|
|
|
364
367
|
|
|
365
368
|
async upcast(hex, lense, content) {
|
|
366
369
|
let res = h3.getResolution(hex)
|
|
367
|
-
|
|
368
|
-
return content
|
|
370
|
+
|
|
369
371
|
|
|
370
372
|
console.log('Upcasting ', hex, lense, content)
|
|
371
373
|
let parent = h3.cellToParent(hex, res - 1)
|
|
372
374
|
await this.put(parent, lense, content)
|
|
373
|
-
|
|
375
|
+
if (res == 0)
|
|
376
|
+
return content
|
|
377
|
+
else
|
|
378
|
+
return this.upcast(parent, lense, content)
|
|
374
379
|
}
|
|
375
380
|
|
|
381
|
+
|
|
376
382
|
// send information upwards, triggers the parent to update its summary
|
|
377
383
|
async updateParent(id, report) {
|
|
378
384
|
let cellinfo = await this.getCellInfo(id)
|
|
@@ -413,6 +419,62 @@ class HoloSphere {
|
|
|
413
419
|
}
|
|
414
420
|
return list
|
|
415
421
|
}
|
|
422
|
+
|
|
423
|
+
subscribe(hex, lense, callback) {
|
|
424
|
+
this.gun.get(hex).get(lense).map().on((data, key) => {
|
|
425
|
+
callback(data, key)
|
|
426
|
+
})
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// VOTING SYSTEM
|
|
430
|
+
|
|
431
|
+
getFinalVote(userId, topic, votes, visited = new Set()) {
|
|
432
|
+
if (visited.has(userId)) {
|
|
433
|
+
return null; // Avoid circular delegations
|
|
434
|
+
}
|
|
435
|
+
visited.add(userId);
|
|
436
|
+
|
|
437
|
+
const delegation = users[userId].delegations[topic];
|
|
438
|
+
if (delegation && votes[delegation] === undefined) {
|
|
439
|
+
return getFinalVote(delegation, topic, votes, visited); // Follow delegation
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return votes[userId] !== undefined ? votes[userId] : null; // Return direct vote or null
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
aggregateVotes(hexId, topic) {
|
|
446
|
+
const votes = hexagonVotes[hexId][topic];
|
|
447
|
+
const aggregatedVotes = {};
|
|
448
|
+
|
|
449
|
+
Object.keys(votes).forEach(userId => {
|
|
450
|
+
const finalVote = getFinalVote(userId, topic, votes);
|
|
451
|
+
if (finalVote !== null) {
|
|
452
|
+
aggregatedVotes[finalVote] = (aggregatedVotes[finalVote] || 0) + 1;
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
return aggregatedVotes;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
async delegateVote(userId, topic, delegateTo) {
|
|
460
|
+
const response = await fetch('/delegate', {
|
|
461
|
+
method: 'POST',
|
|
462
|
+
headers: { 'Content-Type': 'application/json' },
|
|
463
|
+
body: JSON.stringify({ userId, topic, delegateTo })
|
|
464
|
+
});
|
|
465
|
+
alert(await response.text());
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
async vote(userId, hexId, topic, vote) {
|
|
469
|
+
const response = await fetch('/vote', {
|
|
470
|
+
method: 'POST',
|
|
471
|
+
headers: { 'Content-Type': 'application/json' },
|
|
472
|
+
body: JSON.stringify({ userId, hexId, topic, vote })
|
|
473
|
+
});
|
|
474
|
+
alert(await response.text());
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
|
|
416
478
|
}
|
|
417
479
|
|
|
418
480
|
export default HoloSphere;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "holosphere",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Holonic Geospatial Communication Infrastructure based on h3.js and gun.js",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
"license": "GPL-3.0-or-later",
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"ajv": "^8.12.0",
|
|
14
|
-
"dotenv": "^16.4.5",
|
|
15
14
|
"gun": "^0.2020.1239",
|
|
16
15
|
"h3-js": "^4.1.0",
|
|
17
16
|
"openai": "^4.29.2"
|