@will-17173/telegram-cli 0.4.0 → 0.5.0

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.
Files changed (45) hide show
  1. package/README.md +85 -449
  2. package/README.zh-CN.md +84 -445
  3. package/dist/cli/app.js +3 -1
  4. package/dist/cli/app.js.map +1 -1
  5. package/dist/commands/telegram.js +16 -1
  6. package/dist/commands/telegram.js.map +1 -1
  7. package/dist/commands/web.js +52 -0
  8. package/dist/commands/web.js.map +1 -0
  9. package/dist/listen-commands/catalog.js +10 -0
  10. package/dist/listen-commands/catalog.js.map +1 -1
  11. package/dist/listen-commands/dispatch.js +5 -0
  12. package/dist/listen-commands/dispatch.js.map +1 -1
  13. package/dist/presenters/ink/listen-command-menu.js +10 -2
  14. package/dist/presenters/ink/listen-command-menu.js.map +1 -1
  15. package/dist/presenters/ink/listen-scroll.js +1 -1
  16. package/dist/presenters/ink/listen-scroll.js.map +1 -1
  17. package/dist/presenters/ink/listen.js +154 -25
  18. package/dist/presenters/ink/listen.js.map +1 -1
  19. package/dist/presenters/listen-message.js +1 -1
  20. package/dist/presenters/listen-message.js.map +1 -1
  21. package/dist/services/sync-service.js +33 -2
  22. package/dist/services/sync-service.js.map +1 -1
  23. package/dist/storage/message-db.js +117 -0
  24. package/dist/storage/message-db.js.map +1 -1
  25. package/dist/telegram/mtcute-client.js +30 -9
  26. package/dist/telegram/mtcute-client.js.map +1 -1
  27. package/dist/telegram/types.js.map +1 -1
  28. package/dist/web/api.js +196 -0
  29. package/dist/web/api.js.map +1 -0
  30. package/dist/web/assets/index-B5v_8ify.css +1 -0
  31. package/dist/web/assets/index-DC74wrJK.js +9 -0
  32. package/dist/web/index.html +13 -0
  33. package/dist/web/query.js +74 -0
  34. package/dist/web/query.js.map +1 -0
  35. package/dist/web/security.js +23 -0
  36. package/dist/web/security.js.map +1 -0
  37. package/dist/web/server.js +133 -0
  38. package/dist/web/server.js.map +1 -0
  39. package/dist/web/static.js +40 -0
  40. package/dist/web/static.js.map +1 -0
  41. package/dist/web/sync-task.js +161 -0
  42. package/dist/web/sync-task.js.map +1 -0
  43. package/dist/web/types.js +2 -0
  44. package/dist/web/types.js.map +1 -0
  45. package/package.json +10 -5
@@ -0,0 +1,196 @@
1
+ import { getDataDir } from '../config/env.js';
2
+ import { validateLocalRequest } from './security.js';
3
+ import { WebQueryService } from './query.js';
4
+ export async function handleApiRequest(request, context) {
5
+ const security = validateLocalRequest(request, context.port);
6
+ if (!security.ok)
7
+ return failure(security.status, security.code, security.message);
8
+ const url = new URL(request.url);
9
+ try {
10
+ const dataDir = context.dataDir ?? getDataDir();
11
+ const query = new WebQueryService({ dataDir });
12
+ switch (url.pathname) {
13
+ case '/api/health':
14
+ if (request.method !== 'GET')
15
+ return notFound();
16
+ return success({ status: 'ok' });
17
+ case '/api/accounts':
18
+ if (request.method !== 'GET')
19
+ return notFound();
20
+ return success(query.accounts());
21
+ case '/api/chats':
22
+ if (request.method !== 'GET')
23
+ return notFound();
24
+ return success(query.chats({
25
+ account: stringParam(url, 'account'),
26
+ q: stringParam(url, 'q'),
27
+ limit: optionalPositiveIntParam(url, 'limit') ?? 100,
28
+ offset: optionalNonNegativeIntParam(url, 'offset') ?? 0,
29
+ }));
30
+ case '/api/messages':
31
+ if (request.method !== 'GET')
32
+ return notFound();
33
+ return success(query.messages({
34
+ account: stringParam(url, 'account'),
35
+ chatId: requiredNonZeroIntParam(url, 'chatId'),
36
+ q: stringParam(url, 'q'),
37
+ since: stringParam(url, 'since'),
38
+ until: stringParam(url, 'until'),
39
+ limit: optionalPositiveIntParam(url, 'limit') ?? 50,
40
+ cursor: stringParam(url, 'cursor'),
41
+ }));
42
+ case '/api/sync-task':
43
+ if (request.method === 'POST')
44
+ return syncTaskPost(request, context);
45
+ if (request.method !== 'GET')
46
+ return notFound();
47
+ return success(context.syncTask.getState());
48
+ default:
49
+ return notFound();
50
+ }
51
+ }
52
+ catch (error) {
53
+ return errorResponse(error);
54
+ }
55
+ }
56
+ function success(data) {
57
+ return jsonResponse(200, { ok: true, data });
58
+ }
59
+ function failure(status, code, message, details) {
60
+ return jsonResponse(status, { ok: false, error: { code, message, details } });
61
+ }
62
+ function notFound() {
63
+ return failure(404, 'not_found', 'API route not found.');
64
+ }
65
+ function jsonResponse(status, body) {
66
+ return new Response(JSON.stringify(body), {
67
+ status,
68
+ headers: { 'content-type': 'application/json; charset=utf-8' },
69
+ });
70
+ }
71
+ async function syncTaskPost(request, context) {
72
+ if (!isJsonRequest(request)) {
73
+ return failure(400, 'invalid_request', 'Content-Type must include application/json.');
74
+ }
75
+ const body = await parseJsonBody(request);
76
+ if (!isRecord(body)) {
77
+ return failure(400, 'invalid_request', 'Request body must be a JSON object.');
78
+ }
79
+ const account = typeof body.account === 'string' ? body.account.trim() : '';
80
+ if (account === '') {
81
+ return failure(400, 'invalid_request', 'account must be a non-empty string.');
82
+ }
83
+ if (!isSafeNonZeroInteger(body.chatId)) {
84
+ return failure(400, 'invalid_request', 'chatId must be a non-zero integer.');
85
+ }
86
+ let limit = 500;
87
+ if (hasOwn(body, 'limit')) {
88
+ if (!isSafePositiveInteger(body.limit)) {
89
+ return failure(400, 'invalid_request', 'limit must be a positive integer.');
90
+ }
91
+ limit = body.limit;
92
+ }
93
+ const result = await context.syncTask.start({ account, chatId: body.chatId, limit });
94
+ if (result.ok)
95
+ return jsonResponse(200, result);
96
+ if (result.error.code === 'sync_task_running')
97
+ return jsonResponse(409, result);
98
+ if (result.error.code === 'invalid_request' || isAccountErrorCode(result.error.code))
99
+ return jsonResponse(400, result);
100
+ return jsonResponse(500, result);
101
+ }
102
+ function isJsonRequest(request) {
103
+ return request.headers.get('content-type')?.toLowerCase().includes('application/json') ?? false;
104
+ }
105
+ async function parseJsonBody(request) {
106
+ try {
107
+ return await request.json();
108
+ }
109
+ catch {
110
+ return undefined;
111
+ }
112
+ }
113
+ function isRecord(value) {
114
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
115
+ }
116
+ function hasOwn(value, key) {
117
+ return Object.prototype.hasOwnProperty.call(value, key);
118
+ }
119
+ function isSafePositiveInteger(value) {
120
+ return typeof value === 'number' && Number.isSafeInteger(value) && value > 0;
121
+ }
122
+ function isSafeNonZeroInteger(value) {
123
+ return typeof value === 'number' && Number.isSafeInteger(value) && value !== 0;
124
+ }
125
+ function isAccountErrorCode(code) {
126
+ return code === 'account_required'
127
+ || code === 'account_not_found'
128
+ || code === 'account_logged_out'
129
+ || code === 'account_session_missing';
130
+ }
131
+ function stringParam(url, name) {
132
+ const value = url.searchParams.get(name);
133
+ return value == null || value.trim() === '' ? undefined : value;
134
+ }
135
+ function requiredNonZeroIntParam(url, name) {
136
+ const value = parseInteger(url.searchParams.get(name));
137
+ if (value == null || value === 0)
138
+ throw invalidRequest(`${name} must be a non-zero integer.`);
139
+ return value;
140
+ }
141
+ function optionalPositiveIntParam(url, name) {
142
+ const raw = url.searchParams.get(name);
143
+ if (raw == null || raw.trim() === '')
144
+ return undefined;
145
+ const value = parseInteger(raw);
146
+ if (value == null || value <= 0)
147
+ throw invalidRequest(`${name} must be a positive integer.`);
148
+ return value;
149
+ }
150
+ function optionalNonNegativeIntParam(url, name) {
151
+ const raw = url.searchParams.get(name);
152
+ if (raw == null || raw.trim() === '')
153
+ return undefined;
154
+ const value = parseInteger(raw);
155
+ if (value == null || value < 0)
156
+ throw invalidRequest(`${name} must be a non-negative integer.`);
157
+ return value;
158
+ }
159
+ function parseInteger(raw) {
160
+ if (raw == null)
161
+ return undefined;
162
+ const value = raw.trim();
163
+ if (!/^-?\d+$/.test(value))
164
+ return undefined;
165
+ const parsed = Number(value);
166
+ return Number.isSafeInteger(parsed) ? parsed : undefined;
167
+ }
168
+ function invalidRequest(message) {
169
+ return { status: 400, code: 'invalid_request', message };
170
+ }
171
+ function errorResponse(error) {
172
+ if (isApiError(error))
173
+ return failure(error.status, error.code, error.message);
174
+ const message = errorMessage(error);
175
+ if (isKnownValidationMessage(message))
176
+ return failure(400, 'invalid_request', message);
177
+ return failure(500, 'internal_error', 'Internal web API error.');
178
+ }
179
+ function isApiError(error) {
180
+ return typeof error === 'object'
181
+ && error !== null
182
+ && error.status === 400
183
+ && error.code === 'invalid_request'
184
+ && typeof error.message === 'string';
185
+ }
186
+ function isKnownValidationMessage(message) {
187
+ return message === 'invalid_cursor'
188
+ || message.includes('must be a positive integer')
189
+ || message.startsWith('account_required:')
190
+ || message.startsWith('account_not_found:')
191
+ || message.startsWith('account_logged_out:');
192
+ }
193
+ function errorMessage(error) {
194
+ return error instanceof Error ? error.message : String(error);
195
+ }
196
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/web/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ :root{color:#1b2533;font-synthesis:none;text-rendering:optimizelegibility;background:#f4f7f7;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}*{box-sizing:border-box}body{margin:0}button,input,select{font:inherit}button{cursor:pointer}button:disabled{cursor:not-allowed;opacity:.58}button:focus-visible,input:focus-visible,select:focus-visible{outline-offset:2px;outline:3px solid #75d3c7}.app-shell{min-height:100vh}.topbar{background:#fbfcfc;border-bottom:1px solid #cfdbdc;align-items:center;gap:18px;min-height:60px;padding:10px 20px;display:flex}.brand-block{align-items:center;gap:10px;min-width:170px;display:flex}.brand-mark{color:#75d3c7;letter-spacing:0;background:#1b2533;border-radius:6px;justify-content:center;align-items:center;width:34px;height:30px;font-family:SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:13px;font-weight:700;display:inline-flex}.account-picker{align-items:center;gap:8px;display:flex}.account-picker span,.sidebar-tools span,.filters span{color:#5e6b78;text-transform:uppercase;font-size:12px;font-weight:700}select,input{color:#1b2533;background:#fff;border:1px solid #becbd0;border-radius:6px;min-height:38px;padding:8px 10px}.sync-pill{color:#5e6b78;border:1px solid #becbd0;border-radius:999px;margin-left:auto;padding:6px 10px;font-family:SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:12px}.sync-pill-running{color:#77530a;border-color:#d7a631}.sync-pill-done{color:#12685d;border-color:#42a99a}.sync-pill-error{color:#9a2727;border-color:#df7979}.error-strip{color:#9a2727;background:#fff2ef;border-bottom:1px solid #efb4a9;padding:10px 20px}.workspace{grid-template-columns:minmax(260px,340px) minmax(0,1fr);min-height:calc(100vh - 60px);display:grid}.sidebar{background:#fbfcfc;border-right:1px solid #cfdbdc;flex-direction:column;min-width:0;display:flex}.sidebar-tools{border-bottom:1px solid #dce5e6;padding:14px}.sidebar-tools label,.filters label{gap:6px;display:grid}.chat-list{flex-direction:column;gap:8px;padding:10px;display:flex;overflow:auto}.chat-row{color:inherit;text-align:left;background:0 0;border:1px solid #0000;border-radius:6px;gap:5px;min-height:62px;padding:10px;display:grid}.chat-row:hover,.chat-row-active{background:#eef6f5;border-color:#a8d9d1}.chat-name{overflow-wrap:anywhere;font-weight:700}.chat-meta,.message-row time,.message-row small,.chat-header p,.empty-note{color:#66737f}.messages-pane{min-width:0;padding:22px}.chat-header{border-bottom:1px solid #cfdbdc;justify-content:space-between;align-items:flex-start;gap:16px;padding-bottom:18px;display:flex}.chat-header h1{overflow-wrap:anywhere;margin:0;font-size:24px;line-height:1.2}.chat-header p{margin:6px 0 0}.primary-action,.filters button,.load-more{color:#fff;background:#1b2533;border:1px solid #1b2533;border-radius:6px;min-height:38px;padding:8px 12px}.filters{grid-template-columns:minmax(180px,1fr) repeat(2,minmax(180px,220px)) auto;align-items:end;gap:10px;margin:18px 0;display:grid}.message-list{gap:0;margin:0;padding:0;list-style:none;display:grid}.message-row{background:linear-gradient(90deg,#75d3c72e,#0000 18px),#fff;border:1px solid #dce5e6;grid-template-columns:116px minmax(0,1fr);gap:14px;min-height:88px;margin-top:-1px;padding:12px 14px;display:grid}.message-row:first-child{border-radius:6px 6px 0 0;margin-top:0}.message-row:last-child{border-radius:0 0 6px 6px}.message-row time,.message-row small{font-family:SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:12px}.message-row strong{overflow-wrap:anywhere;margin-bottom:4px;display:block}.message-row p{white-space:pre-wrap;overflow-wrap:anywhere;margin:0 0 7px;line-height:1.55}.load-more{margin-top:16px}.empty-note{margin:18px 0}@media (width<=860px){.topbar{flex-direction:column;align-items:stretch}.brand-block,.account-picker,.sync-pill{margin-left:0}.workspace{grid-template-columns:1fr}.sidebar{border-bottom:1px solid #cfdbdc;border-right:0;max-height:42vh}.chat-header,.filters{grid-template-columns:1fr;align-items:stretch}.chat-header{flex-direction:column}.message-row{grid-template-columns:1fr}}