β ΠΠ°Π·Π°Π΄import 'dotenv/config';
const ACCESS_KEY = process.env.BEMOB_ACCESS_KEY;
const SECRET_KEY = process.env.BEMOB_SECRET_KEY;
const BASE = process.env.BEMOB_API_URL || 'https://api.bemob.com';
if (!ACCESS_KEY || !SECRET_KEY) {
console.error('β BEMOB_ACCESS_KEY / BEMOB_SECRET_KEY not set in .env');
process.exit(1);
}
const headers = {
'X-Access-Key': ACCESS_KEY,
'X-Secret-Key': SECRET_KEY,
'Content-Type': 'application/json',
};
// βββ Core API ββββββββββββββββββββββββββββββββββββββββββ
const api = async (method, path, body = null) => {
const opts = { method, headers };
if (body) opts.body = JSON.stringify(body);
const res = await fetch(`${BASE}${path}`, opts);
const data = await res.json();
if (!data.success) {
throw new Error(`BeMob API error: ${JSON.stringify(data.errors || data.message)}`);
}
return data.payload;
};
const get = (path) => api('GET', path);
const post = (path, body) => api('POST', path, body);
const put = (path, body) => api('PUT', path, body);
const del = (path) => api('DELETE', path);
// βββ Entities ββββββββββββββββββββββββββββββββββββββββββ
const user = () => get('/v1/user');
const trafficSources = () => get('/v1/traffic-sources');
const affiliateNetworks = () => get('/v1/affiliate-networks');
const campaigns = () => get('/v1/campaigns');
const offers = () => get('/v1/offers');
const landings = () => get('/v1/landings');
const flows = () => get('/v1/flows');
// βββ Create entities βββββββββββββββββββββββββββββββββββ
const createOffer = (data) => post('/v1/offers', data);
const createLanding = (data) => post('/v1/landings', data);
const createCampaign = (data) => post('/v1/campaigns', data);
const createFlow = (data) => post('/v1/flows', data);
// βββ Update entities βββββββββββββββββββββββββββββββββββ
const updateCampaign = (id, data) => put(`/v1/campaigns/${id}`, data);
const updateOffer = (id, data) => put(`/v1/offers/${id}`, data);
const updateLanding = (id, data) => put(`/v1/landings/${id}`, data);
// βββ Reports βββββββββββββββββββββββββββββββββββββββββββ
const report = (params = {}) => {
const qs = new URLSearchParams(params).toString();
return get(`/v1/reports${qs ? '?' + qs : ''}`);
};
// βββ CLI βββββββββββββββββββββββββββββββββββββββββββββββ
const [cmd, ...args] = process.argv.slice(2);
const fmt = (items) => {
if (!items || (Array.isArray(items) && items.length === 0)) {
console.log(' (ΠΏΡΡΡΠΎ)');
return;
}
if (Array.isArray(items)) {
items.forEach((item) => {
console.log(` β’ ${item.name || item.id} [${item.status || ''}] id:${item.id}`);
if (item.campaignUrl) console.log(` URL: ${item.campaignUrl}`);
if (item.url) console.log(` URL: ${item.url}`);
if (item.destinationUrl) console.log(` Dest: ${item.destinationUrl}`);
});
} else {
console.log(JSON.stringify(items, null, 2));
}
};
const commands = {
async user() {
console.log('\nπ€ BeMob User\n');
const u = await user();
console.log(` Name: ${u.firstName} ${u.lastName}`);
console.log(` Email: ${u.email}`);
console.log(` Plan: ${u.subscriptionPlan}`);
console.log(` Tracking: ${u.trackingDomainCNAME}`);
console.log(` Subdomain: ${u.subdomain}`);
},
async campaigns() {
console.log('\nπ’ Campaigns\n');
fmt(await campaigns());
},
async sources() {
console.log('\nπ Traffic Sources\n');
fmt(await trafficSources());
},
async networks() {
console.log('\nπ’ Affiliate Networks\n');
fmt(await affiliateNetworks());
},
async offers() {
console.log('\nπ― Offers\n');
fmt(await offers());
},
async landings() {
console.log('\nπ Landings\n');
fmt(await landings());
},
async flows() {
console.log('\nπ Flows\n');
fmt(await flows());
},
async all() {
await commands.user();
await commands.sources();
await commands.networks();
await commands.offers();
await commands.landings();
await commands.campaigns();
},
async setup() {
console.log('\nπ§ Setting up BeMob for arbitrage...\n');
// IDs from existing setup
const POPADS_ID = '926837ec-ba08-4b90-af96-6007057dcd2b';
const MOBIDEA_ID = 'a644ec65-dda3-4363-94c0-d21cdd936270';
const WORKSPACE_ID = '1e183b44-fe18-4ef2-ad08-44af43a4b172';
// SmartLink URLs
const CPI_URL = process.env.MOBIDEA_CPI;
const MAINSTREAM_URL = process.env.MOBIDEA_MAINSTREAM;
// Step 1: Create Offers
console.log('1οΈβ£ Creating offers...');
const offerCPI = await createOffer({
name: 'Mobidea CPI SmartLink',
url: CPI_URL,
affiliateNetworkId: MOBIDEA_ID,
workspaceId: WORKSPACE_ID,
status: 'active',
payoutType: 'auto',
enableDailyCap: false,
currencyId: 'USD',
});
console.log(` β
CPI Offer: ${offerCPI.id}`);
const offerMainstream = await createOffer({
name: 'Mobidea Mainstream SmartLink',
url: MAINSTREAM_URL,
affiliateNetworkId: MOBIDEA_ID,
workspaceId: WORKSPACE_ID,
status: 'active',
payoutType: 'auto',
enableDailyCap: false,
currencyId: 'USD',
});
console.log(` β
Mainstream Offer: ${offerMainstream.id}`);
// Step 2: Create Landing Pages
console.log('\n2οΈβ£ Creating landing pages...');
const prelanders = [
{ name: 'Opera Security Scan', sub: 'scan' },
{ name: 'Opera Speed Test', sub: 'speed' },
{ name: 'Opera Top 3 Review', sub: 'review' },
{ name: 'AdBlock Scanner', sub: 'shield' },
{ name: 'AdBlock Before/After', sub: 'clean' },
{ name: 'AdBlock YouTube', sub: 'block' },
{ name: 'Wheel Spin', sub: 'win' },
];
const landingIds = {};
const cpiOfferId = offerCPI.id;
const mainstreamOfferId = offerMainstream.id;
for (const lp of prelanders) {
const offerId = lp.sub === 'win' ? mainstreamOfferId : cpiOfferId;
const landing = await createLanding({
name: lp.name,
url: `https://${lp.sub}.clkway.online`,
workspaceId: WORKSPACE_ID,
status: 'active',
offers: [offerId],
});
landingIds[lp.sub] = landing.id;
console.log(` β
${lp.name}: ${landing.id}`);
}
// Step 3: Create Campaigns with flows (landing β offer)
console.log('\n3οΈβ£ Creating campaigns...');
// CPI campaigns (Opera + AdBlock prelanders)
const cpiLandings = ['scan', 'speed', 'review', 'shield', 'clean', 'block'];
for (const sub of cpiLandings) {
const lp = prelanders.find((p) => p.sub === sub);
const campaign = await createCampaign({
name: `CPI - ${lp.name} - Pop`,
trafficSourceId: POPADS_ID,
workspaceId: WORKSPACE_ID,
status: 'active',
costModel: 'auto',
destinationType: 'flow-built-in',
flowInline: {
rules: [{
name: 'Default',
default: true,
conditions: {},
status: 'active',
paths: [{
name: 'Path 1',
weight: 100,
status: 'active',
directLinking: false,
landings: [{ id: landingIds[sub], weight: 100 }],
offers: [{ id: offerCPI.id, weight: 100 }],
}],
}],
},
});
console.log(` β
${campaign.name}`);
console.log(` URL: ${campaign.campaignUrl}`);
}
// Mainstream campaign (Wheel)
const wheelCampaign = await createCampaign({
name: 'Mainstream - Wheel Spin - Pop',
trafficSourceId: POPADS_ID,
workspaceId: WORKSPACE_ID,
status: 'active',
costModel: 'auto',
destinationType: 'flow-built-in',
flowInline: {
rules: [{
name: 'Default',
default: true,
conditions: {},
status: 'active',
paths: [{
name: 'Path 1',
weight: 100,
status: 'active',
directLinking: false,
landings: [{ id: landingIds.win, weight: 100 }],
offers: [{ id: offerMainstream.id, weight: 100 }],
}],
}],
},
});
console.log(` β
${wheelCampaign.name}`);
console.log(` URL: ${wheelCampaign.campaignUrl}`);
console.log('\nπ Setup complete! Use campaign URLs in PopAds.\n');
},
async help() {
console.log(`
π§ BeMob CLI
ΠΡΠΎΡΠΌΠΎΡΡ:
node bemob.js user β ΠΠ½ΡΠΎ Π°ΠΊΠΊΠ°ΡΠ½ΡΠ°
node bemob.js campaigns β Π‘ΠΏΠΈΡΠΎΠΊ ΠΊΠ°ΠΌΠΏΠ°Π½ΠΈΠΉ
node bemob.js sources β ΠΡΡΠΎΡΠ½ΠΈΠΊΠΈ ΡΡΠ°ΡΠΈΠΊΠ°
node bemob.js networks β ΠΠ°ΡΡΠ½ΡΡΡΠΊΠΈΠ΅ ΡΠ΅ΡΠΈ
node bemob.js offers β ΠΡΡΠ΅ΡΡ
node bemob.js landings β ΠΠ΅Π½Π΄ΠΈΠ½Π³ΠΈ
node bemob.js flows β ΠΠΎΡΠΎΠΊΠΈ
node bemob.js all β ΠΡΡ ΡΡΠ°Π·Ρ
ΠΠ°ΡΡΡΠΎΠΉΠΊΠ°:
node bemob.js setup β Π‘ΠΎΠ·Π΄Π°ΡΡ ΠΎΡΡΠ΅ΡΡ + Π»Π΅Π½Π΄ΠΈΠ½Π³ΠΈ + ΠΊΠ°ΠΌΠΏΠ°Π½ΠΈΠΈ
`);
},
};
const run = commands[cmd] || commands.help;
run().catch((err) => {
console.error(`β ${err.message}`);
process.exit(1);
});
export { api, get, post, put, del, user, campaigns, offers, landings, trafficSources, affiliateNetworks, createOffer, createLanding, createCampaign };