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.
Files changed (2) hide show
  1. package/index.js +211 -149
  2. 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
- let chatID = ctx.message.chat.id;
29
- let hex = (await this.db.get('settings', chatID)).hex
30
- this.delete(hex, ctx.message.text.split(' ')[1])
31
- })
32
-
33
- this.bot.command('get', async (ctx) => {
34
- const chatID = ctx.message.chat.id;
35
- const lense = ctx.message.text.split(' ')[1];
36
- if (!lense) {
37
- return ctx.reply('Please specify a tag.');
38
- }
39
- let hex = (await this.db.get('settings', chatID)).hex
40
- //let hex = settings.hex
41
- console.log('hex', hex)
42
-
43
- let data = await this.get(ctx, hex, lense)
44
-
45
- })
46
-
47
- this.bot.command('gethex', async (ctx) => {
48
- let settings = await this.db.get('settings', chatID)
49
- let id = settings.hex ? settings.hex : 'Hex not set, use /sethex'
50
- ctx.reply(id)
51
- })
52
-
53
- this.bot.command('compute', async (ctx) => {
54
- const chatID = ctx.message.chat.id;
55
- let operation = ctx.message.text.split(' ')[1];
56
- if (operation != 'sum') {
57
- ctx.reply('Operation not implemented')
58
- return
59
- }
60
- let lense = ctx.message.text.split(' ')[2]
61
- if (!lense) {
62
- ctx.reply('Please specify a lense where to perform the operation ')
63
- return
64
- }
65
- let hex = (await this.db.get('settings', chatID)).hex
66
- await this.compute(hex, lense, operation)
67
-
68
- // let parentInfo = await this.getCellInfo(parent)
69
- // parentInfo.wisdom[id] = report
70
- // //update summary
71
- // let summary = await this.summarize(Object.values(parentInfo.wisdom).join('\n'))
72
- // parentInfo.summary = summary
73
-
74
- // await this.db.put('cell', parentInfo)
75
- // return parentInfo
76
-
77
- // let content = await this.db.getAll(hex+'/tags')
78
- })
79
-
80
-
81
-
82
-
83
- this.bot.command('cast', async (ctx) => {
84
- if (!ctx.message.reply_to_message) {
85
- return ctx.reply('Please reply to a message you want to tag.');
86
- }
87
- const tags = ctx.message.text.split(' ').slice(1);
88
- if (tags.length === 0) {
89
- return ctx.reply('Please provide at least one tag.');
90
- }
91
-
92
- const messageID = ctx.message.reply_to_message.message_id;
93
- const chatID = ctx.message.chat.id;
94
- const messageContent = ctx.message.reply_to_message.text;
95
- let settings = await this.db.get('settings', chatID)
96
- let id = settings.hex ? settings.hex : 'Hex not set, use /sethex'
97
- //create root node for the item
98
- let node = await this.gun.get(chatID + '/' + messageID).put({ id: chatID + '/' + messageID, content: messageContent })
99
- for (let tag of tags) {
100
- await this.gun.get(id).get(tag).set(node)
101
- this.upcast(id, tag, node)
102
- }
103
- })
104
-
105
- this.bot.command('publish', async (ctx) => {
106
- console.log(ctx.message)
107
- if (!ctx.message.reply_to_message) {
108
- return ctx.reply('Please reply to a message you want to tag.');
109
- }
110
- const tags = ctx.message.text.split(' ').slice(1);
111
- if (tags.length === 0) {
112
- return ctx.reply('Please provide at least one tag.');
113
- }
114
-
115
- const messageID = ctx.message.reply_to_message.message_id;
116
- const chatID = ctx.message.chat.id;
117
- const messageContent = ctx.message.reply_to_message.text;
118
- let settings = await this.db.get('settings', chatID)
119
- let hex = settings.hex
120
-
121
- for (let tag of tags) {
122
- let node = await this.gun.get(chatID + '/' + messageID).put({ id: chatID + '/' + messageID, content: messageContent })
123
- await this.put(hex, tag, node)
124
- }
125
-
126
- ctx.reply('Tag published.');
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.db.gun.get(lense).get('schema').put(JSON.stringify(schema), ack => {
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 tag:', lense);
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 schemaData;
168
- try {
169
- schemaData = await new Promise((resolve, reject) => {
170
- this.gun.get(lense).get('schema').once(data => {
171
- if (data) {
172
- resolve(JSON.parse(data));
173
- } else {
174
- reject(new Error('No schema data found'));
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 schemaData = JSON.parse(await new Promise((resolve, reject) => {
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 (!schemaData) {
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
- let valid = this.validator.validate(schemaData, parsed);
260
- if (!valid || parsed == null || parsed == undefined) {
261
- console.log('Removing Invalid content:', this.validator.errors);
262
- this.gun.get(hex).get(lense).get(key).put(null);
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
- } else {
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
- if (res == 0)
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
- return this.upcast(parent, lense, content)
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.1",
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"