mirror of
https://github.com/JimLiu/baoyu-skills.git
synced 2026-07-11 13:42:06 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 089e0b5ba9 | |||
| 4bc0d05f66 | |||
| 90f3e2ff24 | |||
| 575ed29391 | |||
| 5d4ff2e9c2 |
@@ -6,7 +6,7 @@
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Skills shared by Baoyu for improving daily work efficiency",
|
||||
"version": "1.4.0"
|
||||
"version": "1.4.2"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
|
||||
English | [中文](./CHANGELOG.zh.md)
|
||||
|
||||
## 1.4.2 - 2026-01-19
|
||||
|
||||
### Documentation
|
||||
- `baoyu-danger-gemini-web`: adds supported browsers list (Chrome, Chromium, Edge) and proxy configuration guide.
|
||||
|
||||
## 1.4.1 - 2026-01-18
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-x`: supports multi-language UI selectors for X Articles (contributed by [@ianchenx](https://github.com/ianchenx)).
|
||||
|
||||
## 1.4.0 - 2026-01-18
|
||||
|
||||
### Features
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
|
||||
[English](./CHANGELOG.md) | 中文
|
||||
|
||||
## 1.4.2 - 2026-01-19
|
||||
|
||||
### 文档
|
||||
- `baoyu-danger-gemini-web`:添加支持的浏览器列表(Chrome、Chromium、Edge)和代理配置指南。
|
||||
|
||||
## 1.4.1 - 2026-01-18
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-x`:支持 X Articles 多语言 UI 选择器(感谢 [@ianchenx](https://github.com/ianchenx) 贡献)。
|
||||
|
||||
## 1.4.0 - 2026-01-18
|
||||
|
||||
### 新功能
|
||||
|
||||
@@ -421,10 +421,18 @@ This skill uses the Gemini Web API (reverse-engineered).
|
||||
|
||||
**Warning:** This project uses unofficial API access via browser cookies. Use at your own risk.
|
||||
|
||||
- First run opens Chrome to authenticate with Google
|
||||
- First run opens a browser to authenticate with Google
|
||||
- Cookies are cached for subsequent runs
|
||||
- No guarantees on API stability or availability
|
||||
|
||||
**Supported browsers** (auto-detected): Google Chrome, Chrome Canary/Beta, Chromium, Microsoft Edge
|
||||
|
||||
**Proxy configuration**: If you need a proxy to access Google services (e.g., in China), set environment variables inline:
|
||||
|
||||
```bash
|
||||
HTTP_PROXY=http://127.0.0.1:7890 HTTPS_PROXY=http://127.0.0.1:7890 /baoyu-danger-gemini-web "Hello"
|
||||
```
|
||||
|
||||
### baoyu-danger-x-to-markdown
|
||||
|
||||
This skill uses a reverse-engineered X (Twitter) API.
|
||||
|
||||
+9
-1
@@ -421,10 +421,18 @@ mkdir -p .baoyu-skills/baoyu-cover-image
|
||||
|
||||
**警告:** 本项目通过浏览器 cookies 使用非官方 API。使用风险自负。
|
||||
|
||||
- 首次运行会打开 Chrome 进行 Google 身份验证
|
||||
- 首次运行会打开浏览器进行 Google 身份验证
|
||||
- Cookies 会被缓存供后续使用
|
||||
- 不保证 API 的稳定性或可用性
|
||||
|
||||
**支持的浏览器**(自动检测):Google Chrome、Chrome Canary/Beta、Chromium、Microsoft Edge
|
||||
|
||||
**代理配置**:如果需要通过代理访问 Google 服务(如中国大陆用户),请在命令前设置环境变量:
|
||||
|
||||
```bash
|
||||
HTTP_PROXY=http://127.0.0.1:7890 HTTPS_PROXY=http://127.0.0.1:7890 /baoyu-danger-gemini-web "你好"
|
||||
```
|
||||
|
||||
### baoyu-danger-x-to-markdown
|
||||
|
||||
此技能使用逆向工程的 X (Twitter) API。
|
||||
|
||||
@@ -198,7 +198,15 @@ CLI note: `scripts/main.ts` supports text generation, image generation, referenc
|
||||
|
||||
## Authentication
|
||||
|
||||
First run opens Chrome to authenticate with Google. Cookies are cached for subsequent runs.
|
||||
First run opens a browser to authenticate with Google. Cookies are cached for subsequent runs.
|
||||
|
||||
**Supported browsers** (auto-detected in order):
|
||||
- Google Chrome
|
||||
- Google Chrome Canary / Beta
|
||||
- Chromium
|
||||
- Microsoft Edge
|
||||
|
||||
Override with `GEMINI_WEB_CHROME_PATH` environment variable if needed.
|
||||
|
||||
```bash
|
||||
# Force cookie refresh
|
||||
@@ -214,6 +222,23 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts --login
|
||||
| `GEMINI_WEB_CHROME_PROFILE_DIR` | Chrome profile directory |
|
||||
| `GEMINI_WEB_CHROME_PATH` | Chrome executable path |
|
||||
|
||||
## Proxy Configuration
|
||||
|
||||
If you need a proxy to access Google services (e.g., in China), set `HTTP_PROXY` and `HTTPS_PROXY` environment variables before running:
|
||||
|
||||
```bash
|
||||
# Example with local proxy
|
||||
HTTP_PROXY=http://127.0.0.1:7890 HTTPS_PROXY=http://127.0.0.1:7890 npx -y bun ${SKILL_DIR}/scripts/main.ts "Hello"
|
||||
|
||||
# Image generation with proxy
|
||||
HTTP_PROXY=http://127.0.0.1:7890 HTTPS_PROXY=http://127.0.0.1:7890 npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image cat.png
|
||||
|
||||
# Cookie refresh with proxy
|
||||
HTTP_PROXY=http://127.0.0.1:7890 HTTPS_PROXY=http://127.0.0.1:7890 npx -y bun ${SKILL_DIR}/scripts/main.ts --login
|
||||
```
|
||||
|
||||
**Note**: Environment variables must be set inline with the command. Shell profile settings (e.g., `.bashrc`) may not be inherited by subprocesses.
|
||||
|
||||
## Examples
|
||||
|
||||
### Generate text response
|
||||
|
||||
@@ -10,6 +10,37 @@ import { parseMarkdown } from './md-to-html.js';
|
||||
|
||||
const X_ARTICLES_URL = 'https://x.com/compose/articles';
|
||||
|
||||
const I18N_SELECTORS = {
|
||||
titleInput: [
|
||||
'textarea[placeholder="Add a title"]',
|
||||
'textarea[placeholder="添加标题"]',
|
||||
'textarea[placeholder="タイトルを追加"]',
|
||||
'textarea[placeholder="제목 추가"]',
|
||||
'textarea[name="Article Title"]',
|
||||
],
|
||||
addPhotosButton: [
|
||||
'[aria-label="Add photos or video"]',
|
||||
'[aria-label="添加照片或视频"]',
|
||||
'[aria-label="写真や動画を追加"]',
|
||||
'[aria-label="사진 또는 동영상 추가"]',
|
||||
],
|
||||
previewButton: [
|
||||
'a[href*="/preview"]',
|
||||
'[data-testid="previewButton"]',
|
||||
'button[aria-label*="preview" i]',
|
||||
'button[aria-label*="预览" i]',
|
||||
'button[aria-label*="プレビュー" i]',
|
||||
'button[aria-label*="미리보기" i]',
|
||||
],
|
||||
publishButton: [
|
||||
'[data-testid="publishButton"]',
|
||||
'button[aria-label*="publish" i]',
|
||||
'button[aria-label*="发布" i]',
|
||||
'button[aria-label*="公開" i]',
|
||||
'button[aria-label*="게시" i]',
|
||||
],
|
||||
};
|
||||
|
||||
function sleep(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
@@ -299,8 +330,9 @@ export async function publishArticle(options: ArticleOptions): Promise<void> {
|
||||
}
|
||||
|
||||
// Wait for editor (title textarea)
|
||||
const titleSelectors = I18N_SELECTORS.titleInput.join(', ');
|
||||
console.log('[x-article] Waiting for editor...');
|
||||
const editorFound = await waitForElement('textarea[placeholder="Add a title"], textarea[name="Article Title"]', 30_000);
|
||||
const editorFound = await waitForElement(titleSelectors, 30_000);
|
||||
if (!editorFound) {
|
||||
console.log('[x-article] Editor not found. Please ensure you have X Premium and are logged in.');
|
||||
await sleep(60_000);
|
||||
@@ -312,8 +344,16 @@ export async function publishArticle(options: ArticleOptions): Promise<void> {
|
||||
console.log('[x-article] Uploading cover image...');
|
||||
|
||||
// Click "Add photos or video" button
|
||||
const addPhotosSelectors = JSON.stringify(I18N_SELECTORS.addPhotosButton);
|
||||
await cdp.send('Runtime.evaluate', {
|
||||
expression: `document.querySelector('[aria-label="Add photos or video"]')?.click()`,
|
||||
expression: `(() => {
|
||||
const selectors = ${addPhotosSelectors};
|
||||
for (const sel of selectors) {
|
||||
const el = document.querySelector(sel);
|
||||
if (el) { el.click(); return true; }
|
||||
}
|
||||
return false;
|
||||
})()`,
|
||||
}, { sessionId });
|
||||
await sleep(500);
|
||||
|
||||
@@ -351,8 +391,16 @@ export async function publishArticle(options: ArticleOptions): Promise<void> {
|
||||
console.log('[x-article] Filling title...');
|
||||
|
||||
// Focus title input
|
||||
const titleInputSelectors = JSON.stringify(I18N_SELECTORS.titleInput);
|
||||
await cdp.send('Runtime.evaluate', {
|
||||
expression: `document.querySelector('textarea[placeholder="Add a title"], textarea[name="Article Title"]')?.focus()`,
|
||||
expression: `(() => {
|
||||
const selectors = ${titleInputSelectors};
|
||||
for (const sel of selectors) {
|
||||
const el = document.querySelector(sel);
|
||||
if (el) { el.focus(); return true; }
|
||||
}
|
||||
return false;
|
||||
})()`,
|
||||
}, { sessionId });
|
||||
await sleep(200);
|
||||
|
||||
@@ -633,15 +681,13 @@ export async function publishArticle(options: ArticleOptions): Promise<void> {
|
||||
|
||||
// Click Preview button
|
||||
console.log('[x-article] Opening preview...');
|
||||
const previewSelectors = JSON.stringify(I18N_SELECTORS.previewButton);
|
||||
const previewClicked = await cdp.send<{ result: { value: boolean } }>('Runtime.evaluate', {
|
||||
expression: `(() => {
|
||||
// Try multiple selectors for preview button
|
||||
const previewLink = document.querySelector('a[href*="/preview"]')
|
||||
|| document.querySelector('[data-testid="previewButton"]')
|
||||
|| document.querySelector('button[aria-label*="preview" i]');
|
||||
if (previewLink) {
|
||||
previewLink.click();
|
||||
return true;
|
||||
const selectors = ${previewSelectors};
|
||||
for (const sel of selectors) {
|
||||
const el = document.querySelector(sel);
|
||||
if (el) { el.click(); return true; }
|
||||
}
|
||||
return false;
|
||||
})()`,
|
||||
@@ -658,12 +704,13 @@ export async function publishArticle(options: ArticleOptions): Promise<void> {
|
||||
// Check for publish button
|
||||
if (submit) {
|
||||
console.log('[x-article] Publishing...');
|
||||
const publishSelectors = JSON.stringify(I18N_SELECTORS.publishButton);
|
||||
await cdp.send('Runtime.evaluate', {
|
||||
expression: `(() => {
|
||||
const publishBtn = document.querySelector('[data-testid="publishButton"], button[aria-label*="publish" i], button[aria-label*="发布" i]');
|
||||
if (publishBtn && !publishBtn.disabled) {
|
||||
publishBtn.click();
|
||||
return true;
|
||||
const selectors = ${publishSelectors};
|
||||
for (const sel of selectors) {
|
||||
const el = document.querySelector(sel);
|
||||
if (el && !el.disabled) { el.click(); return true; }
|
||||
}
|
||||
return false;
|
||||
})()`,
|
||||
|
||||
Reference in New Issue
Block a user