init
This commit is contained in:
commit
343169a973
8 changed files with 1783 additions and 0 deletions
302
skill/client.js
Normal file
302
skill/client.js
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Telegram Reader Client — удобный JS интерфейс к tg-proxy.wzray.com
|
||||
*
|
||||
* Usage:
|
||||
* node tg-client.js get-folders
|
||||
* node tg-client.js get-chats [--limit 50] [--offset 0] [--archived true|false] [--chat-type direct|bot|group|channel] [--folder-id N]
|
||||
* node tg-client.js get-messages <chat_id> [--limit 50] [--offset 0]
|
||||
* node tg-client.js delta <chat_id> <since> [--limit 50] [--offset 0]
|
||||
*
|
||||
* Examples:
|
||||
* node tg-client.js get-folders
|
||||
* node tg-client.js get-chats --limit 100 --chat-type group --folder-id 2
|
||||
* node tg-client.js get-messages 5880803391 --limit 10
|
||||
* node tg-client.js delta 5880803391 "2026-02-16T09:00:00Z"
|
||||
*/
|
||||
|
||||
const https = require('https');
|
||||
|
||||
const BASE_URL = 'tg-proxy.wzray.com';
|
||||
|
||||
function request(path, query = {}) {
|
||||
const queryString = new URLSearchParams(query).toString();
|
||||
const url = `${path}${queryString ? '?' + queryString : ''}`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = https.request(
|
||||
{
|
||||
hostname: BASE_URL,
|
||||
path: url,
|
||||
method: 'GET',
|
||||
headers: { 'User-Agent': 'openclaw-telegram-reader/1.0' }
|
||||
},
|
||||
(res) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
resolve(JSON.parse(data));
|
||||
} catch (e) {
|
||||
reject(new Error(`JSON parse error: ${e.message}`));
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
req.on('error', reject);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
function getFlagValue(args, flag, fallback) {
|
||||
const idx = args.indexOf(flag);
|
||||
if (idx === -1 || idx + 1 >= args.length) return fallback;
|
||||
const parsed = parseInt(args[idx + 1], 10);
|
||||
return Number.isNaN(parsed) ? fallback : parsed;
|
||||
}
|
||||
|
||||
function getStringFlagValue(args, flag, fallback = undefined) {
|
||||
const idx = args.indexOf(flag);
|
||||
if (idx === -1 || idx + 1 >= args.length) return fallback;
|
||||
return args[idx + 1];
|
||||
}
|
||||
|
||||
function getBooleanFlagValue(args, flag, fallback = undefined) {
|
||||
const value = getStringFlagValue(args, flag, undefined);
|
||||
if (value === undefined) return fallback;
|
||||
if (value === 'true') return true;
|
||||
if (value === 'false') return false;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of chats
|
||||
*/
|
||||
async function getChats(limit = 50, offset = 0, options = {}) {
|
||||
const query = { limit, offset };
|
||||
if (options.archived !== undefined) query.archived = String(options.archived);
|
||||
if (options.chatType) query.chat_type = options.chatType;
|
||||
if (options.folderId !== undefined) query.folder_id = String(options.folderId);
|
||||
const result = await request('/chats', query);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of folders
|
||||
*/
|
||||
async function getFolders() {
|
||||
const result = await request('/folders');
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get messages in a specific chat
|
||||
*/
|
||||
async function getChatMessages(chatId, limit = 50, offset = 0) {
|
||||
const result = await request(`/chats/${chatId}/messages`, { limit, offset });
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get messages delta since timestamp for one chat
|
||||
*/
|
||||
async function getChatDelta(chatId, since, limit = 50, offset = 0) {
|
||||
const result = await request(`/chats/${chatId}/delta`, {
|
||||
since,
|
||||
limit,
|
||||
offset
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter chats by criteria
|
||||
*/
|
||||
function filterChats(chats, criteria) {
|
||||
const now = new Date();
|
||||
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
||||
|
||||
return chats.filter(chat => {
|
||||
// Not muted
|
||||
if (criteria.notMuted && chat.is_muted) return false;
|
||||
|
||||
// Pinned
|
||||
if (criteria.pinned && !chat.is_pinned) return false;
|
||||
|
||||
// Active last week
|
||||
if (criteria.activeLastWeek) {
|
||||
if (!chat.last_message_date) return false;
|
||||
const lastMsg = new Date(chat.last_message_date);
|
||||
if (lastMsg < weekAgo) return false;
|
||||
}
|
||||
|
||||
// Inactive (no messages in last week)
|
||||
if (criteria.inactiveLastWeek) {
|
||||
if (!chat.last_message_date) return true; // no messages = inactive
|
||||
const lastMsg = new Date(chat.last_message_date);
|
||||
if (lastMsg >= weekAgo) return false; // has recent messages, skip
|
||||
}
|
||||
|
||||
// Type filter
|
||||
if (criteria.type) {
|
||||
if (criteria.type === 'direct' && chat.type !== 'private') return false;
|
||||
else if (criteria.type === 'bot' && chat.type !== 'bot') return false;
|
||||
else if (criteria.type === 'group' && !['group', 'supergroup', 'forum'].includes(chat.type)) return false;
|
||||
else if (criteria.type === 'channel' && chat.type !== 'channel') return false;
|
||||
}
|
||||
|
||||
// Archived filter
|
||||
if (criteria.archived !== undefined && chat.archived !== criteria.archived) return false;
|
||||
|
||||
// Folder filter
|
||||
if (criteria.folderId !== undefined && !(chat.folder_ids || []).includes(criteria.folderId)) return false;
|
||||
|
||||
// Has unread
|
||||
if (criteria.hasUnread && chat.unread_count === 0) return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter messages by importance
|
||||
*/
|
||||
function filterImportantMessages(messages) {
|
||||
const keywords = ['важно', 'срочно', 'deadline', 'сегодня', '@wzray', 'визирей'];
|
||||
const lowerKeywords = keywords.map(k => k.toLowerCase());
|
||||
|
||||
return messages.filter(msg => {
|
||||
if (!msg.text) return false;
|
||||
|
||||
const text = msg.text.toLowerCase();
|
||||
|
||||
// Check for keywords
|
||||
if (lowerKeywords.some(kw => text.includes(kw))) return true;
|
||||
|
||||
// Mentions (basic check)
|
||||
if (text.includes('@')) return true;
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* CLI
|
||||
*/
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length === 0) {
|
||||
console.log('Usage:');
|
||||
console.log(' node tg-client.js get-folders');
|
||||
console.log(' node tg-client.js get-chats [--limit N] [--offset N] [--archived true|false] [--chat-type direct|bot|group|channel] [--folder-id N]');
|
||||
console.log(' node tg-client.js get-messages <chat_id> [--limit N] [--offset N]');
|
||||
console.log(' node tg-client.js delta <chat_id> <since> [--limit N] [--offset N]');
|
||||
console.log(' node tg-client.js filter-chats [--pinned] [--not-muted] [--active-last-week]');
|
||||
console.log('');
|
||||
console.log('Examples:');
|
||||
console.log(' node tg-client.js get-folders');
|
||||
console.log(' node tg-client.js get-chats --limit 100 --chat-type group --folder-id 2');
|
||||
console.log(' node tg-client.js get-messages 5880803391 --limit 10');
|
||||
console.log(' node tg-client.js delta 5880803391 "2026-02-16T09:00:00Z"');
|
||||
console.log(' node tg-client.js filter-chats --pinned --not-muted --type=direct');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
try {
|
||||
const [command] = args;
|
||||
|
||||
switch (command) {
|
||||
case 'get-folders': {
|
||||
const result = await getFolders();
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'get-chats': {
|
||||
const limit = getFlagValue(args, '--limit', 50);
|
||||
const offset = getFlagValue(args, '--offset', 0);
|
||||
const archived = getBooleanFlagValue(args, '--archived', undefined);
|
||||
const chatType = getStringFlagValue(args, '--chat-type', undefined);
|
||||
const folderId = getFlagValue(args, '--folder-id', undefined);
|
||||
const result = await getChats(limit, offset, { archived, chatType, folderId });
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'get-messages': {
|
||||
const chatId = args[1];
|
||||
if (!chatId) throw new Error('chat_id is required');
|
||||
const limit = getFlagValue(args, '--limit', 50);
|
||||
const offset = getFlagValue(args, '--offset', 0);
|
||||
const result = await getChatMessages(chatId, limit, offset);
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'delta': {
|
||||
const chatId = args[1];
|
||||
const since = args[2];
|
||||
if (!chatId || !since) throw new Error('chat_id and since are required');
|
||||
const limit = getFlagValue(args, '--limit', 50);
|
||||
const offset = getFlagValue(args, '--offset', 0);
|
||||
const result = await getChatDelta(chatId, since, limit, offset);
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'filter-chats': {
|
||||
const limit = getFlagValue(args, '--limit', 100);
|
||||
const result = await getChats(limit);
|
||||
|
||||
if (!result || !result.items) {
|
||||
throw new Error('Invalid response from API');
|
||||
}
|
||||
|
||||
const criteria = {
|
||||
pinned: args.includes('--pinned'),
|
||||
notMuted: args.includes('--not-muted'),
|
||||
activeLastWeek: args.includes('--active-last-week'),
|
||||
inactiveLastWeek: args.includes('--inactive-last-week'),
|
||||
hasUnread: args.includes('--has-unread'),
|
||||
type: args.find(a => a.startsWith('--type='))?.split('=')[1],
|
||||
folderId: args.find(a => a.startsWith('--folder-id='))
|
||||
? parseInt(args.find(a => a.startsWith('--folder-id='))?.split('=')[1], 10)
|
||||
: undefined,
|
||||
archived: args.find(a => a.startsWith('--archived='))?.split('=')[1] === 'true'
|
||||
? true
|
||||
: args.find(a => a.startsWith('--archived='))?.split('=')[1] === 'false'
|
||||
? false
|
||||
: undefined
|
||||
};
|
||||
|
||||
const filtered = filterChats(result.items, criteria);
|
||||
console.log(JSON.stringify(filtered, null, 2));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown command: ${command}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Run CLI if called directly
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
// Export for use as a module
|
||||
module.exports = {
|
||||
getFolders,
|
||||
getChats,
|
||||
getChatMessages,
|
||||
getChatDelta,
|
||||
filterChats,
|
||||
filterImportantMessages
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue