@hasna/experts 0.0.5 → 0.0.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/dist/cli/index.js CHANGED
@@ -1208,6 +1208,34 @@ async function enrichExpert(db, e, opts) {
1208
1208
  }
1209
1209
  return { ok: true, notFound: false, tweets: tweetCount, avatar, rateLimited: false, quotaExhausted: false };
1210
1210
  }
1211
+ async function backfillAvatars(db, opts = {}) {
1212
+ const log = opts.onLog ?? (() => {});
1213
+ const fetchFn = opts.fetchFn ?? fetch;
1214
+ const delayMs = opts.delayMs ?? 150;
1215
+ const experts = db.list({ source: opts.source });
1216
+ const res = { downloaded: 0, skipped: 0, failed: 0 };
1217
+ for (const e of experts) {
1218
+ if (e.avatarLocal || !e.avatar) {
1219
+ res.skipped++;
1220
+ continue;
1221
+ }
1222
+ try {
1223
+ const path = await downloadAvatar(e.avatar, e, fetchFn);
1224
+ if (path) {
1225
+ db.setAvatarLocal(e.source, e.sourceId, path);
1226
+ res.downloaded++;
1227
+ if (res.downloaded % 100 === 0)
1228
+ log(` avatars: ${res.downloaded} downloaded`);
1229
+ } else {
1230
+ res.failed++;
1231
+ }
1232
+ } catch {
1233
+ res.failed++;
1234
+ }
1235
+ await sleep2(delayMs);
1236
+ }
1237
+ return res;
1238
+ }
1211
1239
  async function enrichX(db, opts = {}) {
1212
1240
  const log = opts.onLog ?? (() => {});
1213
1241
  const client = opts.client ?? new ConnectorsClient;
@@ -1269,7 +1297,7 @@ async function enrichX(db, opts = {}) {
1269
1297
  }
1270
1298
 
1271
1299
  // src/cli/index.ts
1272
- var VERSION = "0.0.5";
1300
+ var VERSION = "0.0.6";
1273
1301
  function openDb() {
1274
1302
  const opts = program.opts();
1275
1303
  return new ExpertsDB(opts.db || defaultDbPath());
@@ -1333,6 +1361,21 @@ program.command("enrich [source]").description("Enrich experts via X/Twitter: pr
1333
1361
  console.log(chalk2.dim(`progress: ${after.enriched}/${after.withHandle} enriched`));
1334
1362
  db.close();
1335
1363
  });
1364
+ program.command("avatars [source]").description("Download + properly name profile pictures for experts missing one").option("--delay <ms>", "delay between downloads", (v) => parseInt(v, 10), 150).action(async (source, cmdOpts) => {
1365
+ const db = openDb();
1366
+ requireData(db);
1367
+ console.error(chalk2.dim("Backfilling profile pictures from source media\u2026"));
1368
+ const res = await backfillAvatars(db, {
1369
+ source,
1370
+ delayMs: cmdOpts.delay,
1371
+ onLog: (m) => process.stderr.write(chalk2.dim(m + `
1372
+ `))
1373
+ });
1374
+ console.log(chalk2.green(`\u2713 ${res.downloaded} avatars downloaded`) + chalk2.dim(` (${res.skipped} already had one or no URL, ${res.failed} failed)`));
1375
+ const total = db.enrichmentStats(source).avatars;
1376
+ console.log(chalk2.dim(`total experts with a named avatar: ${total}`));
1377
+ db.close();
1378
+ });
1336
1379
  program.command("tweets <idOrSlug>").description("Show an expert's stored recent tweets").option("-s, --source <name>", "disambiguate by source").option("-n, --limit <n>", "max tweets", (v) => parseInt(v, 10), 10).action((idOrSlug, cmdOpts) => {
1337
1380
  const db = openDb();
1338
1381
  const e = db.get(idOrSlug, cmdOpts.source);
package/dist/enrich.d.ts CHANGED
@@ -41,6 +41,23 @@ export declare function enrichExpert(db: ExpertsDB, e: Expert, opts: EnrichOptio
41
41
  rateLimited: boolean;
42
42
  quotaExhausted: boolean;
43
43
  }>;
44
+ export interface AvatarBackfillResult {
45
+ downloaded: number;
46
+ skipped: number;
47
+ failed: number;
48
+ }
49
+ /**
50
+ * Download + properly name profile pictures for every expert that doesn't have
51
+ * a local one yet, using the source's own avatar URL (e.g. intro.co media). This
52
+ * needs no third-party API, so it completes "a named profile picture for every
53
+ * expert" even when X enrichment is unavailable.
54
+ */
55
+ export declare function backfillAvatars(db: ExpertsDB, opts?: {
56
+ source?: string;
57
+ delayMs?: number;
58
+ fetchFn?: typeof fetch;
59
+ onLog?: (m: string) => void;
60
+ }): Promise<AvatarBackfillResult>;
44
61
  /** Resumable, throttled enrichment over all experts with a Twitter handle. */
45
62
  export declare function enrichX(db: ExpertsDB, opts?: EnrichOptions): Promise<EnrichResult>;
46
63
  //# sourceMappingURL=enrich.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"enrich.d.ts","sourceRoot":"","sources":["../src/enrich.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAmB,MAAM,SAAS,CAAC;AAIvD,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC,GAAG,MAAM,CAQxF;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKtD;AAED,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,mFAAmF;AACnF,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC,EAC5D,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiBxB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,uEAAuE;AACvE,wBAAsB,YAAY,CAChC,EAAE,EAAE,SAAS,EACb,CAAC,EAAE,MAAM,EACT,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,cAAc,EAAE,OAAO,CAAA;CAAE,CAAC,CAyE7H;AAED,8EAA8E;AAC9E,wBAAsB,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAqE5F"}
1
+ {"version":3,"file":"enrich.d.ts","sourceRoot":"","sources":["../src/enrich.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAmB,MAAM,SAAS,CAAC;AAIvD,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC,GAAG,MAAM,CAQxF;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKtD;AAED,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,mFAAmF;AACnF,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC,EAC5D,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiBxB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,uEAAuE;AACvE,wBAAsB,YAAY,CAChC,EAAE,EAAE,SAAS,EACb,CAAC,EAAE,MAAM,EACT,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,cAAc,EAAE,OAAO,CAAA;CAAE,CAAC,CAyE7H;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,EAAE,EAAE,SAAS,EACb,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAAO,GACpG,OAAO,CAAC,oBAAoB,CAAC,CA0B/B;AAED,8EAA8E;AAC9E,wBAAsB,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAqE5F"}
package/dist/index.d.ts CHANGED
@@ -10,7 +10,7 @@ export { crawlSource, type CrawlResult } from "./crawl";
10
10
  export { getSource, listSources, registerSource, IntroSource } from "./sources";
11
11
  export { IntroClient } from "./sources/intro-api";
12
12
  export { normalizeIntroExpert, slugFromUrl } from "./sources/intro";
13
- export { enrichX, enrichExpert, downloadAvatar, avatarBasename, handleFromSocial, type EnrichOptions, type EnrichResult } from "./enrich";
13
+ export { enrichX, enrichExpert, backfillAvatars, downloadAvatar, avatarBasename, handleFromSocial, type EnrichOptions, type EnrichResult, type AvatarBackfillResult } from "./enrich";
14
14
  export { ConnectorsClient, defaultRunner, extractJson, type ConnectorRunner, type ConnectorResult } from "./connectors";
15
15
  export { inferTags, expertText } from "./graph";
16
16
  export * as format from "./format";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,MAAM,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAC1I,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACxH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,MAAM,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACtL,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACxH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -1080,6 +1080,34 @@ async function enrichExpert(db, e, opts) {
1080
1080
  }
1081
1081
  return { ok: true, notFound: false, tweets: tweetCount, avatar, rateLimited: false, quotaExhausted: false };
1082
1082
  }
1083
+ async function backfillAvatars(db, opts = {}) {
1084
+ const log = opts.onLog ?? (() => {});
1085
+ const fetchFn = opts.fetchFn ?? fetch;
1086
+ const delayMs = opts.delayMs ?? 150;
1087
+ const experts = db.list({ source: opts.source });
1088
+ const res = { downloaded: 0, skipped: 0, failed: 0 };
1089
+ for (const e of experts) {
1090
+ if (e.avatarLocal || !e.avatar) {
1091
+ res.skipped++;
1092
+ continue;
1093
+ }
1094
+ try {
1095
+ const path = await downloadAvatar(e.avatar, e, fetchFn);
1096
+ if (path) {
1097
+ db.setAvatarLocal(e.source, e.sourceId, path);
1098
+ res.downloaded++;
1099
+ if (res.downloaded % 100 === 0)
1100
+ log(` avatars: ${res.downloaded} downloaded`);
1101
+ } else {
1102
+ res.failed++;
1103
+ }
1104
+ } catch {
1105
+ res.failed++;
1106
+ }
1107
+ await sleep2(delayMs);
1108
+ }
1109
+ return res;
1110
+ }
1083
1111
  async function enrichX(db, opts = {}) {
1084
1112
  const log = opts.onLog ?? (() => {});
1085
1113
  const client = opts.client ?? new ConnectorsClient;
@@ -1781,6 +1809,7 @@ export {
1781
1809
  defaultRunner,
1782
1810
  defaultDbPath,
1783
1811
  crawlSource,
1812
+ backfillAvatars,
1784
1813
  avatarBasename,
1785
1814
  IntroSource,
1786
1815
  IntroClient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/experts",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "Crawl expert marketplaces (intro.co and more) into a local store, then query them via CLI or a remote HTTP API.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",