arnacon-webrtc-service 0.1.4 → 0.1.6
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/globalserviceconfig.json
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"development": {
|
|
3
3
|
"roflBaseUrl": "https://p8000.m1485.test-proxy-b.rofl.app",
|
|
4
|
+
"useLocalRoflLogic": true,
|
|
5
|
+
"roflLogic": {
|
|
6
|
+
"rpc": "https://testnet.sapphire.oasis.io",
|
|
7
|
+
"chainId": 23295,
|
|
8
|
+
"businessNumberDbAddress": "0x684F9797de793E603368A8Fe1Fe663aAeb327621",
|
|
9
|
+
"callerIdPoolAddress": "0xFB3DD146F1aFfbA661533Ec49298D01f20104CF6"
|
|
10
|
+
},
|
|
4
11
|
"messageProcessorUrl": "https://europe-west3-asterisk-tts-test.cloudfunctions.net/client_msg_processor",
|
|
5
12
|
"polygon": {
|
|
6
13
|
"rpc": "https://polygon-bor-rpc.publicnode.com",
|
|
@@ -65,6 +72,13 @@
|
|
|
65
72
|
},
|
|
66
73
|
"production": {
|
|
67
74
|
"roflBaseUrl": "https://p8000.m206.opf-mainnet-rofl-35.rofl.app",
|
|
75
|
+
"useLocalRoflLogic": true,
|
|
76
|
+
"roflLogic": {
|
|
77
|
+
"rpc": "https://sapphire.oasis.io",
|
|
78
|
+
"chainId": 23294,
|
|
79
|
+
"businessNumberDbAddress": "",
|
|
80
|
+
"callerIdPoolAddress": "0x903796dc34Bb9A5eD76A41bBA6C40CfebD1f4269"
|
|
81
|
+
},
|
|
68
82
|
"messageProcessorUrl": "https://europe-west3-asterisk-tts-test.cloudfunctions.net/client_msg_processor",
|
|
69
83
|
"polygon": {
|
|
70
84
|
"rpc": "https://polygon-bor-rpc.publicnode.com",
|
package/package.json
CHANGED
|
@@ -15,6 +15,23 @@ function createBlockchainApi({
|
|
|
15
15
|
const SAPPHIRE_RPC = config.sapphire.rpc;
|
|
16
16
|
const SAPPHIRE_TESTNET_RPC = config.sapphireTestnet.rpc;
|
|
17
17
|
const NFT_CALLER_ID_POOL_ADDRESS = config.sapphireTestnet.NFTCallerIdPool;
|
|
18
|
+
const ROFL_LOGIC_CONFIG = config.roflLogic || {};
|
|
19
|
+
const ROFL_LOGIC_RPC =
|
|
20
|
+
process.env.ROFL_LOGIC_RPC_URL ||
|
|
21
|
+
ROFL_LOGIC_CONFIG.rpc ||
|
|
22
|
+
SAPPHIRE_TESTNET_RPC ||
|
|
23
|
+
SAPPHIRE_RPC;
|
|
24
|
+
const ROFL_LOGIC_CHAIN_ID =
|
|
25
|
+
Number(process.env.ROFL_LOGIC_CHAIN_ID || ROFL_LOGIC_CONFIG.chainId || 0) || undefined;
|
|
26
|
+
const ROFL_BUSINESS_NUMBER_DB_ADDRESS =
|
|
27
|
+
process.env.ROFL_LOGIC_BUSINESS_NUMBER_DB_ADDRESS ||
|
|
28
|
+
ROFL_LOGIC_CONFIG.businessNumberDbAddress ||
|
|
29
|
+
"";
|
|
30
|
+
const ROFL_CALLER_ID_POOL_ADDRESS =
|
|
31
|
+
process.env.ROFL_LOGIC_CALLER_ID_POOL_ADDRESS ||
|
|
32
|
+
ROFL_LOGIC_CONFIG.callerIdPoolAddress ||
|
|
33
|
+
NFT_CALLER_ID_POOL_ADDRESS;
|
|
34
|
+
const ROFL_PKEY = process.env.ROFL_LOGIC_PKEY || process.env.PKEY || "";
|
|
18
35
|
|
|
19
36
|
const ENS_REGISTRY_ABI = [
|
|
20
37
|
"function owner(bytes32 node) view returns (address)",
|
|
@@ -36,10 +53,27 @@ function createBlockchainApi({
|
|
|
36
53
|
"function tokenOfOwnerByIndex(address owner, uint256 index) view returns (uint256)",
|
|
37
54
|
"function getCallerIdByTokenId(uint256 tokenId) view returns (string phoneNumber, string metadata, address owner)",
|
|
38
55
|
];
|
|
56
|
+
const BUSINESS_NUMBER_DB_ABI = [
|
|
57
|
+
"function getPhoneNumber(string identifier) view returns (string)",
|
|
58
|
+
];
|
|
59
|
+
const CALLER_ID_POOL_ROFL_ABI = [
|
|
60
|
+
"function findNextOwnedCallerId(uint256 startIndex, address expectedOwner, uint256 maxAttempts) view returns (string phoneNumber, uint256 foundIndex, bool found)",
|
|
61
|
+
"function getPoolSize() view returns (uint256)",
|
|
62
|
+
"function admin() view returns (address)",
|
|
63
|
+
"function getTokens() view returns (uint256[])",
|
|
64
|
+
"function getCallerIdByTokenId(uint256 tokenId) view returns (string phoneNumber, string metadata, address owner)",
|
|
65
|
+
];
|
|
39
66
|
|
|
40
67
|
let polygonProvider = null;
|
|
41
68
|
let sapphireProvider = null;
|
|
42
69
|
let sapphireTestnetProvider = null;
|
|
70
|
+
let roflLogicProvider = null;
|
|
71
|
+
let businessNumberDbContract = null;
|
|
72
|
+
let callerIdPoolRoflContract = null;
|
|
73
|
+
let roflPoolOwnerAddress = null;
|
|
74
|
+
let roflCallerIdIndex = 0;
|
|
75
|
+
let roflAddress = null;
|
|
76
|
+
let roflOwnerResolved = false;
|
|
43
77
|
|
|
44
78
|
function getPolygonProvider() {
|
|
45
79
|
if (!polygonProvider) polygonProvider = new ethers.providers.JsonRpcProvider(POLYGON_RPC);
|
|
@@ -56,6 +90,77 @@ function createBlockchainApi({
|
|
|
56
90
|
return sapphireTestnetProvider;
|
|
57
91
|
}
|
|
58
92
|
|
|
93
|
+
function getRoflLogicProvider() {
|
|
94
|
+
if (!roflLogicProvider) {
|
|
95
|
+
if (ROFL_LOGIC_CHAIN_ID) {
|
|
96
|
+
roflLogicProvider = new ethers.providers.JsonRpcProvider(ROFL_LOGIC_RPC, ROFL_LOGIC_CHAIN_ID);
|
|
97
|
+
} else {
|
|
98
|
+
roflLogicProvider = new ethers.providers.JsonRpcProvider(ROFL_LOGIC_RPC);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return roflLogicProvider;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function normalizeContractAddress(value) {
|
|
105
|
+
const normalized = String(value || "").trim();
|
|
106
|
+
if (!normalized || normalized === "0x0000000000000000000000000000000000000000") return "";
|
|
107
|
+
return normalized;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function toSafeNumber(value, fallback = 0) {
|
|
111
|
+
if (typeof value === "number") return value;
|
|
112
|
+
if (typeof value === "bigint") return Number(value);
|
|
113
|
+
if (value && typeof value.toString === "function") return Number(value.toString());
|
|
114
|
+
return fallback;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function getRoflAddress() {
|
|
118
|
+
if (!roflAddress && ROFL_PKEY) {
|
|
119
|
+
try {
|
|
120
|
+
roflAddress = new ethers.Wallet(ROFL_PKEY).address;
|
|
121
|
+
} catch (err) {
|
|
122
|
+
logger.error(`[ROFL_LOCAL] invalid PKEY: ${err.message}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return roflAddress;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function getBusinessNumberDbContract() {
|
|
129
|
+
if (!businessNumberDbContract) {
|
|
130
|
+
const address = normalizeContractAddress(ROFL_BUSINESS_NUMBER_DB_ADDRESS);
|
|
131
|
+
if (!address) return null;
|
|
132
|
+
businessNumberDbContract = new ethers.Contract(address, BUSINESS_NUMBER_DB_ABI, getRoflLogicProvider());
|
|
133
|
+
}
|
|
134
|
+
return businessNumberDbContract;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function getRoflCallerIdPoolContract() {
|
|
138
|
+
if (!callerIdPoolRoflContract) {
|
|
139
|
+
const address = normalizeContractAddress(ROFL_CALLER_ID_POOL_ADDRESS);
|
|
140
|
+
if (!address) return null;
|
|
141
|
+
callerIdPoolRoflContract = new ethers.Contract(address, CALLER_ID_POOL_ROFL_ABI, getRoflLogicProvider());
|
|
142
|
+
}
|
|
143
|
+
return callerIdPoolRoflContract;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function getRoflPoolOwnerAddress() {
|
|
147
|
+
if (roflOwnerResolved) return roflPoolOwnerAddress || getRoflAddress();
|
|
148
|
+
|
|
149
|
+
roflOwnerResolved = true;
|
|
150
|
+
const pool = getRoflCallerIdPoolContract();
|
|
151
|
+
if (!pool) {
|
|
152
|
+
roflPoolOwnerAddress = getRoflAddress();
|
|
153
|
+
return roflPoolOwnerAddress;
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
roflPoolOwnerAddress = await pool.admin();
|
|
157
|
+
} catch (err) {
|
|
158
|
+
logger.warn(`[ROFL_LOCAL] pool admin() failed, falling back to ROFL key address: ${err.message}`);
|
|
159
|
+
roflPoolOwnerAddress = getRoflAddress();
|
|
160
|
+
}
|
|
161
|
+
return roflPoolOwnerAddress;
|
|
162
|
+
}
|
|
163
|
+
|
|
59
164
|
function isEthAddress(str) {
|
|
60
165
|
return /^0x[0-9a-fA-F]{40}$/.test(str);
|
|
61
166
|
}
|
|
@@ -285,6 +390,74 @@ function createBlockchainApi({
|
|
|
285
390
|
}
|
|
286
391
|
}
|
|
287
392
|
|
|
393
|
+
async function roflFindBusinessNumber(callee) {
|
|
394
|
+
const contract = getBusinessNumberDbContract();
|
|
395
|
+
if (!contract) return null;
|
|
396
|
+
|
|
397
|
+
const businessName = String(callee || "").trim().toLowerCase();
|
|
398
|
+
if (!businessName) return null;
|
|
399
|
+
try {
|
|
400
|
+
const phoneNumber = await contract.getPhoneNumber(businessName);
|
|
401
|
+
return phoneNumber && phoneNumber !== "" ? phoneNumber : null;
|
|
402
|
+
} catch (err) {
|
|
403
|
+
logger.error(`[ROFL_LOCAL] find-business-number failed for ${businessName}: ${err.message}`);
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
async function roflAssignFromNumber() {
|
|
409
|
+
const pool = getRoflCallerIdPoolContract();
|
|
410
|
+
if (!pool) return null;
|
|
411
|
+
|
|
412
|
+
try {
|
|
413
|
+
const poolSize = await pool.getPoolSize();
|
|
414
|
+
if (toSafeNumber(poolSize) <= 0) return null;
|
|
415
|
+
|
|
416
|
+
const roflKeyAddress = getRoflAddress();
|
|
417
|
+
|
|
418
|
+
// Explicit request: if PKEY is not set, assign directly from pool
|
|
419
|
+
// without owner filtering (pure round-robin by token list index).
|
|
420
|
+
if (!roflKeyAddress) {
|
|
421
|
+
const tokens = await pool.getTokens();
|
|
422
|
+
if (!tokens || tokens.length === 0) return null;
|
|
423
|
+
|
|
424
|
+
const idx = roflCallerIdIndex % tokens.length;
|
|
425
|
+
const tokenId = tokens[idx];
|
|
426
|
+
const [fromNumber] = await pool.getCallerIdByTokenId(tokenId);
|
|
427
|
+
if (!fromNumber || fromNumber === "") return null;
|
|
428
|
+
|
|
429
|
+
roflCallerIdIndex = idx + 1;
|
|
430
|
+
return fromNumber;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const ownerForPool = (await getRoflPoolOwnerAddress()) || roflKeyAddress;
|
|
434
|
+
if (!ownerForPool) return null;
|
|
435
|
+
|
|
436
|
+
const [fromNumber, foundIndex, found] = await pool.findNextOwnedCallerId(
|
|
437
|
+
ethers.BigNumber.from(roflCallerIdIndex),
|
|
438
|
+
ownerForPool,
|
|
439
|
+
poolSize,
|
|
440
|
+
);
|
|
441
|
+
|
|
442
|
+
if (!found || !fromNumber || fromNumber === "") return null;
|
|
443
|
+
roflCallerIdIndex = toSafeNumber(foundIndex, roflCallerIdIndex) + 1;
|
|
444
|
+
return fromNumber;
|
|
445
|
+
} catch (err) {
|
|
446
|
+
logger.error(`[ROFL_LOCAL] assign-from-number failed: ${err.message}`);
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function getRoflLogicInfo() {
|
|
452
|
+
return {
|
|
453
|
+
rpc: ROFL_LOGIC_RPC || null,
|
|
454
|
+
chainId: ROFL_LOGIC_CHAIN_ID || null,
|
|
455
|
+
businessNumberDbAddress: normalizeContractAddress(ROFL_BUSINESS_NUMBER_DB_ADDRESS) || null,
|
|
456
|
+
callerIdPoolAddress: normalizeContractAddress(ROFL_CALLER_ID_POOL_ADDRESS) || null,
|
|
457
|
+
roflAddress: getRoflAddress() || null,
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
288
461
|
return {
|
|
289
462
|
ethers,
|
|
290
463
|
getPolygonProvider,
|
|
@@ -302,6 +475,9 @@ function createBlockchainApi({
|
|
|
302
475
|
verifyHttpSignalingSignature,
|
|
303
476
|
resolveCallerServiceProviderContract,
|
|
304
477
|
nftGetOwnedNumber,
|
|
478
|
+
roflFindBusinessNumber,
|
|
479
|
+
roflAssignFromNumber,
|
|
480
|
+
getRoflLogicInfo,
|
|
305
481
|
getRpcForNetwork,
|
|
306
482
|
};
|
|
307
483
|
}
|
|
@@ -6,6 +6,9 @@ function createCallRouter({
|
|
|
6
6
|
roflBaseUrl,
|
|
7
7
|
fetchImpl = fetch,
|
|
8
8
|
logger = console,
|
|
9
|
+
useLocalRoflLogic = false,
|
|
10
|
+
lookupBusinessNumberImpl = null,
|
|
11
|
+
assignFromNumberImpl = null,
|
|
9
12
|
}) {
|
|
10
13
|
function isRawEmail(str) {
|
|
11
14
|
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str) && !str.endsWith(".global");
|
|
@@ -34,6 +37,15 @@ function createCallRouter({
|
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
async function roflFindBusinessNumber(callee) {
|
|
40
|
+
if (useLocalRoflLogic && typeof lookupBusinessNumberImpl === "function") {
|
|
41
|
+
try {
|
|
42
|
+
return (await lookupBusinessNumberImpl(callee)) || null;
|
|
43
|
+
} catch (err) {
|
|
44
|
+
logger.error(`[ROFL_LOCAL] find-business-number failed for ${callee}: ${err.message}`);
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
37
49
|
try {
|
|
38
50
|
const resp = await fetchImpl(`${roflBaseUrl}/find-business-number`, {
|
|
39
51
|
method: "POST",
|
|
@@ -82,6 +94,15 @@ function createCallRouter({
|
|
|
82
94
|
}
|
|
83
95
|
|
|
84
96
|
async function roflAssignFromNumber() {
|
|
97
|
+
if (useLocalRoflLogic && typeof assignFromNumberImpl === "function") {
|
|
98
|
+
try {
|
|
99
|
+
return (await assignFromNumberImpl()) || null;
|
|
100
|
+
} catch (err) {
|
|
101
|
+
logger.error(`[ROFL_LOCAL] assign-from-number failed: ${err.message}`);
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
85
106
|
try {
|
|
86
107
|
const resp = await fetchImpl(`${roflBaseUrl}/assign-from-number`);
|
|
87
108
|
if (!resp.ok) return null;
|
|
@@ -237,6 +237,7 @@ const config = {
|
|
|
237
237
|
polygon: pickRuntimeConfig("polygon", {}),
|
|
238
238
|
sapphire: pickRuntimeConfig("sapphire", {}),
|
|
239
239
|
sapphireTestnet: pickRuntimeConfig("sapphireTestnet", {}),
|
|
240
|
+
roflLogic: pickRuntimeConfig("roflLogic", {}),
|
|
240
241
|
};
|
|
241
242
|
const serviceRuntimes = loadedServices;
|
|
242
243
|
const selectedServiceId = process.env.SERVICE_ID || null;
|
|
@@ -268,6 +269,8 @@ const INTERNAL_BIND_IP = config.bindIp || "127.0.0.1";
|
|
|
268
269
|
|
|
269
270
|
// ROFL API config
|
|
270
271
|
const ROFL_BASE_URL = config.roflBaseUrl;
|
|
272
|
+
const USE_LOCAL_ROFL_LOGIC =
|
|
273
|
+
String(process.env.USE_LOCAL_ROFL_LOGIC || pickRuntimeConfig("useLocalRoflLogic", true)) === "true";
|
|
271
274
|
const MESSAGE_PROCESSOR_URL =
|
|
272
275
|
config.messageProcessorUrl ||
|
|
273
276
|
"https://europe-west3-asterisk-tts-test.cloudfunctions.net/client_msg_processor";
|
|
@@ -313,7 +316,17 @@ const callRouterApi = createCallRouter({
|
|
|
313
316
|
roflBaseUrl: ROFL_BASE_URL,
|
|
314
317
|
fetchImpl: fetch,
|
|
315
318
|
logger: console,
|
|
319
|
+
useLocalRoflLogic: USE_LOCAL_ROFL_LOGIC,
|
|
320
|
+
lookupBusinessNumberImpl: (...args) => blockchainApi.roflFindBusinessNumber(...args),
|
|
321
|
+
assignFromNumberImpl: (...args) => blockchainApi.roflAssignFromNumber(...args),
|
|
316
322
|
});
|
|
323
|
+
const roflLogicInfo = blockchainApi.getRoflLogicInfo();
|
|
324
|
+
console.log(
|
|
325
|
+
`[ROFL] mode=${USE_LOCAL_ROFL_LOGIC ? "local_rofl_logic" : "remote_http"} ` +
|
|
326
|
+
`baseUrl=${ROFL_BASE_URL || "n/a"} rpc=${roflLogicInfo.rpc || "n/a"} ` +
|
|
327
|
+
`chainId=${roflLogicInfo.chainId || "n/a"} businessDb=${roflLogicInfo.businessNumberDbAddress || "n/a"} ` +
|
|
328
|
+
`callerIdPool=${roflLogicInfo.callerIdPoolAddress || "n/a"} roflAddress=${roflLogicInfo.roflAddress || "n/a"}`,
|
|
329
|
+
);
|
|
317
330
|
const notificationApi = createNotificationApi({
|
|
318
331
|
blockchainApi,
|
|
319
332
|
signalingPlanAbi: SIGNALING_PLAN_ABI,
|