@praeko/cli 0.0.0-stage → 0.1.1
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/CHANGELOG.md +13 -0
- package/LICENSE +21 -0
- package/README.md +58 -2
- package/dist/chunks/adaptadores-ESUZ36N2.mjs +18 -0
- package/dist/chunks/buffer-SP52FWTI.mjs +14 -0
- package/dist/chunks/chunk-LCFK6X55.mjs +16 -0
- package/dist/chunks/chunk-LLXYUCTT.mjs +184 -0
- package/dist/chunks/chunk-QDZLRBTP.mjs +1125 -0
- package/dist/chunks/chunk-TTGQU5CG.mjs +238 -0
- package/dist/chunks/chunk-UK3UKRPR.mjs +363 -0
- package/dist/chunks/chunk-VJRMAQCR.mjs +56 -0
- package/dist/chunks/chunk-Z325UM27.mjs +34 -0
- package/dist/chunks/chunk-ZZKGFOPH.mjs +415 -0
- package/dist/chunks/instagram-GNY73ESY.mjs +293 -0
- package/dist/chunks/instagram-meta-COY6BTNX.mjs +412 -0
- package/dist/chunks/local-FMMUWECN.mjs +14 -0
- package/dist/chunks/registro-5PYVQ5LJ.mjs +12 -0
- package/dist/chunks/remoto-OAB5Y2MS.mjs +13 -0
- package/dist/chunks/tiktok-QOUHOWDU.mjs +311 -0
- package/dist/chunks/tiktok-api-36DV2H43.mjs +309 -0
- package/dist/chunks/youtube-RZ4XTUZW.mjs +199 -0
- package/dist/chunks/youtube-analytics-3KIW2ZIW.mjs +109 -0
- package/dist/chunks/youtube-api-Z2RP5D2V.mjs +94 -0
- package/dist/chunks/youtube-comunidade-OAK5GGK4.mjs +25 -0
- package/dist/praeko.mjs +854 -0
- package/package.json +47 -4
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
|
|
2
|
+
import "./chunk-LCFK6X55.mjs";
|
|
3
|
+
|
|
4
|
+
// ../core/src/provedores/instagram-meta.mjs
|
|
5
|
+
var REF_META = "instagram/meta";
|
|
6
|
+
var HOST_PADRAO = "https://graph.facebook.com";
|
|
7
|
+
var VERSAO = "v23.0";
|
|
8
|
+
var HOSTS_VALIDOS = ["https://graph.facebook.com", "https://graph.instagram.com"];
|
|
9
|
+
var num = (x) => x != null && x !== "" && !Number.isNaN(+x) ? +x : null;
|
|
10
|
+
var ate = (n, max, padrao = 25) => Math.max(1, Math.min(max, Math.floor(+n) || padrao));
|
|
11
|
+
var falha = (status, j, txt) => Object.assign(new Error(`Instagram: ${j?.error?.message || String(txt).slice(0, 200)}${j?.error?.code ? ` (c\xF3digo ${j.error.code}${j.error.error_subcode ? "/" + j.error.error_subcode : ""})` : ""}`), { status: status || 502, codigo: j?.error?.code ?? null, subcodigo: j?.error?.error_subcode ?? null, mensagemMeta: j?.error?.message ?? null });
|
|
12
|
+
async function lerToken(ctx) {
|
|
13
|
+
const bruto = await ctx.segredos.obter(REF_META);
|
|
14
|
+
if (!bruto) throw Object.assign(new Error("Instagram (Meta) n\xE3o conectado: praeko conta instagram enviar"), { status: 409, codigoLocal: "meta_nao_conectado" });
|
|
15
|
+
const t = typeof bruto === "string" ? JSON.parse(bruto) : bruto;
|
|
16
|
+
if (!t?.token || !t?.userId) throw Object.assign(new Error("token do Instagram (Meta) incompleto: reenvie com praeko conta instagram enviar"), { status: 409, codigoLocal: "meta_nao_conectado" });
|
|
17
|
+
return t;
|
|
18
|
+
}
|
|
19
|
+
var DIALOGO_OAUTH = "https://www.facebook.com/dialog/oauth";
|
|
20
|
+
var LONGO_MIN_S = 30 * 86400;
|
|
21
|
+
function urlAutorizacaoOAuth({ appId, configId, escopos, redirect, estado }) {
|
|
22
|
+
const u = new URL(DIALOGO_OAUTH);
|
|
23
|
+
u.searchParams.set("client_id", appId);
|
|
24
|
+
u.searchParams.set("display", "popup");
|
|
25
|
+
if (escopos) u.searchParams.set("scope", escopos);
|
|
26
|
+
else {
|
|
27
|
+
u.searchParams.set("config_id", configId);
|
|
28
|
+
u.searchParams.set("override_default_response_type", "true");
|
|
29
|
+
}
|
|
30
|
+
u.searchParams.set("redirect_uri", redirect);
|
|
31
|
+
u.searchParams.set("response_type", "code");
|
|
32
|
+
u.searchParams.set("state", estado);
|
|
33
|
+
u.searchParams.set("prompt", "login");
|
|
34
|
+
return u.toString();
|
|
35
|
+
}
|
|
36
|
+
async function grafoOAuth(ctx, caminho, params) {
|
|
37
|
+
const u = new URL(`${HOST_PADRAO}/${VERSAO}/${caminho}`);
|
|
38
|
+
for (const [k, v] of Object.entries(params)) if (v != null) u.searchParams.set(k, v);
|
|
39
|
+
const r = await (ctx.fetchFn || fetch)(u.toString(), { method: "GET" });
|
|
40
|
+
const txt = await r.text();
|
|
41
|
+
let j;
|
|
42
|
+
try {
|
|
43
|
+
j = txt ? JSON.parse(txt) : {};
|
|
44
|
+
} catch {
|
|
45
|
+
throw falha(r.status, null, txt);
|
|
46
|
+
}
|
|
47
|
+
if (j.error || !r.ok) throw falha(r.status, j, txt);
|
|
48
|
+
return j;
|
|
49
|
+
}
|
|
50
|
+
var tokenDe = (j, etapa) => {
|
|
51
|
+
if (!j?.access_token) throw Object.assign(new Error(`Instagram: a Meta n\xE3o devolveu o token (${etapa})`), { status: 502, codigo: null });
|
|
52
|
+
const n = num(j.expires_in);
|
|
53
|
+
return { token: String(j.access_token), expiraEmS: n && n > 0 ? n : null };
|
|
54
|
+
};
|
|
55
|
+
async function trocarCodigoOAuth(ctx, { appId, appSecret, codigo, redirect }) {
|
|
56
|
+
return tokenDe(await grafoOAuth(ctx, "oauth/access_token", { client_id: appId, client_secret: appSecret, redirect_uri: redirect, code: codigo }), "troca do c\xF3digo");
|
|
57
|
+
}
|
|
58
|
+
async function estenderTokenOAuth(ctx, { appId, appSecret, token }) {
|
|
59
|
+
const r = tokenDe(await grafoOAuth(ctx, "oauth/access_token", { grant_type: "fb_exchange_token", client_id: appId, client_secret: appSecret, fb_exchange_token: token }), "troca por longa dura\xE7\xE3o");
|
|
60
|
+
return { ...r, expiraEmS: r.expiraEmS ?? 60 * 86400 };
|
|
61
|
+
}
|
|
62
|
+
var CAMPOS_IG = "id,username,name,profile_picture_url,followers_count";
|
|
63
|
+
var contaIg = (ig, pagina) => ({ pagina, instagram: { id: String(ig.id), usuario: ig.username || null, nome: ig.name || null, avatar: ig.profile_picture_url || null, seguidores: num(ig.followers_count) } });
|
|
64
|
+
async function inspecionarToken(ctx, { appId, appSecret, token }) {
|
|
65
|
+
const j = await grafoOAuth(ctx, "debug_token", { input_token: token, access_token: `${appId}|${appSecret}` }), d = j.data || {};
|
|
66
|
+
const iso = (n) => n ? new Date(Number(n) * 1e3).toISOString() : null;
|
|
67
|
+
return {
|
|
68
|
+
tipo: d.type || null,
|
|
69
|
+
valido: d.is_valid ?? null,
|
|
70
|
+
expiraEm: d.expires_at ? iso(d.expires_at) : null,
|
|
71
|
+
dadosExpiramEm: d.data_access_expires_at ? iso(d.data_access_expires_at) : null,
|
|
72
|
+
escopos: (d.granular_scopes || []).map((g) => ({ escopo: String(g.scope), ids: (g.target_ids || []).map(String) })),
|
|
73
|
+
escoposGerais: (d.scopes || []).map(String)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
var CAMPOS_IG_BASE = "id,username,name,profile_picture_url";
|
|
77
|
+
var ARESTAS_NEGOCIO = ["instagram_accounts", "owned_instagram_accounts", "client_instagram_accounts"];
|
|
78
|
+
async function descobrirContas(ctx, token, { appId, appSecret, tudo = false, negocios = [] } = {}) {
|
|
79
|
+
const contas = [], vistos = /* @__PURE__ */ new Set(), passos = [];
|
|
80
|
+
const por = (c) => {
|
|
81
|
+
if (!vistos.has(c.instagram.id)) {
|
|
82
|
+
vistos.add(c.instagram.id);
|
|
83
|
+
contas.push(c);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const limpa = (x) => String(x ?? "").split(token).join("***");
|
|
87
|
+
const tentar = async (passo, fn, resumir = (v) => `${(v?.data || []).length} item(ns)`) => {
|
|
88
|
+
try {
|
|
89
|
+
const v = await fn();
|
|
90
|
+
passos.push({ passo, ok: true, resumo: resumir(v) });
|
|
91
|
+
return v;
|
|
92
|
+
} catch (e) {
|
|
93
|
+
passos.push({ passo, ok: false, http: e.status ?? null, codigo: e.codigo ?? null, subcodigo: e.subcodigo ?? null, mensagem: limpa(e.mensagemMeta || e.message).slice(0, 300) });
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const parar = () => !tudo && contas.length > 0;
|
|
98
|
+
const diag = { escopos: [], idsPorEscopo: {}, idsInstagram: 0, resolvidos: 0, debugToken: appId && appSecret ? "ok" : "sem_credencial", paginas: 0, portfolios: 0 };
|
|
99
|
+
const doIg = (ig, pagina = null) => ig?.id && por(contaIg(ig, pagina));
|
|
100
|
+
const arestas = async (negocio, lista = ARESTAS_NEGOCIO) => {
|
|
101
|
+
for (const a of lista) {
|
|
102
|
+
const j = await tentar(`/${negocio}/${a}`, () => grafoOAuth(ctx, `${negocio}/${a}`, { fields: CAMPOS_IG_BASE, limit: 100, access_token: token }));
|
|
103
|
+
for (const ig of j?.data || []) doIg(ig);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
let info = null, clienteNegocio = null;
|
|
107
|
+
if (appId && appSecret) {
|
|
108
|
+
info = await tentar("debug_token", () => inspecionarToken(ctx, { appId, appSecret, token }), (v) => `tipo ${v.tipo || "?"}, ${v.valido ? "v\xE1lido" : "inv\xE1lido"}, ${v.escopos.length} escopo(s)`);
|
|
109
|
+
if (!info) diag.debugToken = "falhou";
|
|
110
|
+
else {
|
|
111
|
+
diag.escopos = info.escopos.map((g) => g.escopo);
|
|
112
|
+
for (const g of info.escopos) diag.idsPorEscopo[g.escopo] = g.ids.length;
|
|
113
|
+
const dica = (e) => e.startsWith("instagram_") ? "ig" : e.startsWith("pages_") ? "pagina" : e.includes("business") ? "negocio" : null;
|
|
114
|
+
const alvos = /* @__PURE__ */ new Map();
|
|
115
|
+
for (const g of info.escopos) for (const id of g.ids.slice(0, 20)) {
|
|
116
|
+
const d = dica(g.escopo);
|
|
117
|
+
if (d && (!alvos.has(id) || d === "ig")) alvos.set(id, d);
|
|
118
|
+
}
|
|
119
|
+
diag.idsInstagram = [...new Set(info.escopos.filter((g) => g.escopo.startsWith("instagram_")).flatMap((g) => g.ids))].length;
|
|
120
|
+
for (const [id, d] of alvos) {
|
|
121
|
+
if (d !== "pagina") {
|
|
122
|
+
const ig = d === "ig" ? await tentar(`/${id} (como conta de Instagram)`, () => grafoOAuth(ctx, id, { fields: CAMPOS_IG, access_token: token }), (v) => `@${v.username || "?"}`) : null;
|
|
123
|
+
if (ig?.id && ig.username) {
|
|
124
|
+
doIg(ig);
|
|
125
|
+
diag.resolvidos++;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
await arestas(id);
|
|
129
|
+
} else {
|
|
130
|
+
const pg = await tentar(`/${id} (como P\xE1gina)`, () => grafoOAuth(ctx, id, { fields: `id,name,instagram_business_account{${CAMPOS_IG}}`, access_token: token }), (v) => v.instagram_business_account ? "com Instagram ligado" : "sem Instagram ligado");
|
|
131
|
+
if (pg?.instagram_business_account) doIg(pg.instagram_business_account, { id: String(pg.id), nome: pg.name || null });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (tudo) {
|
|
137
|
+
await tentar("/me?fields=id,name (quem fez o login)", () => grafoOAuth(ctx, "me", { fields: "id,name", access_token: token }), (v) => `id ${v.id ?? "(vazio)"}${v.name ? `, ${v.name}` : ""}`);
|
|
138
|
+
for (const n of negocios) await arestas(n, [...ARESTAS_NEGOCIO, "owned_pages", "client_pages"]);
|
|
139
|
+
}
|
|
140
|
+
if (!parar()) {
|
|
141
|
+
const eu = await tentar("/me?fields=id,name,client_business_id", () => grafoOAuth(ctx, "me", { fields: "id,name,client_business_id", access_token: token }), (v) => `id ${v.id ?? "(vazio)"}${v.name ? `, ${v.name}` : ""}, client_business_id ${v.client_business_id ?? "(vazio)"}`);
|
|
142
|
+
clienteNegocio = eu?.client_business_id ? String(eu.client_business_id) : null;
|
|
143
|
+
const perm = await tentar("/me/permissions", () => grafoOAuth(ctx, "me/permissions", { access_token: token }), (v) => `${(v?.data || []).filter((x) => x.status === "granted").length} concedida(s): ${(v?.data || []).filter((x) => x.status === "granted").map((x) => x.permission).join(", ") || "nenhuma"}`);
|
|
144
|
+
void perm;
|
|
145
|
+
if (clienteNegocio) {
|
|
146
|
+
await arestas(clienteNegocio, [...ARESTAS_NEGOCIO, "assigned_instagram_accounts"]);
|
|
147
|
+
const nb = await tentar(`/${clienteNegocio}?fields=instagram_accounts{id,username}`, () => grafoOAuth(ctx, clienteNegocio, { fields: "instagram_accounts{id,username}", access_token: token }), (v) => `${(v?.instagram_accounts?.data || []).length} item(ns)`);
|
|
148
|
+
for (const ig of nb?.instagram_accounts?.data || []) doIg(ig);
|
|
149
|
+
}
|
|
150
|
+
const aia = await tentar("/me/assigned_instagram_accounts", () => grafoOAuth(ctx, "me/assigned_instagram_accounts", { fields: CAMPOS_IG_BASE, limit: 100, access_token: token }));
|
|
151
|
+
for (const ig of aia?.data || []) doIg(ig);
|
|
152
|
+
const ia = await tentar("/me/instagram_accounts", () => grafoOAuth(ctx, "me/instagram_accounts", { fields: CAMPOS_IG_BASE, limit: 100, access_token: token }));
|
|
153
|
+
for (const ig of ia?.data || []) doIg(ig);
|
|
154
|
+
const ap = await tentar("/me/assigned_pages", () => grafoOAuth(ctx, "me/assigned_pages", { fields: `id,name,instagram_business_account{${CAMPOS_IG}}`, limit: 100, access_token: token }));
|
|
155
|
+
for (const p of ap?.data || []) if (p.instagram_business_account?.id) doIg(p.instagram_business_account, { id: String(p.id), nome: p.name || null });
|
|
156
|
+
}
|
|
157
|
+
if (!parar()) {
|
|
158
|
+
const b = await tentar("/me/businesses", () => grafoOAuth(ctx, "me/businesses", { fields: `id,name,owned_instagram_accounts{${CAMPOS_IG}},instagram_accounts{${CAMPOS_IG}}`, limit: 100, access_token: token }));
|
|
159
|
+
diag.portfolios = (b?.data || []).length;
|
|
160
|
+
for (const n of b?.data || []) {
|
|
161
|
+
for (const ig of [...n.owned_instagram_accounts?.data || [], ...n.instagram_accounts?.data || []]) doIg(ig);
|
|
162
|
+
if (!contas.length || tudo) await arestas(n.id);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (!parar()) {
|
|
166
|
+
const j = await tentar("/me/accounts", () => grafoOAuth(ctx, "me/accounts", { fields: `id,name,instagram_business_account{${CAMPOS_IG}}`, limit: 100, access_token: token }));
|
|
167
|
+
diag.paginas = (j?.data || []).length;
|
|
168
|
+
for (const p of j?.data || []) if (p.instagram_business_account?.id) doIg(p.instagram_business_account, { id: String(p.id), nome: p.name || null });
|
|
169
|
+
}
|
|
170
|
+
return { contas, diag, token: info, passos, clienteNegocio };
|
|
171
|
+
}
|
|
172
|
+
async function verificarAcesso(ctx, { token, igId, info }) {
|
|
173
|
+
let usuario = null;
|
|
174
|
+
try {
|
|
175
|
+
usuario = (await grafoOAuth(ctx, igId, { fields: "id,username", access_token: token })).username || null;
|
|
176
|
+
} catch (e) {
|
|
177
|
+
return { ok: false, motivo: "leitura", detalhe: e.mensagemMeta || e.message };
|
|
178
|
+
}
|
|
179
|
+
if (info) return info.escopos.some((g) => g.escopo === "instagram_content_publish") || info.escoposGerais.includes("instagram_content_publish") ? { ok: true, usuario } : { ok: false, motivo: "publicar", detalhe: "instagram_content_publish n\xE3o consta nos escopos concedidos" };
|
|
180
|
+
try {
|
|
181
|
+
await grafoOAuth(ctx, `${igId}/content_publishing_limit`, { fields: "quota_usage", access_token: token });
|
|
182
|
+
return { ok: true, usuario };
|
|
183
|
+
} catch (e) {
|
|
184
|
+
return { ok: false, motivo: "publicar", detalhe: e.mensagemMeta || e.message };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async function chamar(ctx, caminho, params = {}, { metodo = "GET", t } = {}) {
|
|
188
|
+
const tk = t || await lerToken(ctx);
|
|
189
|
+
const url = new URL(`${tk.host || HOST_PADRAO}/${VERSAO}/${caminho}`);
|
|
190
|
+
const init = { method: metodo };
|
|
191
|
+
if (metodo === "GET") {
|
|
192
|
+
for (const [k, v] of Object.entries(params)) if (v != null) url.searchParams.set(k, v);
|
|
193
|
+
url.searchParams.set("access_token", tk.token);
|
|
194
|
+
} else {
|
|
195
|
+
init.headers = { "content-type": "application/x-www-form-urlencoded" };
|
|
196
|
+
init.body = new URLSearchParams([...Object.entries(params).filter(([, v]) => v != null).map(([k, v]) => [k, String(v)]), ["access_token", tk.token]]).toString();
|
|
197
|
+
}
|
|
198
|
+
const r = await (ctx.fetchFn || fetch)(url.toString(), init);
|
|
199
|
+
const txt = await r.text();
|
|
200
|
+
let j;
|
|
201
|
+
try {
|
|
202
|
+
j = txt ? JSON.parse(txt) : {};
|
|
203
|
+
} catch {
|
|
204
|
+
throw falha(r.status, null, txt);
|
|
205
|
+
}
|
|
206
|
+
if (j.error || !r.ok) throw falha(r.status, j, txt);
|
|
207
|
+
return j;
|
|
208
|
+
}
|
|
209
|
+
var CAMPOS_MIDIA = "id,caption,media_type,media_product_type,media_url,thumbnail_url,permalink,timestamp,like_count,comments_count";
|
|
210
|
+
var tipoDe = (m) => m.media_product_type === "REELS" ? "reel" : m.media_type === "VIDEO" ? "video" : m.media_type === "CAROUSEL_ALBUM" ? "carrossel" : m.media_product_type === "STORY" ? "story" : "imagem";
|
|
211
|
+
var primeiraLinha = (t, max = 120) => {
|
|
212
|
+
const l = String(t || "").split("\n").map((x) => x.trim()).find(Boolean) || "";
|
|
213
|
+
return l.length > max ? `${l.slice(0, max - 1)}\u2026` : l;
|
|
214
|
+
};
|
|
215
|
+
var midiaDe = (m) => ({
|
|
216
|
+
id: m.id,
|
|
217
|
+
rede: "instagram",
|
|
218
|
+
titulo: primeiraLinha(m.caption),
|
|
219
|
+
legenda: m.caption || "",
|
|
220
|
+
tipo: tipoDe(m),
|
|
221
|
+
thumbnail: m.thumbnail_url || (m.media_type === "IMAGE" || m.media_type === "CAROUSEL_ALBUM" ? m.media_url : null) || null,
|
|
222
|
+
permalink: m.permalink || null,
|
|
223
|
+
publicadoEm: m.timestamp || null,
|
|
224
|
+
curtidas: num(m.like_count),
|
|
225
|
+
comentarios: num(m.comments_count)
|
|
226
|
+
});
|
|
227
|
+
async function perfil({ ctx, t }) {
|
|
228
|
+
const tk = t || await lerToken(ctx);
|
|
229
|
+
const j = await chamar(ctx, tk.userId, { fields: "username,name,profile_picture_url,followers_count,follows_count,media_count" }, { t: tk });
|
|
230
|
+
return { id: String(j.id || tk.userId), usuario: j.username, nome: j.name || null, avatar: j.profile_picture_url || null, seguidores: num(j.followers_count), seguindo: num(j.follows_count), midias: num(j.media_count) };
|
|
231
|
+
}
|
|
232
|
+
async function seguidores({ ctx }) {
|
|
233
|
+
return (await perfil({ ctx })).seguidores;
|
|
234
|
+
}
|
|
235
|
+
async function listarMidias({ ctx, max = 25, pagina }) {
|
|
236
|
+
const t = await lerToken(ctx);
|
|
237
|
+
const j = await chamar(ctx, `${t.userId}/media`, { fields: CAMPOS_MIDIA, limit: ate(max, 50), after: pagina }, { t });
|
|
238
|
+
return { midias: (j.data || []).map(midiaDe), proxima: j.paging?.cursors?.after && j.paging?.next ? j.paging.cursors.after : null };
|
|
239
|
+
}
|
|
240
|
+
async function lerMidia({ ctx, mediaId }) {
|
|
241
|
+
const j = await chamar(ctx, mediaId, { fields: CAMPOS_MIDIA });
|
|
242
|
+
return midiaDe(j);
|
|
243
|
+
}
|
|
244
|
+
var usuarioDe = (t) => String(t.username || "").toLowerCase();
|
|
245
|
+
var comentarioDe = (c, midiaId, dono) => ({
|
|
246
|
+
id: c.id,
|
|
247
|
+
rede: "instagram",
|
|
248
|
+
midiaId,
|
|
249
|
+
autor: c.username || c.from?.username || null,
|
|
250
|
+
texto: c.text ?? "",
|
|
251
|
+
publicadoEm: c.timestamp || null,
|
|
252
|
+
curtidas: num(c.like_count) ?? 0,
|
|
253
|
+
doDono: !!c.username && c.username.toLowerCase() === dono,
|
|
254
|
+
oculto: !!c.hidden
|
|
255
|
+
});
|
|
256
|
+
async function comentariosDaMidia({ ctx, mediaId, max = 50, t }) {
|
|
257
|
+
const tk = t || await lerToken(ctx), dono = usuarioDe(tk);
|
|
258
|
+
const j = await chamar(ctx, `${mediaId}/comments`, { fields: "id,username,text,timestamp,like_count,hidden,replies{id,username,text,timestamp,like_count,hidden}", limit: ate(max, 100) }, { t: tk });
|
|
259
|
+
return (j.data || []).map((c) => {
|
|
260
|
+
const topo = comentarioDe(c, mediaId, dono), respostas = (c.replies?.data || []).map((r) => comentarioDe(r, mediaId, dono));
|
|
261
|
+
return { ...topo, totalRespostas: respostas.length, respondido: !topo.doDono && respostas.some((r) => r.doDono), respostas };
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
async function listarComentarios({ ctx, mediaId, semResposta = false, max = 50, pagina, maxMidias = 25 }) {
|
|
265
|
+
const t = await lerToken(ctx);
|
|
266
|
+
let midias, proxima = null;
|
|
267
|
+
if (mediaId) midias = [await lerMidia({ ctx, mediaId })];
|
|
268
|
+
else {
|
|
269
|
+
const r = await listarMidias({ ctx, max: maxMidias, pagina });
|
|
270
|
+
midias = r.midias;
|
|
271
|
+
proxima = r.proxima;
|
|
272
|
+
}
|
|
273
|
+
const comentarios = [];
|
|
274
|
+
const comComentarios = midias.filter((m) => m.comentarios), LOTE = 13;
|
|
275
|
+
for (let i = 0; i < comComentarios.length; i += LOTE) {
|
|
276
|
+
const lote = await Promise.all(comComentarios.slice(i, i + LOTE).map(async (m) => [m, await comentariosDaMidia({ ctx, mediaId: m.id, max: 50, t })]));
|
|
277
|
+
for (const [m, lista] of lote) for (const c of lista) {
|
|
278
|
+
if (semResposta && (c.doDono || c.respondido)) continue;
|
|
279
|
+
comentarios.push({ ...c, midia: { id: m.id, titulo: m.titulo, thumbnail: m.thumbnail, tipo: m.tipo, permalink: m.permalink, publicadoEm: m.publicadoEm } });
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
comentarios.sort((a, b) => String(b.publicadoEm).localeCompare(String(a.publicadoEm)));
|
|
283
|
+
return { comentarios: comentarios.slice(0, max), midias, proxima };
|
|
284
|
+
}
|
|
285
|
+
async function responderComentario({ ctx, commentId, texto }) {
|
|
286
|
+
if (!commentId) throw new Error("falta o id do coment\xE1rio");
|
|
287
|
+
if (!texto || !texto.trim() || texto.length > 2200) throw new Error("texto inv\xE1lido: de 1 a 2200 caracteres");
|
|
288
|
+
const j = await chamar(ctx, `${commentId}/replies`, { message: texto }, { metodo: "POST" });
|
|
289
|
+
return { id: j.id, parentId: commentId, texto };
|
|
290
|
+
}
|
|
291
|
+
async function ocultarComentario({ ctx, commentId, ocultar = true }) {
|
|
292
|
+
if (!commentId) throw new Error("falta o id do coment\xE1rio");
|
|
293
|
+
const j = await chamar(ctx, commentId, { hide: ocultar ? "true" : "false" }, { metodo: "POST" });
|
|
294
|
+
return { id: commentId, oculto: !!ocultar, ok: j.success !== false };
|
|
295
|
+
}
|
|
296
|
+
async function listarMencoes({ ctx, max = 25 }) {
|
|
297
|
+
const t = await lerToken(ctx);
|
|
298
|
+
const j = await chamar(ctx, `${t.userId}/tags`, { fields: "id,caption,media_type,media_product_type,permalink,timestamp,username,thumbnail_url,media_url", limit: ate(max, 50) }, { t });
|
|
299
|
+
return (j.data || []).map((m) => ({ id: m.id, rede: "instagram", autor: m.username || null, texto: m.caption || "", url: m.permalink || null, publicadoEm: m.timestamp || null, tipo: tipoDe(m), thumbnail: m.thumbnail_url || m.media_url || null, respondido: false }));
|
|
300
|
+
}
|
|
301
|
+
var METRICAS = {
|
|
302
|
+
reelAmpliada: "reach,saved,shares,views,total_interactions,ig_reels_avg_watch_time,ig_reels_video_view_total_time,likes,comments,reels_skip_rate,reposts",
|
|
303
|
+
feedAmpliada: "reach,saved,shares,views,total_interactions,likes,comments,reposts,follows,profile_visits",
|
|
304
|
+
reel: "reach,saved,shares,views,total_interactions,ig_reels_avg_watch_time,ig_reels_video_view_total_time,likes,comments",
|
|
305
|
+
feed: "reach,saved,shares,views,total_interactions,likes,comments",
|
|
306
|
+
minimas: "reach,saved,shares,views"
|
|
307
|
+
};
|
|
308
|
+
async function metricasMidia({ ctx, mediaId, tipo = "reel" }) {
|
|
309
|
+
const listas = tipo === "reel" ? [METRICAS.reelAmpliada, METRICAS.reel, METRICAS.minimas] : [METRICAS.feedAmpliada, METRICAS.feed, METRICAS.minimas];
|
|
310
|
+
let j;
|
|
311
|
+
for (const [i, lista] of listas.entries()) {
|
|
312
|
+
try {
|
|
313
|
+
j = await chamar(ctx, `${mediaId}/insights`, { metric: lista });
|
|
314
|
+
break;
|
|
315
|
+
} catch (e) {
|
|
316
|
+
if (e.status === 401 || e.status === 403 || e.status === 409 || i === listas.length - 1) throw e;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const v = {};
|
|
320
|
+
for (const d of j.data || []) v[d.name] = num(d.values?.[0]?.value ?? d.total_value?.value);
|
|
321
|
+
return {
|
|
322
|
+
alcance: v.reach ?? null,
|
|
323
|
+
salvamentos: v.saved ?? null,
|
|
324
|
+
compartilhamentos: v.shares ?? null,
|
|
325
|
+
views: v.views ?? null,
|
|
326
|
+
interacoes: v.total_interactions ?? null,
|
|
327
|
+
tempoMedioMs: v.ig_reels_avg_watch_time ?? null,
|
|
328
|
+
tempoTotalMs: v.ig_reels_video_view_total_time ?? null,
|
|
329
|
+
curtidas: v.likes ?? null,
|
|
330
|
+
comentarios: v.comments ?? null,
|
|
331
|
+
taxaPulo: v.reels_skip_rate ?? null,
|
|
332
|
+
reposts: v.reposts ?? null,
|
|
333
|
+
novosSeguidores: v.follows ?? null,
|
|
334
|
+
visitasPerfil: v.profile_visits ?? null,
|
|
335
|
+
bruto: v
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
var transitorio = (e) => !e?.status || e.status >= 500 || e.status === 429 || [1, 2, 4, 17, 32, 341, 613].includes(Number(e.codigo));
|
|
339
|
+
async function criarContainerReel({ ctx, videoUrl, capaUrl = null, legenda, compartilharNoFeed = true, miniaturaMs = null }) {
|
|
340
|
+
if (!/^https:\/\//.test(String(videoUrl || ""))) throw new Error("video_url precisa ser uma URL p\xFAblica https");
|
|
341
|
+
if (!legenda || !String(legenda).trim()) throw new Error("legenda vazia");
|
|
342
|
+
const t = await lerToken(ctx);
|
|
343
|
+
const j = await chamar(ctx, `${t.userId}/media`, { media_type: "REELS", video_url: videoUrl, caption: legenda, share_to_feed: compartilharNoFeed ? "true" : "false", ...capaUrl ? { cover_url: capaUrl } : miniaturaMs != null ? { thumb_offset: miniaturaMs } : {} }, { metodo: "POST", t });
|
|
344
|
+
return { id: String(j.id) };
|
|
345
|
+
}
|
|
346
|
+
async function statusContainer({ ctx, containerId }) {
|
|
347
|
+
const j = await chamar(ctx, containerId, { fields: "status_code,status" });
|
|
348
|
+
return { codigo: String(j.status_code || "").toUpperCase(), detalhe: j.status || null };
|
|
349
|
+
}
|
|
350
|
+
async function publicarContainer({ ctx, containerId }) {
|
|
351
|
+
const t = await lerToken(ctx);
|
|
352
|
+
const j = await chamar(ctx, `${t.userId}/media_publish`, { creation_id: containerId }, { metodo: "POST", t });
|
|
353
|
+
return { id: String(j.id) };
|
|
354
|
+
}
|
|
355
|
+
async function limitePublicacao({ ctx }) {
|
|
356
|
+
const t = await lerToken(ctx);
|
|
357
|
+
const j = await chamar(ctx, `${t.userId}/content_publishing_limit`, { fields: "quota_usage,config" }, { t });
|
|
358
|
+
const d = j.data?.[0];
|
|
359
|
+
if (!d) return null;
|
|
360
|
+
const uso = num(d.quota_usage), total = num(d.config?.quota_total);
|
|
361
|
+
return uso == null || total == null ? null : { uso, total };
|
|
362
|
+
}
|
|
363
|
+
async function permalinkDe({ ctx, mediaId }) {
|
|
364
|
+
const j = await chamar(ctx, mediaId, { fields: "permalink" });
|
|
365
|
+
return j.permalink || null;
|
|
366
|
+
}
|
|
367
|
+
async function comentarMidia({ ctx, mediaId, texto }) {
|
|
368
|
+
if (!texto || !String(texto).trim() || String(texto).length > 2200) throw new Error("coment\xE1rio inv\xE1lido: de 1 a 2200 caracteres");
|
|
369
|
+
const j = await chamar(ctx, `${mediaId}/comments`, { message: texto }, { metodo: "POST" });
|
|
370
|
+
return { id: String(j.id) };
|
|
371
|
+
}
|
|
372
|
+
async function acharMidiaPublicada({ ctx, legenda, apos }) {
|
|
373
|
+
const { midias } = await listarMidias({ ctx, max: 5 });
|
|
374
|
+
const norm = (s) => String(s || "").replace(/\s+/g, " ").trim();
|
|
375
|
+
const m = midias.find((x) => norm(x.legenda) === norm(legenda) && (!apos || Date.parse(x.publicadoEm) >= apos - 6e4));
|
|
376
|
+
return m ? { id: m.id, url: m.permalink || null } : null;
|
|
377
|
+
}
|
|
378
|
+
export {
|
|
379
|
+
DIALOGO_OAUTH,
|
|
380
|
+
HOSTS_VALIDOS,
|
|
381
|
+
HOST_PADRAO,
|
|
382
|
+
LONGO_MIN_S,
|
|
383
|
+
REF_META,
|
|
384
|
+
VERSAO,
|
|
385
|
+
acharMidiaPublicada,
|
|
386
|
+
chamar,
|
|
387
|
+
comentarMidia,
|
|
388
|
+
comentariosDaMidia,
|
|
389
|
+
criarContainerReel,
|
|
390
|
+
descobrirContas,
|
|
391
|
+
estenderTokenOAuth,
|
|
392
|
+
inspecionarToken,
|
|
393
|
+
lerMidia,
|
|
394
|
+
lerToken,
|
|
395
|
+
limitePublicacao,
|
|
396
|
+
listarComentarios,
|
|
397
|
+
listarMencoes,
|
|
398
|
+
listarMidias,
|
|
399
|
+
metricasMidia,
|
|
400
|
+
midiaDe,
|
|
401
|
+
ocultarComentario,
|
|
402
|
+
perfil,
|
|
403
|
+
permalinkDe,
|
|
404
|
+
publicarContainer,
|
|
405
|
+
responderComentario,
|
|
406
|
+
seguidores,
|
|
407
|
+
statusContainer,
|
|
408
|
+
transitorio,
|
|
409
|
+
trocarCodigoOAuth,
|
|
410
|
+
urlAutorizacaoOAuth,
|
|
411
|
+
verificarAcesso
|
|
412
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
MODO_PADRAO,
|
|
4
|
+
criar,
|
|
5
|
+
estadoDe
|
|
6
|
+
} from "./chunk-TTGQU5CG.mjs";
|
|
7
|
+
import "./chunk-VJRMAQCR.mjs";
|
|
8
|
+
import "./chunk-Z325UM27.mjs";
|
|
9
|
+
import "./chunk-LCFK6X55.mjs";
|
|
10
|
+
export {
|
|
11
|
+
MODO_PADRAO,
|
|
12
|
+
criar,
|
|
13
|
+
estadoDe
|
|
14
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
NOMES,
|
|
4
|
+
carregar,
|
|
5
|
+
carregarTodos
|
|
6
|
+
} from "./chunk-VJRMAQCR.mjs";
|
|
7
|
+
import "./chunk-LCFK6X55.mjs";
|
|
8
|
+
export {
|
|
9
|
+
NOMES,
|
|
10
|
+
carregar,
|
|
11
|
+
carregarTodos
|
|
12
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
REF_CHAVE,
|
|
4
|
+
criar,
|
|
5
|
+
itemDoContrato
|
|
6
|
+
} from "./chunk-QDZLRBTP.mjs";
|
|
7
|
+
import "./chunk-Z325UM27.mjs";
|
|
8
|
+
import "./chunk-LCFK6X55.mjs";
|
|
9
|
+
export {
|
|
10
|
+
REF_CHAVE,
|
|
11
|
+
criar,
|
|
12
|
+
itemDoContrato
|
|
13
|
+
};
|