-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathwatcher.js
233 lines (196 loc) · 8.21 KB
/
watcher.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
"use strict";
const _ = require('lodash');
const eventBus = require('ocore/event_bus.js');
const conf = require('ocore/conf.js');
const mutex = require('ocore/mutex.js');
const network = require('ocore/network.js');
const storage = require("ocore/storage.js");
const db = require("ocore/db.js");
const walletGeneral = require("ocore/wallet_general.js");
const constants = require("ocore/constants.js");
const formulaEvaluation = require("ocore/formula/evaluation.js");
const dag = require('aabot/dag.js');
const operator = require('aabot/operator.js');
const aa_state = require('aabot/aa_state.js');
const discordInstance = require('./discordInstance');
let notifiedPlots = {};
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function onAAResponse(objAAResponse) {
const { aa_address, trigger_unit, trigger_address, bounced, response } = objAAResponse;
if (bounced && trigger_address === operator.getAddress())
return console.log(`=== our request ${trigger_unit} bounced with error`, response.error);
if (bounced)
return console.log(`request ${trigger_unit} bounced with error`, response.error);
handleAAResponse(objAAResponse);
}
async function onAARequest(objAARequest, arrResponses) {
const address = objAARequest.unit.authors[0].address;
if (address === operator.getAddress())
return console.log(`skipping our own request`);
if (arrResponses[0].bounced)
return console.log(`trigger ${objAARequest.unit.unit} from ${address} will bounce`, arrResponses[0].response.error);
const aas = arrResponses.map(r => r.aa_address);
console.log(`request from ${address} trigger ${objAARequest.unit.unit} affected AAs`, aas);
for (let objAAResponse of arrResponses)
handleAAResponse(objAAResponse)
}
function handleAAResponse(objAAResponse) {
const { aa_address, response: { responseVars } } = objAAResponse;
if (aa_address === conf.city_aa && responseVars) {
const { event } = responseVars;
if (event) {
const objEvent = JSON.parse(event);
console.log('city event', objEvent);
const { type, plot_num, city, x, y } = objEvent;
if (type === 'allocate') {
console.log(`new plot ${plot_num} in ${city} at ${x}:${y}`);
if (typeof plot_num !== 'number')
throw Error(`plot_num ${plot_num} not a number`);
checkForNeighbors(plot_num);
}
else
console.log(`not an allocation event`);
}
else
console.log(`no event from city`);
}
}
async function checkForNeighbors(plot_num) {
console.log(`checking for neighbors of ${plot_num}`);
const aa_unlock = await aa_state.lock();
let upcomingStateVars = _.cloneDeep(aa_state.getUpcomingStateVars());
let upcomingBalances = _.cloneDeep(aa_state.getUpcomingBalances());
const vars = aa_state.getUpcomingAAStateVars(conf.city_aa);
const plot = vars['plot_' + plot_num];
if (!plot)
throw Error(`plot ${plot_num} not found`);
const { city, owner } = plot;
console.log(`checking for neighbors of ${plot_num} in ${city}`);
let neighbor_plot_num;
for (let name in vars) {
const m = name.match(/^plot_(\d+)$/);
if (m) {
const plot1_num = +m[1];
const plot1 = vars[name];
if (plot1.city === city && plot1.status === 'land' && plot1_num < plot_num) {
console.log(`will check if ${name} is a neighbor`);
try {
const bNeighbors = await formulaEvaluation.executeGetterInState(db, conf.city_aa, 'are_neighbors', [plot1_num, plot_num], upcomingStateVars, upcomingBalances);
console.log(`plots ${plot1_num} and ${plot_num} are neighbors? ${bNeighbors}`);
if (bNeighbors) {
neighbor_plot_num = plot1_num;
break;
}
}
catch (e) {
console.log(`are_neighbors failed`, e);
// neighbor_plot_num = plot1_num;
// break;
}
}
}
}
aa_unlock();
if (neighbor_plot_num) {
await notifyNeighbors(neighbor_plot_num, plot_num);
}
await db.query("UPDATE node_vars SET value=? WHERE name='last_plot_num'", [plot_num]);
console.log(`done checking for neighbors of ${plot_num} in ${city}`);
}
async function notifyNeighbors(plot1_num, plot2_num) {
if (notifiedPlots[plot2_num])
return console.log(`already notified about plot ${plot2_num} matching`);
const vars = aa_state.getUpcomingAAStateVars(conf.city_aa);
const plot1 = vars['plot_' + plot1_num];
const plot2 = vars['plot_' + plot2_num];
const usernames1 = await getUsernames(plot1.owner);
const usernames2 = await getUsernames(plot2.owner);
console.log({ usernames1, usernames2 });
if (usernames1.discord && usernames2.discord) {
const channel = await discordInstance.channels.fetch(process.env.CHANNEL_ID);
if (!channel)
throw Error(`failed to get discord channel`);
try {
const guild = channel.guild;
if (!guild) throw Error('server not found');
await guild.members.fetch();
const discordMember1 = guild.members.cache.find(m => m.user.username === usernames1.discord);
const discordMember2 = guild.members.cache.find(m => m.user.username === usernames2.discord);
const member1Mention = discordMember1 ? `<@${discordMember1.user.id}>` : '@' + usernames1.discord;
const member2Mention = discordMember2 ? `<@${discordMember2.user.id}>` : '@' + usernames2.discord;
await channel.send({
content: `${member1Mention} ${member2Mention} you became neighbors! Each of you gets two new empty plots and a house on the old one. You both need to claim the new plots and the house at https://obyte.city/claim/${plot1_num}-${plot2_num} within 10 minutes of each other. You can do this at any time. Please message each other to agree when you send your claiming transactions.`,
allowedMentions: { parse: ['users'] }
});
}
catch (e) {
console.log(`sending to discord failed`, e);
throw e;
}
}
else
console.log(`currently only both users on discord are supported`); // todo: both on telegram or they are on different networks
notifiedPlots[plot2_num] = true;
}
// get usernames of a user on discord, telegram, etc
async function getUsernames(address) {
const rows = await db.query("SELECT attestor_address, value FROM attested_fields WHERE attestor_address IN(?) AND address=? AND field='username' ORDER BY rowid DESC", [Object.keys(conf.attestors), address]);
if (rows.length === 0)
throw Error(`no attestations for ${address}`);
let usernames = {};
for (let { attestor_address, value: username } of rows) {
const service = conf.attestors[attestor_address];
if (!usernames[service]) // the later attestation has precedence
usernames[service] = username;
}
return usernames;
}
async function loadLibs() {
for (let address of conf.lib_aas) {
// await dag.loadAA(address);
const definition = await dag.readAADefinition(address);
const payload = { address, definition };
await storage.insertAADefinitions(db, [payload], constants.GENESIS_UNIT, 0, false);
}
}
function initAsset() {
const vars = aa_state.getAAStateVars(conf.city_aa);
const asset = vars.constants?.asset;
if (!asset)
throw Error(`asset not defined yet in ${conf.city_aa}`);
network.requestHistoryFor([asset]);
}
async function checkForMissedNeighbors() {
console.log(`checking for missed neighbors`);
await db.query(`CREATE TABLE IF NOT EXISTS node_vars (
name VARCHAR(30) NOT NULL PRIMARY KEY,
value TEXT NOT NULL,
last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)`);
await db.query(`INSERT OR IGNORE INTO node_vars (name, value) VALUES ('last_plot_num', 0)`);
const [{ value: last_seen_plot_num }] = await db.query("SELECT value FROM node_vars WHERE name='last_plot_num'");
const vars = aa_state.getAAStateVars(conf.city_aa);
const last_plot_num = vars.state.last_plot_num;
for (let plot_num = last_seen_plot_num + 1; plot_num <= last_plot_num; plot_num++){
const plot = vars['plot_' + plot_num];
if (plot)
await checkForNeighbors(plot_num);
}
console.log(`done checking for missed neighbors`);
}
async function startWatching() {
await loadLibs();
if (!process.env.BOT_TOKEN) throw new Error('error: BOT_TOKEN is required');
await discordInstance.login(process.env.BOT_TOKEN);
eventBus.on("aa_request_applied", onAARequest);
eventBus.on("aa_response_applied", onAAResponse);
await aa_state.followAA(conf.city_aa);
await aa_state.followAA(conf.vrf_oracle_aa);
for (let address in conf.attestors)
walletGeneral.addWatchedAddress(address);
initAsset();
await checkForMissedNeighbors();
}
exports.startWatching = startWatching;