analytics: creates a file with user performance information

This commit is contained in:
jimmylee 2020-09-16 01:40:03 -07:00
parent 6a98dc7bb0
commit bdaed6c1f8
9 changed files with 524 additions and 32 deletions

1
.gitignore vendored
View File

@ -11,6 +11,7 @@ package-lock.json
yarn.lock
node_modules
dist
analytics.txt
200MB_BUCKET_TEST.txt
500MB_BUCKET_TEST.txt

View File

@ -83,7 +83,7 @@ export const formatAsFilecoinConversion = (number) => {
};
export const formatAsFilecoin = (number) => {
return `${formatNumber(number)} FIL`;
return `${number} FIL`;
};
export const pluralize = (text, count) => {
@ -171,18 +171,9 @@ export const getRemainingTime = (seconds) => {
export const hexToRGBA = (hex, alpha = 1) => {
hex = hex.replace("#", "");
var r = parseInt(
hex.length == 3 ? hex.slice(0, 1).repeat(2) : hex.slice(0, 2),
16
);
var g = parseInt(
hex.length == 3 ? hex.slice(1, 2).repeat(2) : hex.slice(2, 4),
16
);
var b = parseInt(
hex.length == 3 ? hex.slice(2, 3).repeat(2) : hex.slice(4, 6),
16
);
var r = parseInt(hex.length == 3 ? hex.slice(0, 1).repeat(2) : hex.slice(0, 2), 16);
var g = parseInt(hex.length == 3 ? hex.slice(1, 2).repeat(2) : hex.slice(2, 4), 16);
var b = parseInt(hex.length == 3 ? hex.slice(2, 3).repeat(2) : hex.slice(4, 6), 16);
if (alpha) {
return "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")";
} else {
@ -214,10 +205,7 @@ export const createSlug = (text, base = "untitled") => {
return base;
}
text = text
.toString()
.toLowerCase()
.trim();
text = text.toString().toLowerCase().trim();
const sets = [
{ to: "a", from: "[ÀÁÂÃÅÆĀĂĄẠẢẤẦẨẪẬẮẰẲẴẶ]" },

View File

@ -2,7 +2,7 @@ import * as Serializers from "~/node_common/serializers";
import { runQuery } from "~/node_common/data/utilities";
export default async () => {
export default async (sanitize = true) => {
return await runQuery({
label: "GET_EVERY_USER",
queryFn: async (DB) => {
@ -12,8 +12,12 @@ export default async () => {
return [];
}
const sanitized = r.map((each) => Serializers.user(each));
return JSON.parse(JSON.stringify(sanitized));
if (sanitize) {
const sanitized = r.map((each) => Serializers.user(each));
return JSON.parse(JSON.stringify(sanitized));
}
return JSON.parse(JSON.stringify(r));
},
errorFn: async (e) => {
console.log({

View File

@ -0,0 +1,47 @@
const { performance } = require("perf_hooks");
const TIME_START = performance.now();
const timeDifference = (current, previous) => {
var msPerMinute = 60 * 1000;
var msPerHour = msPerMinute * 60;
var msPerDay = msPerHour * 24;
var msPerMonth = msPerDay * 30;
var msPerYear = msPerDay * 365;
var elapsed = current - previous;
if (elapsed < msPerMinute) {
return Math.round(elapsed / 1000) + " seconds ago";
} else if (elapsed < msPerHour) {
return Math.round(elapsed / msPerMinute) + " minutes ago";
} else if (elapsed < msPerDay) {
return Math.round(elapsed / msPerHour) + " hours ago";
} else if (elapsed < msPerMonth) {
return "approximately " + Math.round(elapsed / msPerDay) + " days ago";
} else if (elapsed < msPerYear) {
return "approximately " + Math.round(elapsed / msPerMonth) + " months ago";
} else {
return "approximately " + Math.round(elapsed / msPerYear) + " years ago";
}
};
const setTime = () => {
return `[ \x1b[33m\x1b[5m${timeDifference(performance.now(), TIME_START)}\x1b[0m ]`;
};
export const error = (message, name = "ERROR ") => {
console.log(`\x1b[1m[ \x1b[31m${name}\x1b[0m\x1b[1m ]\x1b[0m ${setTime()} ${message}`);
};
export const task = (message, name = "SCRIPT") => {
console.log(`\x1b[1m[ \x1b[32m${name}\x1b[0m\x1b[1m ]\x1b[0m ${setTime()} ${message}`);
};
export const taskTimeless = (message, name = "SCRIPT") => {
console.log(`\x1b[1m[ \x1b[32m${name}\x1b[0m\x1b[1m ]\x1b[0m ${message}`);
};
export const note = (message, name = "NOOP ") => {
console.log(`\x1b[1m[ \x1b[33m${name}\x1b[0m\x1b[1m ]\x1b[0m ${setTime()} ${message}`);
};

View File

@ -144,8 +144,6 @@ export const setupWithThread = async ({ buckets }) => {
const res = await client.getThread("buckets");
buckets.withThread(res.id.toString());
console.log(`[ buckets ] getThread success`);
} catch (error) {
if (error.message !== "Thread not found") {
throw new Error(error.message);
@ -156,19 +154,13 @@ export const setupWithThread = async ({ buckets }) => {
const threadID = newId.toString();
buckets.withThread(threadID);
console.log(`[ buckets ] newDB success`);
}
return buckets;
};
// NOTE(jim): Requires @textile/hub
export const getBucketAPIFromUserToken = async ({
user,
bucketName,
encrypted = false,
}) => {
export const getBucketAPIFromUserToken = async ({ user, bucketName, encrypted = false }) => {
const token = user.data.tokens.api;
const name = Strings.isEmpty(bucketName) ? BUCKET_NAME : bucketName;
const identity = await PrivateKey.fromString(token);

View File

@ -17,9 +17,7 @@
"electron-prod": "NODE_ENV=production next build && electron-builder --dir",
"scripts": "NODE_TLS_REJECT_UNAUTHORIZED=0 node ./scripts",
"www-setup-database": "NODE_TLS_REJECT_UNAUTHORIZED=0 node ./scripts setup-database",
"www-seed-database": "NODE_TLS_REJECT_UNAUTHORIZED=0 node ./scripts seed-database",
"www-drop-database": "NODE_TLS_REJECT_UNAUTHORIZED=0 node ./scripts drop-database",
"www-adjust-database": "NODE_TLS_REJECT_UNAUTHORIZED=0 node ./scripts adjust"
"www-seed-database": "NODE_TLS_REJECT_UNAUTHORIZED=0 node ./scripts seed-database"
},
"build": {
"appId": "com.slate",

View File

@ -0,0 +1,66 @@
import configs from "~/knexfile";
import knex from "knex";
import fs from "fs";
import * as Data from "~/node_common/data";
import * as Strings from "~/common/strings";
const envConfig = configs["development"];
const db = knex(envConfig);
console.log(`RUNNING: worker-analytics.js`);
function sortObject(obj) {
var arr = [];
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
arr.push({
key: prop,
value: obj[prop],
});
}
}
arr.sort(function (a, b) {
return b.value - a.value;
});
//arr.sort(function(a, b) { a.value.toLowerCase().localeCompare(b.value.toLowerCase()); }); //use this to sort as strings
return arr; // returns array
}
const run = async () => {
const response = await Data.getEveryUser(false);
let count = 0;
let bytes = 0;
let userMap = {};
response.forEach((user) => {
count = count + 1;
let userBytes = 0;
user.data.library[0].children.forEach((each) => {
userBytes = each.size + userBytes;
bytes = each.size + bytes;
});
userMap[user.username] = userBytes;
});
userMap = sortObject(userMap);
userMap = userMap.map((each, index) => {
return { ...each, index, value: Strings.bytesToSize(each.value) };
});
console.log(userMap);
console.log("TOTAL USER COUNT", count);
console.log("TOTAL BYTES", bytes);
console.log("TOTAL BYTES (CONVERTED)", Strings.bytesToSize(bytes));
fs.writeFile("analytics.txt", JSON.stringify({ data: userMap }, null, 2), function (err) {
if (err) return console.log(err);
});
};
run();
console.log(`FINISHED: worker-analytics.js`);
console.log(` CTRL +C to return to terminal.`);

View File

@ -0,0 +1,310 @@
import fs from "fs-extra";
import * as Environment from "~/node_common/environment";
import * as Data from "~/node_common/data";
import * as Utilities from "~/node_common/utilities";
import * as Strings from "~/common/strings";
import * as Logs from "~/node_common/script-logging";
import { Buckets, PrivateKey } from "@textile/hub";
import { v4 as uuid } from "uuid";
// 50 MB minimum
const MINIMUM_BYTES_FOR_STORAGE = 52428800;
const STORAGE_BOT_NAME = "STORAGE WORKER";
const PRACTICE_RUN = false;
const SKIP_NEW_BUCKET_CREATION = true;
const TEXTILE_KEY_INFO = {
key: Environment.TEXTILE_HUB_KEY,
secret: Environment.TEXTILE_HUB_SECRET,
};
console.log(`RUNNING: worker-heavy-stones.js`);
const run = async () => {
const response = await Data.getEveryUser(false);
const storageUsers = [];
const writable = [];
const slateAddresses = [];
let bytes = 0;
let dealUsers = 0;
let totalUsers = 0;
let encryptedUsers = 0;
// NOTE(jim): Only users who agree. Opt in by default.
for (let i = 0; i < response.length; i++) {
const user = response[i];
if (user.data.allow_automatic_data_storage) {
storageUsers.push(user);
dealUsers = dealUsers + 1;
}
if (user.data.allow_encrypted_data_storage) {
encryptedUsers = encryptedUsers + 1;
}
totalUsers = totalUsers + 1;
}
for (let i = 0; i < storageUsers.length; i++) {
const user = storageUsers[i];
const printData = {
username: storageUsers[i].username,
slateURL: `https://slate.host/${storageUsers[i].username}`,
isForcingEncryption: user.data.allow_encrypted_data_storage,
};
let buckets;
try {
const token = user.data.tokens.api;
const identity = await PrivateKey.fromString(token);
buckets = await Buckets.withKeyInfo(TEXTILE_KEY_INFO);
await buckets.getToken(identity);
buckets = await Utilities.setupWithThread({ buckets });
} catch (e) {
Logs.error(e.message);
}
let userBuckets = [];
try {
userBuckets = await buckets.list();
} catch (e) {
Logs.error(e.message);
}
let userBytes = 0;
for (let k = 0; k < userBuckets.length; k++) {
try {
const path = await buckets.listPath(userBuckets[k].key, "/");
const data = path.item;
if (data.name !== "data") {
continue;
}
userBuckets[k].bucketSize = data.size;
userBytes = userBytes + data.size;
bytes = bytes + userBytes;
} catch (e) {
Logs.error(e.message);
}
}
printData.bytes = userBytes;
// NOTE(jim): Skip people.
if (userBytes < MINIMUM_BYTES_FOR_STORAGE) {
Logs.note(`SKIP: ${user.username}`);
continue;
}
const PowergateSingleton = await Utilities.getPowergateAPIFromUserToken({
user,
});
const { powerInfo, power } = PowergateSingleton;
let balance = 0;
let address = null;
try {
if (powerInfo) {
powerInfo.balancesList.forEach((a) => {
balance = a.balance;
address = a.addr.addr;
});
} else {
Logs.error(`Powergate powerInfo does not exist.`);
}
} catch (e) {
Logs.error(e.message);
}
let storageDeals = [];
try {
const listStorageResult = await power.listStorageDealRecords({
ascending: false,
includePending: false,
includeFinal: true,
});
listStorageResult.recordsList.forEach((o) => {
storageDeals.push({
dealId: o.dealInfo.dealId,
rootCid: o.rootCid,
proposalCid: o.dealInfo.proposalCid,
pieceCid: o.dealInfo.pieceCid,
addr: o.addr,
miner: o.dealInfo.miner,
size: o.dealInfo.size,
// NOTE(jim): formatted size.
formattedSize: Strings.bytesToSize(o.dealInfo.size),
pricePerEpoch: o.dealInfo.pricePerEpoch,
startEpoch: o.dealInfo.startEpoch,
// NOTE(jim): just for point of reference on the total cost.
totalSpeculatedCost: Strings.formatAsFilecoinConversion(
o.dealInfo.pricePerEpoch * o.dealInfo.duration
),
duration: o.dealInfo.duration,
activationEpoch: o.dealInfo.activationEpoch,
time: o.time,
pending: o.pending,
});
});
} catch (e) {
Logs.error(e.message);
}
if (balance === 0) {
Logs.error(`OUT OF FUNDS: ${user.username}`);
continue;
}
if (address) {
slateAddresses.push(address);
}
printData.address = address;
printData.balanceAttoFil = balance;
Logs.taskTimeless(`\x1b[36m\x1b[1mhttps://slate.host/${user.username}\x1b[0m`);
Logs.taskTimeless(`\x1b[36m\x1b[1m${address}\x1b[0m`);
Logs.taskTimeless(`\x1b[36m\x1b[1m${Strings.bytesToSize(userBytes)} stored each deal.\x1b[0m`);
Logs.taskTimeless(
`\x1b[36m\x1b[1m${Strings.formatAsFilecoinConversion(balance)} remaining\x1b[0m`
);
console.log(storageDeals);
// NOTE(jim): tracks all buckets.
printData.buckets = [];
for (let j = 0; j < userBuckets.length; j++) {
const keyBucket = userBuckets[j];
let key;
let encrypt;
if (keyBucket.name.startsWith("open-")) {
Logs.note(`bucket found: open-data ${keyBucket.key}`);
key = keyBucket.key;
}
if (keyBucket.name.startsWith("encrypted-data-")) {
Logs.note(`bucket found: encrypted-data ${keyBucket.key}`);
key = keyBucket.key;
}
if (keyBucket.name === "data" && !SKIP_NEW_BUCKET_CREATION) {
key = null;
encrypt = !!user.data.allow_encrypted_data_storage;
// NOTE(jim): Create a new bucket
const newBucketName = encrypt ? `encrypted-data-${uuid()}` : `open-data-${uuid()}`;
// NOTE(jim): Get the root key of the bucket
let items;
try {
const path = await buckets.listPath(keyBucket.key, "/");
items = path.item;
} catch (e) {
Logs.error(e.message);
}
Logs.task(`creating new bucket: ${newBucketName}.`);
// NOTE(jim): Create a new bucket
try {
Logs.note(`attempting ...`);
if (!PRACTICE_RUN) {
let newBucket = await buckets.create(newBucketName, encrypt, items.cid);
key = newBucket.root.key;
Logs.task(`created ${newBucketName} successfully.`);
} else {
Logs.note(`practice skipping ...`);
}
} catch (e) {
Logs.error(e.message);
}
}
if (key) {
try {
if (!PRACTICE_RUN) {
await buckets.archive(key);
Logs.task(`\x1b[32mNEW DEAL SUCCESSFUL !!!\x1b[0m`);
} else {
Logs.note(`archive skipping ...`);
}
printData.buckets.push({
key,
success: false,
});
} catch (e) {
if (e.message === `the same bucket cid is already archived successfully`) {
printData.buckets.push({
key,
success: true,
});
} else {
printData.buckets.push({
key,
success: false,
});
}
Logs.note(e.message);
}
}
}
writable.push(printData);
for (let k = 0; k < printData.buckets.length; k++) {
let targetBucket = printData.buckets[k];
if (targetBucket.success) {
try {
Logs.task(`deleting bucket with key: ${targetBucket.key}`);
await buckets.remove(targetBucket.key);
Logs.task(`successfully deleted ${targetBucket.key}`);
} catch (e) {
Logs.error(e.message);
}
}
}
console.log("\n");
}
Logs.task(`total storage per run: ${Strings.bytesToSize(bytes)}`);
Logs.task(`total storage per run (with replication x5): ${Strings.bytesToSize(bytes * 5)}`);
Logs.task(`creating slate-storage-addresses.json`);
fs.writeFile(
"slate-storage-addresses.json",
JSON.stringify(
{
rootAddress:
"t3xhj6odc2cjj3z6kmxqugjjai2unacme65gnwigse4xx6jcpmfmi6jg6miqintibacluxi4ydlmolfpruznba",
addresses: slateAddresses,
},
null,
2
),
function (e) {
if (e) return Logs.error(e.message);
}
);
console.log(`${consoleName(STORAGE_BOT_NAME)} finished. \n\n`);
console.log(`FINISHED: worker-heavy-stones.js`);
console.log(` CTRL +C to return to terminal.`);
};
run();

View File

@ -0,0 +1,86 @@
{
"rootAddress": "t3xhj6odc2cjj3z6kmxqugjjai2unacme65gnwigse4xx6jcpmfmi6jg6miqintibacluxi4ydlmolfpruznba",
"addresses": [
"t3qxr7b2zxue5i4afu65m4z5zqdz5reieelxq7kknjxbdm5qshu5qpy75mwn53z6cgybfsn5wwczk4a5xozn5q",
"t3whc2f2m3oyc6ooq2efo756bgipyx3g4jsaoa5pjsmug5tnrtbunq7n6vfzrcn624p73q5iuo6qzqy7qtjolq",
"t3xebyitlbeoyiwzwrrlpyrffyv4kassujmfo6x7eslvqno4gty5y6e2k533pghvseeps7etvfxjekguhh5vja",
"t3xhj6odc2cjj3z6kmxqugjjai2unacme65gnwigse4xx6jcpmfmi6jg6miqintibacluxi4ydlmolfpruznba",
"t3wtu4igmtsqyt6yje7ydsok4y3ldluudwvji6ikr3vahcvszcxvx4qx2aeoqzkkjeaaneugeqkd3k6u2a77oq",
"t3ufy5gufgefnde7kmu6t4xqrfgvj5mppk7gawgo4dhjdspujsentra5xqab4l7akhoravpuh2u6bt7dtajs4q",
"t3sqfigf6mtwa2gflwr2vffvw4dabvkcfwxet57y6bl6hyiir5d4d2bavvvto6qbatcw4xzrlxuniailcfbxiq",
"t3xaq3j6o7ppihucad3kiefa3wk2bgcietkzunii64jmfvpd3kdbksnh7o43cbommojdjaw6f66w2ju4posaea",
"t3rmc35axdjzte2qqovwigsvb54bj7slvtzs6sarouea3rhryx6lwnrmuysdsxbkdoqxcqhl6vmhej4af5kmaq",
"t3stsrzaavir4js45znovkh5xi42pezdqrmgn5rojjlodpxjf4wcxxzxzvsf6g2s6ya7jgfh4djiwyjaiiujsa",
"t3u2y3nedj4wwptfdsqutetswtp5vidmpgqableqypbxd3jzyemydazntcvnkrj64az7rok6r3qd2xwfw2os6a",
"t3wbym4vrysh3bt3likgcddzoow6yenlaeerp6v7qjko22mbrfm5c7t5ryn645culqx7ghc6x6eshk6sofkjqa",
"t3usqxiiyhroebbv6u3bql5q3yyuvzdtcsnp4c6sciuf5fte5qpomctf5lvmrm53jonor4wtxtweqkp5fkguxa",
"t3r5tbghbwvnhtb5jfj2dkjphoclt5pxbhig2vqx4eglzhcai7sc4qoyfdiptgs4qjyv2pvpazhyjbph6wxada",
"t3uomd73rdenajgywrcxowu7zmg3hrahau64bl57mjihnably5yvsaurlp7kz755owmv4limc6p67ei45c5uxa",
"t3vfrps6g6sn3zouniezppfobdxavgdouxptl4dvh3asuine5haf5k5mytzev7cujohu5l3nlbqsagip7lhcwa",
"t3ryq46h6rppl3lbhxzgxuy6kgv3jk2ccw4ftotpowcxeeveuq4ffmqgz5rubtsnlvstqa5tktvqrlu7tyf4oa",
"t3urh7aqva2rotrsxzzxsrcsc3bsdq3y7t62qelqsm5isqlcmvvvhmrk4ryk67wdn3ydkgl66gvn7nkhjafylq",
"t3qnk7idszraxmv64sf7wiw7oufm4uunv72pfbuv7qmzcduq6gqhoq7b2hfwf7cfl3x2ptxa32oxyyr6wnl63q",
"t3uk63tsi2jrmtdiaq2dughsh7fpeexa6sem6525hfbq4cp2kdiejht4avxr3ky764yxb7eu6wvshgykp7oboa",
"t3verovflndbqlk4flpwdzcn3jpssehaqysw2onuh2pteef6jerwqpiynzutvum55gotv3yxahgfflfwsjhkya",
"t3rdc4nj2odm4uan424nhrfbztsm74rtruizkhegzintw3if5vx6zr6j7u34i2j2sq4nbml6rsrubia6uz3yxa",
"t3qi2pocdq7p4pif7h4ukvatfo6bucojtmjg5omyms6km5g4zpr6gujxydg7nvn3m4uk3kysvwlmgaodyjuakq",
"t3wusmc5tpgyfitx3ovtz26aijgszua3gglmddvp4osr3ac2ptqr2uafrre5i5k7cl5z7muuc2vljszfui4xva",
"t3ukzgvcutccdrep2gs7ryekiznwqghjq5oi75n555f5ghjw2k34glbka652psduz4jvjyxge42cmjv5yvmhbq",
"t3rfiqqk3tc53r2msjsqteu4kvu5wyym75xkf3mhsbqjvw36ehxe3v6nn6adfucgptd6yoe7uq4zwkwc7je2na",
"t3ue5wv4yv4lwy4cretyzqvzvyxkswnwnh3ox44h62fgtu2vnycrovpgp472tbdq7nrb6kwu2473fldwg6kx4q",
"t3rqo3puhnoqx2qjy5nt6zo6kmyietogq7qexjzid3dirlkdrgo23jupqp4zyxap2y7u5ov2kfjq2bjs64x7aq",
"t3rmf2ptrod62xi24inrhurnyty623jr5f55nhj4kqh7x5sdyvswspbvweij7fin4myafsrom6bxcjftmd5lzq",
"t3quikp4vuxt3wre5iskkrcwwwnrltdq73dl5epey4pkmpapm2a4ktitgs5sv2po4cq6vqv7znt7vz2qovkj6a",
"t3uajdv2vhnuz3vu7bqbxgtvgysznggt3gfh64d5pdl32tr5n4e3ezpvdm5z7fhku5nelais6vgtq3wji7blsa",
"t3r333pg23cos5ham25kc4u63pjyxzmhvts4a4g4wfxr2twiay6kblpoj6oiodanaiangoxgnylc4rquibztba",
"t3qzaxfa3fq7xk3yknv6py5rrxrdn4eh4d4w7lhhfnuldgnpdhpvkmgfx3bbi7gnrzxq3qx3nnljwdj3qdmwxq",
"t3quepkr7x7wl7rxpf5ak5vqzwmedwrzm47spkkm4mhys5w3oe46smr246lq6va5csixij63yzzywxf5rzny3q",
"t3qwnpqt3u7v67qh6skas3klfwn234fcrtji7vvmavxypmpctp27vak563aiyt3ea7vyo6yt4fqniyewkd3pka",
"t3wpjwxjieay6agc5qape6jwhgw5zogmtntclyzzlexe6z56rjcbjuowpgolomvzazlpe5q7jpdgc3m3q4rwna",
"t3w53afyb6qlxieowmeo4jmmrlf6yg2dpzxcoew3rxnixymx4s6seosnxx62jir6jjw6sumaiuo322abbkysdq",
"t3rd4xv4bt6djj7ffosq7qtlgz7pv4sgwh55duvc7btfpokbxmeekxqazjzx5dgyoar6okxhsxoodrr4tcpdaq",
"t3qokecjlovjbpxfalscwbgsimu7h2w4ptzmoshfhm4ro4ubkfeyka4rbkkzh635apxjcvkdx3222beyspa76a",
"t3vjv6tfve7x43b3pksrcrfuyywrwczg6z27mbsihutu5s2vxvhnujnhzizdnzu5xodmfgwsiuiskgwwp66p4q",
"t3xgzmo4cjqy5wwghjquq5xvbvfh2zmrepsv245au44ankfe72qd4c7vdqwca5vzpbfvls36uedspi4f5wji7q",
"t3qr672wse5si6qdnru5wyeayihus5mucakxpxjgfn5zum6dhfapasoiesoffymimiv3ynrjh3ql7ombapdqaa",
"t3r5xruqmuuk7h2kihea6elzf7wwt2c2j55xakkykvu432tvf26etc2ljx4cssvbcxhlxompjgdnzgitgn3spq",
"t3qyt2qgzefxbjpkpztalme27ye4m6762sfomyengjdr2u63rqola7gjlripnrkaz7uxgxg7khdwpdq5s4cxea",
"t3w2w453fvef2kdo5vcs5wyxtiypbzg6nk7v76jx3l5ovaucll74f6zh4xlbsiizvxoatev5wbnw2ukvzntxbq",
"t3su4zltzna2zrfit6fmxtqinv3fptfb4fa7csprxtt6alyxx2ws4olp7t7bvjvgopmaj7iyhygook662r63aa",
"t3sq5367awbyus4lpv3oqednskwubvw77jzxjxlwxd6hyjcza7yaorki5o32chpoprb7rilp5kbnrqi3p2h3xq",
"t3u7bthtl2k7f5rcibzrfmx6rdljgvg4za57ri6gl5ab4qvkrfefnmv6bfpqgfqxciyy2yygel6rdbbvbwe2va",
"t3vctbb3gfwxwkyt6fnwussv4p2fv7d4okutt4suas5a3pifvga2nlruwmj75fys2yw2jzqtmtt7b32xgcnmpq",
"t3uvqekf2xerew2q4mm3eesfttiup7bwf22vnqiktyniotynzuv7mcwi3myadaemiwqweqz7gw6vgqsnzzra7a",
"t3ussvk7srutroy5zghmyqispbx5zsbmyck4d5cuc4hlpwn4ho5glpipnqmvgftijqkxi4ybjyz4jlgrbvpuoa",
"t3r4irvw2zz6odhrl6pwhsfjmxpsp3lcoejsf722ph5mshvy2rxad3ubp6ekrrt6onsugrarvzwz2asbdka75q",
"t3vkplobic7r4onyk6ounnahpzfklo3wfzxkccyf5ppm4psocqwbfkhzw74fbgp7mk6yxcnzutm3bnhcjnn45a",
"t3sjuykzboxk7whkyui3xdcgo3sw6ld2c4vat5dmmevbyfoewsmepduxdaovduj2onpt4dmhfwxejbacyg7wea",
"t3qb34p2usj5rl5yu4tqtos3lslaf6b3sfu54qbqzpfjmzhgcde5serunkesfflhoydhmb2dhbergtjhtph5xq",
"t3wspizngi4bu54ovd2fad5oioktj55knc4gs2vzlpmng6kitkhbxuzm5ormjpzcqqczdlnzimhprlvmh3l5xq",
"t3rqwdvxfjib452d4ativtcumcmgmupha3xzp24obofy5d3wtdno25bsrmahlfq4esuxow3mu5d26mbnhwfhdq",
"t3s2f4ssatrgc5gcdz76fzafx2ziurpqhk2cjasyuf2hzjbg2ftysn7ryuxo2ucjmiz3y2aagsgcjkox3qdmaq",
"t3qsb6pc6dkouflhjiwctnsdutv3ikzph4v2rk5bo6ulz6xo3u4axiyj6i2vg4tch7fv4z5tcpkzbzlrrez7fa",
"t3uai5mpbqb35kmpamqa5ms5l6dxsaxo6xix32q3gqfd2ex46jg2notdyv5i65tklwv5qjjaq6ra4qlikrzpca",
"t3r7ojjjjclqcd6c2ojp54kybeatz7kg3cshhxjxx4aagqehww5g6tk6pympccfi6w2mrelzbodjyeijrhxh2a",
"t3uz2huvzqcmt6ctqtj2htuprrztsf7hfkfiyudsmafl7usqup7lb5rab7mpyhkphgtfd2xrf6cik3hhpyippq",
"t3querv4g4uwn6qjnhj27nf373czskhoshcdm2wm7wp67gxffakekf7om7zq3gs6xqveix7wfrajelrl4ypv5q",
"t3qha5puqjbgarh42zp575ma57aqw3rf2czymjabfoq3mmevqzgih4iipx5s6k4m33ocqvre2fzcp26u75ogzq",
"t3rbvnzlok5gb7boilyiobzlaojh6rlgqvpchusyfc6jasfnnyhohnz6gmdvabwytkerpk2lo4is35ku4lq5ga",
"t3qzvnd2cn5sqml3tbz4x7mnu3hlm2ezckt2pxl62js3ulbwln2cd3kavjw5qegaqjyyjdwm7mcrarfsxgfwxq",
"t3veex27t4p2rnzawfn36aynumydactoylld2yyrt4jjabzxbqaysbpu2xzrulah6mypiatg2wp6uyegtppoqa",
"t3u5ffm3d2mx2vnzvv4h3sux5kmpx2dfcqkr64yjnykkkqq5rjufxertkyg52zc72gj5gbnqomvdvafrd6hmza",
"t3uaqwp5b67fn5x7a5irfvs6w2n5euxwvgvhimmzucypo2lm4p2pbmh522mgqpe7p4ciw2tngzdwaafhn6g3rq",
"t3r6kvxhc76q62lctpejdykhvbcueaion7uko3tvoirvyv7fhyzby25r7htwooiflffkovmzd6lq6dyizwhcea",
"t3vzpex72h34yt4kjbrksvqxeep5go7cwq76c2sbmqp45cecxs5qud53ogg5tm2mwihh2mmejralc4bzbxolqa",
"t3qkm7g3vyf76mqglnvjj5immp3vkwaygzl7c4jpyirhpm3wtoucaxgvwki3gna2kynhehsbbrumwfgngt4feq",
"t3qcsvmreumiuhknyloeg4ugliklvnxdy4lckasetxt3zewydsay2d3244oowwfutynf7plgrx5b75phf53jxa",
"t3s7ebu6ptfoxzryiqqyyylu7q6hbumr4jvnromqcmsnjd34w3dgwzrknvvrxdlaiqruuyzhivbaioq3epqveq",
"t3xdukxl4yoz3ai72m43dl6hpkzawads5cg2iu3zr7jplp2ulalmnalleiks7pbhvo5kcaqt63pd3v34sbxgga",
"t3rkocg7q4sxbqbdnizy2y6bqjfrtusyo7azvql3dzogc2p5wl5uytpbjv4uyqkkf6blzmjiau4sq6el4tu27q",
"t3rlxlrmqh66mdeurawzlrw26idtw2pd52ea5vhuvjz224oyk6uzi3yvjqpplsbfu7rslgewwgcbkqsykrhivq",
"t3vvireffp6tuqrxovi6xy3wbrega74su2dxb2j6vui726jzmnfn2a3tlcxhg4fhuw6gjqmm5bvzpe6etnkg5q",
"t3q4yok6riuzggj3ua3grekw6oqdleb5bgztdtrevfslwyf2z55uosbfturojfr2jf65xcew3qfc7ichwpd4la",
"t3xh5lu2wshzsq45hwc7fcdj2vnz5qljmp4swk5h56djp6smtti4ctan2tovebysqybzfcfuqjbn5eheh4sjoq",
"t3rojfwoaaxtv3kgtnzisbijsctd74mr6fe46gw6xno6cw5szdvrprmq4hbvjas562iyd7x26uqzsfuyd5zfla"
]
}