mirror of
https://github.com/tiennm99/store-scraper-bot.git
synced 2026-08-20 02:28:46 +00:00
refactor: drop Mongo class discriminator + delete src/models/
Inline trivial factory bodies into the repos and scrapers that used them. The class:/_id: fields were Java-Mongo parity artifacts that nothing in this codebase reads — Redis docs with the old fields still parse fine; the next write drops them.
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
import store from 'app-store-scraper';
|
||||
import { newAppleApp } from '../models/apple-app.js';
|
||||
|
||||
// Mirrors Java AppStoreScraper (api/apple/AppStoreScraper.java).
|
||||
// Calls the `app-store-scraper` npm lib directly (no HTTP roundtrip).
|
||||
|
||||
export function buildAppleRequestByTrackId(id, country) {
|
||||
@@ -20,9 +18,6 @@ export function createAppleScraper(config, repository) {
|
||||
return store.app(req);
|
||||
}
|
||||
|
||||
// rawApp returns a JSON-text representation of the parsed object so the
|
||||
// /rawappleapp command and any other text consumers stay parity-compatible
|
||||
// with the previous HTTP-text response.
|
||||
async function rawApp(req) {
|
||||
return JSON.stringify(await app(req));
|
||||
}
|
||||
@@ -30,7 +25,7 @@ export function createAppleScraper(config, repository) {
|
||||
async function cache(resp) {
|
||||
if (!resp || !resp.appId) return;
|
||||
try {
|
||||
await repo.save(newAppleApp(resp.appId, resp, Date.now()));
|
||||
await repo.save({ _id: resp.appId, app: resp, millis: Date.now() });
|
||||
} catch (err) {
|
||||
logger.warn({ appId: resp.appId, err: err.message }, 'failed to cache apple app');
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import gplay from 'google-play-scraper';
|
||||
import { newGoogleApp } from '../models/google-app.js';
|
||||
|
||||
// Mirrors Java GooglePlayScraper (api/google/GooglePlayScraper.java).
|
||||
// Calls the `google-play-scraper` npm lib directly (no HTTP roundtrip).
|
||||
|
||||
export function buildGoogleRequest(appId, country) {
|
||||
@@ -16,9 +14,6 @@ export function createGoogleScraper(config, repository) {
|
||||
return gplay.app(req);
|
||||
}
|
||||
|
||||
// rawApp returns a JSON-text representation of the parsed object so the
|
||||
// /rawgoogleapp command and any other text consumers stay parity-compatible
|
||||
// with the previous HTTP-text response.
|
||||
async function rawApp(req) {
|
||||
return JSON.stringify(await app(req));
|
||||
}
|
||||
@@ -28,7 +23,7 @@ export function createGoogleScraper(config, repository) {
|
||||
const id = resp.appId || fallbackId;
|
||||
if (!id) return;
|
||||
try {
|
||||
await repo.save(newGoogleApp(id, resp, Date.now()));
|
||||
await repo.save({ _id: id, app: resp, millis: Date.now() });
|
||||
} catch (err) {
|
||||
logger.warn({ appId: id, err: err.message }, 'failed to cache google app');
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
// Admin singleton document — Java parity (_id="admin", class="Admin").
|
||||
export const ADMIN_ID = 'admin';
|
||||
|
||||
export function newAdmin() {
|
||||
return { _id: ADMIN_ID, class: 'Admin', groups: [] };
|
||||
}
|
||||
|
||||
export function adminAddGroup(admin, groupId) {
|
||||
if (admin.groups.includes(groupId)) return false;
|
||||
admin.groups.push(groupId);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function adminRemoveGroup(admin, groupId) {
|
||||
const i = admin.groups.indexOf(groupId);
|
||||
if (i < 0) return false;
|
||||
admin.groups.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function adminHasGroup(admin, groupId) {
|
||||
return admin.groups.includes(groupId);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
// AppleApp cache entry — Java parity (_id=appId, class="AppleApp").
|
||||
// TTL is enforced by Upstash Redis EX, so no isExpired helper.
|
||||
export function newAppleApp(appId, response, millis) {
|
||||
return { _id: appId, class: 'AppleApp', app: response, millis };
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
// GoogleApp cache entry — Java parity (_id=appId, class="GoogleApp").
|
||||
// TTL is enforced by Upstash Redis EX, so no isExpired helper.
|
||||
export function newGoogleApp(appId, response, millis) {
|
||||
return { _id: appId, class: 'GoogleApp', app: response, millis };
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
// Group document — Java parity (_id is string form of Telegram chat ID).
|
||||
export function groupIdToKey(groupId) {
|
||||
return String(groupId);
|
||||
}
|
||||
|
||||
export function groupKeyToId(key) {
|
||||
return Number(key);
|
||||
}
|
||||
|
||||
export function newGroup(groupId) {
|
||||
return {
|
||||
_id: groupIdToKey(groupId),
|
||||
class: 'Group',
|
||||
appleApps: [],
|
||||
googleApps: [],
|
||||
};
|
||||
}
|
||||
|
||||
function addApp(list, appId, country) {
|
||||
if (list.some((a) => a.appId === appId)) return false;
|
||||
list.push({ appId, country });
|
||||
return true;
|
||||
}
|
||||
|
||||
function removeApp(list, appId) {
|
||||
const i = list.findIndex((a) => a.appId === appId);
|
||||
if (i < 0) return false;
|
||||
list.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function groupAddAppleApp(group, appId, country) {
|
||||
return addApp(group.appleApps, appId, country);
|
||||
}
|
||||
|
||||
export function groupRemoveAppleApp(group, appId) {
|
||||
return removeApp(group.appleApps, appId);
|
||||
}
|
||||
|
||||
export function groupAddGoogleApp(group, appId, country) {
|
||||
return addApp(group.googleApps, appId, country);
|
||||
}
|
||||
|
||||
export function groupRemoveGoogleApp(group, appId) {
|
||||
return removeApp(group.googleApps, appId);
|
||||
}
|
||||
@@ -1,48 +1,44 @@
|
||||
import { getJson, putJson } from './upstash.js';
|
||||
import {
|
||||
ADMIN_ID,
|
||||
adminAddGroup,
|
||||
adminHasGroup,
|
||||
adminRemoveGroup,
|
||||
newAdmin,
|
||||
} from '../models/admin.js';
|
||||
|
||||
// Upstash-backed admin singleton — Java parity at the document level
|
||||
// (logical key 'admin' holds the same shape Mongo stored at _id="admin").
|
||||
// The physical Redis key carries the configured KEY_PREFIX (handled by adapter).
|
||||
const ADMIN_KEY = 'admin';
|
||||
|
||||
// Upstash-backed admin singleton. Holds the authorized chat ID allowlist.
|
||||
export function createAdminRepository(handle) {
|
||||
async function init() {
|
||||
const existing = await getJson(handle, ADMIN_ID);
|
||||
const existing = await getJson(handle, ADMIN_KEY);
|
||||
if (existing) return;
|
||||
await save(newAdmin());
|
||||
await save({ _id: ADMIN_KEY, groups: [] });
|
||||
}
|
||||
|
||||
async function getAdmin() {
|
||||
const doc = await getJson(handle, ADMIN_ID);
|
||||
return doc ?? newAdmin();
|
||||
const doc = await getJson(handle, ADMIN_KEY);
|
||||
return doc ?? { _id: ADMIN_KEY, groups: [] };
|
||||
}
|
||||
|
||||
async function save(admin) {
|
||||
await putJson(handle, ADMIN_ID, admin);
|
||||
await putJson(handle, ADMIN_KEY, admin);
|
||||
}
|
||||
|
||||
async function addGroup(groupId) {
|
||||
const admin = await getAdmin();
|
||||
if (!adminAddGroup(admin, groupId)) return false;
|
||||
if (admin.groups.includes(groupId)) return false;
|
||||
admin.groups.push(groupId);
|
||||
await save(admin);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function removeGroup(groupId) {
|
||||
const admin = await getAdmin();
|
||||
if (!adminRemoveGroup(admin, groupId)) return false;
|
||||
const i = admin.groups.indexOf(groupId);
|
||||
if (i < 0) return false;
|
||||
admin.groups.splice(i, 1);
|
||||
await save(admin);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function hasGroup(groupId) {
|
||||
const admin = await getAdmin();
|
||||
return adminHasGroup(admin, groupId);
|
||||
return admin.groups.includes(groupId);
|
||||
}
|
||||
|
||||
async function getAllGroups() {
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
import { del, getJson, putJson } from './upstash.js';
|
||||
import {
|
||||
groupAddAppleApp,
|
||||
groupAddGoogleApp,
|
||||
groupIdToKey,
|
||||
groupRemoveAppleApp,
|
||||
groupRemoveGoogleApp,
|
||||
newGroup,
|
||||
} from '../models/group.js';
|
||||
|
||||
// Upstash-backed per-group state. Logical key shape: `group:{chatId}`.
|
||||
// Physical key gets KEY_PREFIX prepended by the adapter.
|
||||
export function createGroupRepository(handle) {
|
||||
function key(groupId) {
|
||||
return `group:${groupIdToKey(groupId)}`;
|
||||
return `group:${String(groupId)}`;
|
||||
}
|
||||
|
||||
function newGroup(groupId) {
|
||||
return { _id: String(groupId), appleApps: [], googleApps: [] };
|
||||
}
|
||||
|
||||
async function exists(groupId) {
|
||||
@@ -38,6 +33,19 @@ export function createGroupRepository(handle) {
|
||||
await del(handle, key(groupId));
|
||||
}
|
||||
|
||||
function addApp(list, appId, country) {
|
||||
if (list.some((a) => a.appId === appId)) return false;
|
||||
list.push({ appId, country });
|
||||
return true;
|
||||
}
|
||||
|
||||
function removeApp(list, appId) {
|
||||
const i = list.findIndex((a) => a.appId === appId);
|
||||
if (i < 0) return false;
|
||||
list.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function mutateAndSave(groupId, mutator) {
|
||||
const group = await getGroup(groupId);
|
||||
if (!mutator(group)) return false;
|
||||
@@ -52,12 +60,12 @@ export function createGroupRepository(handle) {
|
||||
initGroup,
|
||||
deleteGroup,
|
||||
addAppleApp: (groupId, appId, country) =>
|
||||
mutateAndSave(groupId, (g) => groupAddAppleApp(g, appId, country)),
|
||||
mutateAndSave(groupId, (g) => addApp(g.appleApps, appId, country)),
|
||||
removeAppleApp: (groupId, appId) =>
|
||||
mutateAndSave(groupId, (g) => groupRemoveAppleApp(g, appId)),
|
||||
mutateAndSave(groupId, (g) => removeApp(g.appleApps, appId)),
|
||||
addGoogleApp: (groupId, appId, country) =>
|
||||
mutateAndSave(groupId, (g) => groupAddGoogleApp(g, appId, country)),
|
||||
mutateAndSave(groupId, (g) => addApp(g.googleApps, appId, country)),
|
||||
removeGoogleApp: (groupId, appId) =>
|
||||
mutateAndSave(groupId, (g) => groupRemoveGoogleApp(g, appId)),
|
||||
mutateAndSave(groupId, (g) => removeApp(g.googleApps, appId)),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user