weibo/favorites
weiboRead-onlyGet current user's favorited (bookmarked) Weibo posts
weibo.com
Last 7 days
0
Last 30 days
0
All time
0
weibo/favorites.js
/* @meta
{
"name": "weibo/favorites",
"description": "Get current user's favorited (bookmarked) Weibo posts",
"domain": "weibo.com",
"args": {
"page": {"required": false, "description": "Page number (default: 1, use 'all' to fetch everything)"},
"format": {"required": false, "description": "Output format: json (default) or md (markdown)"}
},
"capabilities": ["network"],
"readOnly": true,
"example": "bb-browser site weibo/favorites all md"
}
*/
async function(args) {
const fetchAll = args.page === 'all';
const format = args.format || 'json';
const startPage = fetchAll ? 1 : (parseInt(args.page) || 1);
const strip = (html) => (html || '').replace(/<[^>]+>/g, '').replace(/ /g, ' ').replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&').trim();
const parsePost = (s) => {
const item = {
id: s.idstr || String(s.id),
mblogid: s.mblogid,
text: s.text_raw || strip(s.text || ''),
created_at: s.created_at,
source: strip(s.source || ''),
reposts_count: s.reposts_count || 0,
comments_count: s.comments_count || 0,
likes_count: s.attitudes_count || 0,
is_long_text: !!s.isLongText,
pic_count: s.pic_num || 0,
user: {
id: s.user?.id,
screen_name: s.user?.screen_name,
verified: s.user?.verified || false
},
url: 'https://weibo.com/' + (s.user?.id || '') + '/' + (s.mblogid || '')
};
if (s.retweeted_status) {
const rt = s.retweeted_status;
item.retweeted = {
id: rt.idstr || String(rt.id),
text: rt.text_raw || strip(rt.text || ''),
user: rt.user?.screen_name || '[deleted]',
likes_count: rt.attitudes_count || 0
};
}
return item;
};
// Fetch total count from tags API
let total = null;
if (fetchAll) {
const tagResp = await fetch('/ajax/favorites/tags?page=1&is_show_total=1', {credentials: 'include'});
if (tagResp.ok) {
const tagData = await tagResp.json();
total = tagData.fav_total_num ?? null;
}
}
const allPosts = [];
let page = startPage;
const PAGE_SIZE = 16;
const SAFETY_MAX = 1000;
let maxPages;
if (fetchAll) {
maxPages = (typeof total === 'number' && total > 0)
? Math.min(SAFETY_MAX, Math.ceil(total / PAGE_SIZE))
: SAFETY_MAX;
} else {
maxPages = 1;
}
for (let i = 0; i < maxPages; i++) {
const resp = await fetch('/ajax/favorites/all_fav?page=' + page, {credentials: 'include'});
if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Not logged in?'};
const data = await resp.json();
if (!data.ok) return {error: 'API error: ' + (data.msg || 'unknown'), hint: 'Not logged in?'};
const items = Array.isArray(data.data) ? data.data : [];
if (items.length === 0) break;
for (const item of items) {
allPosts.push(parsePost(item));
}
if (!fetchAll) break;
page++;
}
if (format === 'md') {
const lines = ['# Weibo Favorites', ''];
if (total !== null) lines.push('Total: ' + total + ' | Fetched: ' + allPosts.length, '');
else lines.push('Fetched: ' + allPosts.length, '');
for (let k = 0; k < allPosts.length; k++) {
const p = allPosts[k];
lines.push('## ' + (k + 1) + '. ' + (p.user.screen_name || 'unknown'));
lines.push('');
lines.push('> ' + p.text.replace(/\n/g, '\n> '));
lines.push('');
if (p.retweeted) {
lines.push('**Retweet @' + p.retweeted.user + ':** ' + p.retweeted.text.substring(0, 200));
lines.push('');
}
lines.push('- Date: ' + p.created_at);
lines.push('- Stats: ' + p.likes_count + ' likes, ' + p.comments_count + ' comments, ' + p.reposts_count + ' reposts');
if (p.pic_count > 0) lines.push('- Pictures: ' + p.pic_count);
lines.push('- Link: ' + p.url);
lines.push('');
lines.push('---');
lines.push('');
}
return lines.join('\n');
}
const result = {
page: fetchAll ? 'all' : startPage,
count: allPosts.length,
posts: allPosts
};
if (total !== null) result.total = total;
return result;
}
Updated Apr 14, 2026Created Apr 14, 2026SHA-256: fe4cfc9d1292…