Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
672213f
feat(Create Posts): reimplement basic BotGuardSolver + create the `Cr…
Illusion137 Aug 15, 2026
5350333
refactor(Create Posts): since RunAttestationCommand is already listed…
Illusion137 Aug 15, 2026
1dc269f
fix(Create Posts): fix the action contents union type in `ChannelList…
Illusion137 Aug 15, 2026
1546fda
fix(Create Posts): put the new types in the types `index.ts`
Illusion137 Aug 15, 2026
d0bfda9
fix(Create Posts/initialData): pass in the proper baseURL and actuall…
Illusion137 Aug 15, 2026
d310994
feat(Create Posts/Proto): CreateBackstagePostParams proto and proto u…
Illusion137 Aug 16, 2026
cd0b6e1
fix(Create Posts/RunAttestationCommand): store the raw_ids as well si…
Illusion137 Aug 16, 2026
333235f
feat/refactor(Create Posts): move creating posts into `PostManager` a…
Illusion137 Aug 16, 2026
0b86ec5
fix(Create Posts): fix `parseLooseJSON` not properly parsing challeng…
Illusion137 Aug 16, 2026
c9b4f94
fix(Create Posts): fix everything related to `RunAttestationCommand.p…
Illusion137 Aug 16, 2026
5383c1c
feat/refactor(Create Posts): update the type names and add support fo…
Illusion137 Aug 16, 2026
bdcb802
refactor(Create Posts): change `image.reader` to `image.source`
Illusion137 Aug 16, 2026
4113fa0
fix(Create Posts): small bugs patched relating to not being able to u…
Illusion137 Aug 16, 2026
4449391
feat(Create Posts): input validation on `scheduled_publish_time_seconds`
Illusion137 Aug 16, 2026
0a95d7a
fix(Create Posts): fix the previous input validation
Illusion137 Aug 16, 2026
e66596a
feat(Create Posts): example for creating posts
Illusion137 Aug 16, 2026
29fa076
feat(Create Posts): implement rest of `Parser` classes + make `PostMa…
Illusion137 Aug 19, 2026
58ee0fb
chore(HTTPClient): change the text/html check to be for `Accept` and …
Illusion137 Aug 19, 2026
4743e4e
refactor(HTTPClient): change the text/html check at all to just inste…
Illusion137 Aug 19, 2026
9fcb547
feat(BotGuard): initial botguard solver interface
Illusion137 Aug 21, 2026
59f3624
feat(BotGuard): fetching initial page challenge (and ytcfg with it; p…
Illusion137 Aug 21, 2026
96afef5
fix(BotGuard): make challenge info interface all required data
Illusion137 Aug 21, 2026
cd83c5f
feat(RunAttestationCommand): update types and also store raw ids for …
Illusion137 Aug 21, 2026
7055d9b
feat(Parser): parse `botguardData` and `eats` for /att/get
Illusion137 Aug 21, 2026
2ac3a89
feat(Innertube): `initialData` also return `eacr_token` from `yt_atn`
Illusion137 Aug 21, 2026
49f9007
feat(Innertube): add support for `eacr_token` in `getAttestationChall…
Illusion137 Aug 21, 2026
ad7368d
refactor(BotGuard): pick only whats actually required from `BotGuardC…
Illusion137 Aug 22, 2026
f2b76a6
feat(BotGuard): add `BotGuardManager`
Illusion137 Aug 22, 2026
0833b16
feat(Innertube): expose `BotGuardManager` within `Innertube`
Illusion137 Aug 22, 2026
df6d437
feat(BotGuard): add support for running `RunAttestationCommand` in `B…
Illusion137 Aug 22, 2026
20a319b
refactor(BotGuard): merge both run functions into one and compact the…
Illusion137 Aug 22, 2026
2421856
feat(BotGuard): add log support
Illusion137 Aug 22, 2026
26fc5ec
feat(BotGuard): add commented out tests
Illusion137 Aug 22, 2026
1c9b4a9
refactor(BotGuard): clean up lines a bit
Illusion137 Aug 22, 2026
31b5827
refactor(BotGuard): change `opts` to `args` to match style of project…
Illusion137 Aug 22, 2026
f72d072
feat(Innertube): let `getAttestationChallenge` have more arguments li…
Illusion137 Aug 22, 2026
a9d59ac
feat(better-context): add one-time-context support to `HTTPClient` an…
Illusion137 Aug 22, 2026
4c64f98
feat(Context): add support for YouTube Studio Web context
Illusion137 Aug 22, 2026
4176262
feat/fix(BotGuard): use `one_time_context` system for `eats`
Illusion137 Aug 22, 2026
fda525c
feat(better-context): use a better extra context system
Illusion137 Aug 22, 2026
f6be900
refactor(parseLooseJSON): make the function closer to the BGUtils ver…
Illusion137 Aug 22, 2026
b9f48d9
refactor(parseLooseJSON): remove eval comment since, some runtimes do…
Illusion137 Aug 22, 2026
d6169f7
feat: Session based eats
Illusion137 Aug 26, 2026
a691cbe
feat(Parser): parse `challenge_prompt` from `response_context`
Illusion137 Aug 30, 2026
799ad48
feat(BotGuard): add a BotGuardLogBinding type and let the BotGuardMan…
Illusion137 Aug 30, 2026
0adf121
refactor(Innertube): just let the session and `execute` handle the `e…
Illusion137 Aug 30, 2026
bc38ac9
refactor(BotGuard): add comments to exposed api +
Illusion137 Aug 31, 2026
285c887
refactor(BotGuard): update `log` comment
Illusion137 Aug 31, 2026
5438510
Merge https://github.com/Illusion137/YouTube.js into feat(BotGuard)
Illusion137 Aug 31, 2026
4e9f9c0
feat(HTTPClient): auto adjust `WEB_CREATOR` context with `request.ret…
Illusion137 Aug 31, 2026
e00e472
feat(Innertube): let `initialData` auto update session `eats`
Illusion137 Aug 31, 2026
50ef674
feat(Context): implement UserInfo_DelegationContext + create a util t…
Illusion137 Aug 31, 2026
9f91724
feat(Innertube): allow one_time_context modifications in `getAttestat…
Illusion137 Aug 31, 2026
b9dcb72
feat(BotGuard): add `one_time_context` to `getChallenge`
Illusion137 Aug 31, 2026
d695094
refactor(Parser): no need to parse eats since its automatically handl…
Illusion137 Aug 31, 2026
f68fa1f
feat(Parser): parse other attestation related data found in YouTube S…
Illusion137 Aug 31, 2026
481091d
feat(Misc): add support for `EngagementType` of `ENGAGEMENT_TYPE_CREA…
Illusion137 Aug 31, 2026
82bc51a
fix/refactor(Innertube): forgot to remove `eats` from `getAttestation…
Illusion137 Aug 31, 2026
f8f3aa2
feat/refactor(BotGuard): change `ClientType` to `InnerTubeClient` and…
Illusion137 Aug 31, 2026
105c532
feat(Misc): add more EngagementTypes
Illusion137 Sep 1, 2026
6c93da4
fix(Innertube/HTTPClient): initialData not properly using cookies
Illusion137 Sep 1, 2026
f016708
feat(BotGuard): add `studioAttestationChallenge` + `studioAttestation…
Illusion137 Sep 1, 2026
6042461
fix(BotGuard): properly spread out the ids in `Object.assign()`
Illusion137 Sep 1, 2026
61e840d
Merge branch 'feat(BotGuard)' of https://github.com/Illusion137/YouTu…
Illusion137 Sep 1, 2026
c76376e
fix(BotGuard): allow `log` to use attestation commands
Illusion137 Sep 2, 2026
5dc6243
Merge branch 'feat(BotGuard)' of https://github.com/Illusion137/YouTu…
Illusion137 Sep 2, 2026
9e78b9e
fix(Innertube): merge with BotGuard
Illusion137 Sep 2, 2026
bed25bb
refactor(RunAttestationCommand): no longer any need for challenge sol…
Illusion137 Sep 2, 2026
fb01676
refactor(PostManager): use BotGuardManager
Illusion137 Sep 2, 2026
04770b2
fix(BotGuard): actually fix `log()` `args` type (`Omit` doesn't go ov…
Illusion137 Sep 2, 2026
ac6f512
Merge branch 'feat(BotGuard)' of https://github.com/Illusion137/YouTu…
Illusion137 Sep 2, 2026
049b374
chore(PostMangager): linting
Illusion137 Sep 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/posts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock.json
18 changes: 18 additions & 0 deletions examples/posts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "posts",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {},
"devDependencies": {
"@types/jsdom": "^27.0.0",
"typescript": "^5.0.4"
},
"dependencies": {
"bgutils-js": "^4.0.3",
"cookie-eater": "^1.0.0",
"image-size": "^2.0.2",
"jsdom": "^28.1.0",
"youtubei.js": "file:../../"
}
}
30 changes: 30 additions & 0 deletions examples/posts/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Innertube, UniversalCache } from 'youtubei.js';
import { botguard_solver, get_channel_id, image_path_to_post_image, youtube_cookies } from './utils.ts';

(async () => {
const COOKIES = await youtube_cookies();

const yt = await Innertube.create({ cache: new UniversalCache(false), cookie: COOKIES });
const CHANNEL_ID = await get_channel_id(yt);

const posts_manager = yt.posts(botguard_solver);
const create_post_result = await posts_manager.create({
comment_text: 'Test post 001',
// scheduled_publish_time_seconds: String(Math.floor(Date.now() / 1000) + (60 * 60)) // upload in a hour
}, CHANNEL_ID,
{
type: "IMAGE_POLL",
options: [
{text: "Yes", image: {
source: await image_path_to_post_image(""),
preview_coordinates: {left: 0, top: 0, right: 1, bottom: 1}
}},
{text: "No", image: {
source: await image_path_to_post_image(""),
preview_coordinates: {left: 0, top: 0, right: 1, bottom: 1}
}}
]
});

console.log(create_post_result);
})();
67 changes: 67 additions & 0 deletions examples/posts/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { readFile } from 'node:fs/promises';
import { BotGuardClient } from "bgutils-js/botguard";
import { USER_AGENT } from "bgutils-js/utils";
import { imageSize } from 'image-size';
import { JSDOM, VirtualConsole } from "jsdom";
import Innertube, { type Types } from 'youtubei.js';
import { get_cookies } from 'cookie-eater';

export const botguard_solver: Types.BotGuardSolver<string> = {
solve: async(botguard_challenge, binding) => {
const virtual_console = new VirtualConsole();
const dom = new JSDOM('<!DOCTYPE html><html lang="en"><head><title></title></head><body></body></html>', { url: "https://www.youtube.com", referrer: "https://www.youtube.com/", userAgent: USER_AGENT, resources: "usable", runScripts: "dangerously", virtualConsole: virtual_console });

Object.assign(globalThis, { window: dom.window, document: dom.window.document, location: dom.window.location, origin: dom.window.origin });

if (!("navigator" in globalThis)) {
Object.defineProperty(globalThis, "navigator", { value: dom.window.navigator });
}

Object.defineProperty(dom.window.HTMLCanvasElement.prototype, "getContext", { value: () => null, writable: true });

let interpreter_url = botguard_challenge.interpreter_url.private_do_not_access_or_else_safe_script_wrapped_value ?? botguard_challenge.interpreter_url.private_do_not_access_or_else_trusted_resource_url_wrapped_value ?? "";

if (interpreter_url.startsWith("//")) interpreter_url = `https:${interpreter_url}`;

const bg_script_response = await fetch(interpreter_url);
const interpreter_javascript = await bg_script_response.text();

new Function(interpreter_javascript)();

const botguard = await BotGuardClient.create({ program: botguard_challenge.program, globalName: botguard_challenge.global_name, globalObject: globalThis });

const botguard_response = await botguard.snapshot({ contentBinding: { atr_challenge: binding } });
return botguard_response;
}
};

export async function get_channel_id(yt: Innertube, index?: number): Promise<string> {
const account_info = await yt.account.getInfo(true);
const account = account_info[index ?? 0];
if (account === undefined) throw new Error("No accounts found");
const resolve_url = `https://www.youtube.com/${account.channel_handle.text}`;
const resolved = await yt.resolveURL(resolve_url);
const channel_id = resolved.payload.browseId as string;
return channel_id;
}

export async function image_path_to_post_image(image_path: string): Promise<Types.PostImage> {
const body = await readFile(image_path);
const { width, height, type } = imageSize(body);
return {
body,
size: body.length,
width,
height,
base64: `data:image/${type ?? 'png'};base64,${body.toString('base64')}`
};
}

export async function youtube_cookies(): Promise<string|undefined> {
const HOST_NAME = '.youtube.com';
const cookies_map = await get_cookies([HOST_NAME]);
const cookies = cookies_map?.[HOST_NAME].toString() ?? undefined;

if(cookies) console.log(`Successfully found ${cookies_map?.[HOST_NAME].getCookies().length} cookies`);
return cookies;
}
20 changes: 20 additions & 0 deletions examples/posts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"strict": true,
"moduleResolution": "bundler",
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"noEmit": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"skipLibCheck": true,
"allowImportingTsExtensions": true
}
}
159 changes: 159 additions & 0 deletions protos/generated/misc/params.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading