@acedatacloud/skills 2026.713.2 → 2026.713.3

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.
package/AGENTS.md CHANGED
@@ -29,7 +29,7 @@ Skills are located in the `skills/` directory (also mirrored to `.agents/skills/
29
29
  ### AI Chat & Tools
30
30
  - **ai-chat** — Unified LLM gateway — GPT, Claude, Gemini, Kimi, Grok (50+ models)
31
31
  - **google-search** — Search the web, images, news, maps, places, and videos via Google
32
- - **tgstat** — Discover and analyze public Telegram channels/groups (no login; optional TGStat API Token)
32
+ - **tgstat** — Discover and analyze public Telegram channels/groups (BYOC username; no login)
33
33
  - **face-transform** — Face analysis, beautification, age/gender transform, swap, cartoon
34
34
  - **short-url** — Create and manage short URLs
35
35
  - **onepage-pdf** — Convert an HTML page into one tall single-page PDF (local; no API token; needs Python + pymupdf + Chrome/Edge)
package/README.md CHANGED
@@ -48,7 +48,7 @@ Compatible with **30+ AI coding agents** via the [agentskills.io](https://agents
48
48
  |-------|-------------|
49
49
  | [ai-chat](skills/ai-chat/) | Unified LLM gateway — GPT, Claude, Gemini, Kimi, Grok (50+ models) |
50
50
  | [google-search](skills/google-search/) | Search the web, images, news, maps, places, and videos via Google |
51
- | [tgstat](skills/tgstat/) | Discover and analyze public Telegram channels/groups; no login required, optional TGStat API Token |
51
+ | [tgstat](skills/tgstat/) | Discover and analyze public Telegram channels/groups using a connected username as the default target |
52
52
  | [face-transform](skills/face-transform/) | Face analysis, beautification, age/gender transform, swap, cartoon |
53
53
  | [short-url](skills/short-url/) | Create and manage short URLs |
54
54
  | [onepage-pdf](skills/onepage-pdf/) | Convert an HTML page into one tall single-page PDF — no pagination breaks (local, no token) |
@@ -76,7 +76,7 @@ These skills drive third-party connectors users wire up at [auth.acedata.cloud/u
76
76
  | [didi-ride](skills/didi-ride/) | Book DiDi rides, estimate fares, query/cancel orders, plan routes via the DiDi MCP | `didi` (BYOC) |
77
77
  | [wecom](skills/wecom/) | WeCom (企业微信) self-built app — contacts, app messages, WeDoc, schedules, meetings | `wecom` (BYOC) |
78
78
  | [tencent-docs](skills/tencent-docs/) | Create / read / list / search / manage Tencent Docs — docs, sheets, slides, mind maps, flowcharts | `tencentdocs` (BYOC) |
79
- | [tgstat](skills/tgstat/) | Public Telegram source discovery, rankings, and audience research | `tgstat` (public; optional Token) |
79
+ | [tgstat](skills/tgstat/) | Public Telegram source discovery, rankings, and audience research | `tgstat` (BYOC username) |
80
80
 
81
81
  ## Prerequisites
82
82
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acedatacloud/skills",
3
- "version": "2026.713.2",
3
+ "version": "2026.713.3",
4
4
  "description": "Agent Skills for AceDataCloud AI services — music, image, video generation, LLM chat, web search. Compatible with Claude Code, GitHub Copilot, Gemini CLI, OpenAI Codex, and 30+ AI coding agents.",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -1,44 +1,35 @@
1
1
  ---
2
2
  name: tgstat
3
- description: "Research public Telegram channels and groups with TGStat. Use when discovering communities by topic, comparing audience size/reach/activity, checking a known @username, shortlisting ad or outreach sources, or querying TGStat API quota. Works without a TGStat login using web search plus public TGStat pages; an optional TGSTAT_TOKEN enables official structured search and full statistics. Read-only: does not join groups, scrape members, or send messages."
3
+ description: "Research public Telegram channels and groups with TGStat. Use when discovering communities by topic, comparing public audience/reach/activity, checking a known @username, or shortlisting ad and outreach sources. The connector supplies TGSTAT_USERNAME as the default profile. Read-only: does not log in, join groups, scrape members, or send messages."
4
4
  when_to_use: |
5
5
  Use for Telegram source discovery, competitor research, ad-channel
6
6
  selection, and public audience analysis. It can find channels/chats by
7
- keyword, inspect known public usernames, compare ranking metrics, and
8
- check optional TGStat API quota. Use the separate telegram connector for
9
- reading or sending messages in the user's own account.
7
+ keyword, inspect known public usernames, compare visible metrics, and use
8
+ the connected Telegram username as the default target. Use the separate
9
+ telegram connector for reading or sending messages in the user's account.
10
10
  connections: [tgstat]
11
11
  allowed_tools: [Bash, web_search, web_fetch]
12
12
  license: Apache-2.0
13
13
  metadata:
14
14
  author: acedatacloud
15
- version: "2.0"
15
+ version: "2.1"
16
16
  ---
17
17
 
18
18
  # TGStat Research
19
19
 
20
- Use [scripts/tgstat.py](./scripts/tgstat.py) for public TGStat pages and the
21
- optional official API. It uses only the Python standard library.
20
+ Use [scripts/tgstat.py](./scripts/tgstat.py) for public TGStat research. It uses
21
+ only the Python standard library. Resolve the script at the start of every Bash
22
+ call because each call runs in a fresh shell:
22
23
 
23
24
  ```bash
24
25
  TGSTAT="$SKILL_DIR/scripts/tgstat.py"; [ -f "$TGSTAT" ] || TGSTAT=$(find /tmp -maxdepth 8 -path '*/skills/*/scripts/tgstat.py' 2>/dev/null | head -1)
25
26
  [ -f "$TGSTAT" ] || { echo "tgstat script not found (SKILL_DIR=$SKILL_DIR)" >&2; exit 1; }
26
- python3 "$TGSTAT" mode
27
+ python3 "$TGSTAT" profile
27
28
  ```
28
29
 
29
- The connector has two modes:
30
-
31
- - **Public research (default):** no TGStat account or token. Uses `web_search`
32
- for discovery and public TGStat ranking/entity pages for verification.
33
- - **TGStat API Token (optional):** when the connector injects
34
- `$TGSTAT_TOKEN`, the same commands automatically use the official Stat API.
35
-
36
- Commands default to `--access-mode auto`. Use `--access-mode public` before the
37
- subcommand when a configured Token lacks access to a paid API method; use
38
- `--access-mode api` to require API mode and fail clearly if no Token exists.
39
-
40
- Never ask the user to paste a token into chat or pass it on the command line.
41
- If they want API mode, ask them to add the Token through the TGStat connector.
30
+ The connector injects `$TGSTAT_USERNAME`. It is not a login credential; it is
31
+ only the default public channel/group target. Never print the full environment
32
+ or treat the username as proof that the user owns the Telegram account.
42
33
 
43
34
  ## Discover Sources by Topic
44
35
 
@@ -47,8 +38,7 @@ Run `search` first:
47
38
  ```bash
48
39
  TGSTAT="$SKILL_DIR/scripts/tgstat.py"; [ -f "$TGSTAT" ] || TGSTAT=$(find /tmp -maxdepth 8 -path '*/skills/*/scripts/tgstat.py' 2>/dev/null | head -1)
49
40
  [ -f "$TGSTAT" ] || { echo "tgstat script not found (SKILL_DIR=$SKILL_DIR)" >&2; exit 1; }
50
- python3 "$TGSTAT" search "Claude API" --type all --language english --country us
51
- python3 "$TGSTAT" --access-mode api search --category technology --type channel
41
+ python3 "$TGSTAT" search "Claude API" --type all --language English --country US
52
42
  ```
53
43
 
54
44
  In public mode the command returns `web_queries`. Call `web_search` once per
@@ -65,20 +55,8 @@ TGStat's own keyword-search result endpoint requires sign-in. Do not try to
65
55
  bypass it, replay private AJAX endpoints, or claim public mode searches the
66
56
  full TGStat index.
67
57
 
68
- Official API mode also supports category-only search. Category values are
69
- TGStat reference keys; if a key is rejected, query the user's intended topic by
70
- keyword in public mode instead of guessing another category.
71
-
72
- With `$TGSTAT_TOKEN`, `search` calls the official `channels/search` endpoint
73
- instead. That endpoint may require a paid Stat API plan. `--language` must be a
74
- TGStat language key such as `english` or `russian`; `--country` must be a
75
- two-letter country code such as `us` or `ru`.
76
-
77
- If API search reports plan access denied, rerun explicitly in public mode:
78
-
79
- ```bash
80
- python3 "$TGSTAT" --access-mode public search "Claude API" --type all
81
- ```
58
+ `--language` and `--country` are search terms for the web index, not guaranteed
59
+ TGStat database filters.
82
60
 
83
61
  ## Browse Public Rankings
84
62
 
@@ -104,12 +82,13 @@ results as web-index discoveries, not as an authoritative TGStat rank.
104
82
 
105
83
  ## Inspect a Known Channel or Group
106
84
 
107
- Accept only a public `@username`, bare username, `t.me/<username>` link, or a
108
- TGStat entity URL:
85
+ Accept a public `@username`, bare username, `t.me/<username>` link, or a TGStat
86
+ entity URL. Omit the target to inspect `$TGSTAT_USERNAME` from the connector:
109
87
 
110
88
  ```bash
111
89
  TGSTAT="$SKILL_DIR/scripts/tgstat.py"; [ -f "$TGSTAT" ] || TGSTAT=$(find /tmp -maxdepth 8 -path '*/skills/*/scripts/tgstat.py' 2>/dev/null | head -1)
112
90
  [ -f "$TGSTAT" ] || { echo "tgstat script not found (SKILL_DIR=$SKILL_DIR)" >&2; exit 1; }
91
+ python3 "$TGSTAT" info
113
92
  python3 "$TGSTAT" info @durov
114
93
  python3 "$TGSTAT" stat https://t.me/example_public_chat
115
94
  ```
@@ -119,28 +98,20 @@ returns public metadata, and enriches it with ranking metrics when the entity
119
98
  appears in the current public ranking. Empty metrics mean TGStat did not expose
120
99
  them publicly; do not call that full statistics.
121
100
 
122
- With `$TGSTAT_TOKEN`:
123
-
124
- - `info` uses `channels/get` for structured identity/category metadata.
125
- - `stat` uses `channels/stat` for fuller channel reach/ER or chat activity.
126
-
127
101
  For ad selection, rank by relevant reach/activity rather than subscriber count
128
102
  alone. High subscribers with weak reach or chat MAU can indicate an inactive or
129
103
  inflated audience. Present metrics as evidence, not a guarantee of lead quality.
130
104
 
131
- ## Check API Mode and Quota
105
+ ## Check the Connected Profile
132
106
 
133
107
  ```bash
134
108
  TGSTAT="$SKILL_DIR/scripts/tgstat.py"; [ -f "$TGSTAT" ] || TGSTAT=$(find /tmp -maxdepth 8 -path '*/skills/*/scripts/tgstat.py' 2>/dev/null | head -1)
135
109
  [ -f "$TGSTAT" ] || { echo "tgstat script not found (SKILL_DIR=$SKILL_DIR)" >&2; exit 1; }
136
- python3 "$TGSTAT" mode
137
- python3 "$TGSTAT" quota
110
+ python3 "$TGSTAT" profile
138
111
  ```
139
112
 
140
- Without a Token, `quota` returns `null`. With a Token it calls `usage/stat`.
141
- If TGStat reports an inactive plan or insufficient access, explain that the
142
- public workflow still works and that API search/full stats depend on the user's
143
- TGStat plan.
113
+ `profile` reports the normalized public username used by target-less `info` and
114
+ `stat`. It does not verify ownership or authenticate to Telegram/TGStat.
144
115
 
145
116
  ## Outreach Research Workflow
146
117
 
@@ -162,5 +133,4 @@ For prospecting or partnership research:
162
133
  - Public pages and HTML can change; if parsing fails, use `web_fetch` for a
163
134
  single public page and report only values visible in that page.
164
135
  - Public web discovery is incomplete and regional TGStat pages may differ.
165
- - Never print `$TGSTAT_TOKEN`, include it in reports, or expose command errors
166
- containing secrets.
136
+ - Treat `$TGSTAT_USERNAME` as public profile context, not authentication.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """TGStat research CLI with a zero-auth public mode and optional API mode."""
2
+ """Public TGStat research CLI with a connected Telegram username."""
3
3
 
4
4
  from __future__ import annotations
5
5
 
@@ -15,7 +15,6 @@ from html.parser import HTMLParser
15
15
  from typing import Dict, List, Optional, Tuple, Union
16
16
 
17
17
  DEFAULT_PUBLIC_BASE = "https://tgstat.com"
18
- DEFAULT_API_BASE = "https://api.tgstat.ru"
19
18
  USER_AGENT = (
20
19
  "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
21
20
  "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126 Safari/537.36"
@@ -24,7 +23,6 @@ PUBLIC_USERNAME_RE = re.compile(r"[A-Za-z0-9_]{3,}")
24
23
  TGSTAT_HOST_RE = re.compile(r"^(?:[a-z]{2,3}\.)?tgstat\.com$", re.IGNORECASE)
25
24
  TGSTAT_INPUT_HOST_RE = re.compile(r"^(?:(?:[a-z]{2,3}\.)?tgstat\.com|tgstat\.ru)$", re.IGNORECASE)
26
25
  ENTITY_PATH_RE = re.compile(r"/(channel|chat)/(@[A-Za-z0-9_]{3,}|id\d+)/stat/?", re.IGNORECASE)
27
- API_HOST = urllib.parse.urlparse(DEFAULT_API_BASE).hostname or ""
28
26
 
29
27
 
30
28
  class TGStatError(RuntimeError):
@@ -66,12 +64,8 @@ def _safe_url(url: str) -> str:
66
64
  return urllib.parse.urlunsplit((parsed.scheme, parsed.netloc, parsed.path, "", ""))
67
65
 
68
66
 
69
- def _safe_error_body(body: str, url: str) -> str:
70
- compact = _compact(body)
71
- token = urllib.parse.parse_qs(urllib.parse.urlsplit(url).query).get("token", [""])[0]
72
- if token:
73
- compact = compact.replace(token, "[REDACTED]")
74
- return compact[:200]
67
+ def _safe_error_body(body: str) -> str:
68
+ return _compact(body)[:200]
75
69
 
76
70
 
77
71
  def _validate_request_url(url: str, initial_host: Optional[str] = None) -> None:
@@ -83,9 +77,6 @@ def _validate_request_url(url: str, initial_host: Optional[str] = None) -> None:
83
77
  if TGSTAT_HOST_RE.fullmatch(allowed_from):
84
78
  if not TGSTAT_HOST_RE.fullmatch(host):
85
79
  raise TGStatError(f"TGStat redirected to an unexpected host: {host}")
86
- elif allowed_from == API_HOST:
87
- if host != API_HOST:
88
- raise TGStatError(f"TGStat API redirected to an unexpected host: {host}")
89
80
  else:
90
81
  raise TGStatError(f"unsupported TGStat request host: {allowed_from}")
91
82
 
@@ -325,9 +316,9 @@ def _request_with_url(
325
316
  return response.read().decode("utf-8", "replace"), response.geturl()
326
317
  except urllib.error.HTTPError as exc:
327
318
  body = exc.read().decode("utf-8", "replace")
328
- raise TGStatError(f"HTTP {exc.code} from {_safe_url(url)}: {_safe_error_body(body, url)}") from exc
319
+ raise TGStatError(f"HTTP {exc.code} from {_safe_url(url)}: {_safe_error_body(body)}") from exc
329
320
  except urllib.error.URLError as exc:
330
- raise TGStatError(f"network error for {_safe_url(url)}: {_safe_error_body(str(exc.reason), url)}") from exc
321
+ raise TGStatError(f"network error for {_safe_url(url)}: {_safe_error_body(str(exc.reason))}") from exc
331
322
 
332
323
 
333
324
  def _request(url: str, timeout: int, data: Optional[bytes] = None, headers: Optional[dict] = None) -> str:
@@ -341,30 +332,7 @@ def _public_base(value: str) -> str:
341
332
  return f"https://{parsed.hostname}"
342
333
 
343
334
 
344
- def _api_get(args: argparse.Namespace, path: str, params: Optional[dict] = None) -> dict:
345
- token = os.environ.get("TGSTAT_TOKEN", "")
346
- if not token:
347
- raise TGStatError("TGSTAT_TOKEN is required for this API-only operation")
348
- query = {"token": token, **{key: value for key, value in (params or {}).items() if value not in (None, "")}}
349
- url = f"{DEFAULT_API_BASE}/{path.lstrip('/')}?{urllib.parse.urlencode(query)}"
350
- raw = _request(url, args.timeout)
351
- try:
352
- payload = json.loads(raw)
353
- except json.JSONDecodeError as exc:
354
- raise TGStatError("TGStat API returned a non-JSON response") from exc
355
- if payload.get("status") != "ok":
356
- error = str(payload.get("error") or "TGStat API request failed").replace(token, "[REDACTED]")
357
- raise TGStatError(error)
358
- return payload
359
-
360
-
361
335
  def _normalize_target(target: str) -> Tuple[Optional[str], Optional[str], Optional[str]]:
362
- return _normalize_target_for_mode(target, for_api=False)
363
-
364
-
365
- def _normalize_target_for_mode(
366
- target: str, *, for_api: bool
367
- ) -> Tuple[Optional[str], Optional[str], Optional[str]]:
368
336
  target = target.strip()
369
337
  if target.startswith("http://") or target.startswith("https://"):
370
338
  parsed = urllib.parse.urlparse(target)
@@ -375,8 +343,6 @@ def _normalize_target_for_mode(
375
343
  if not entity:
376
344
  raise TGStatError("TGStat target must be a channel or chat statistics URL")
377
345
  identifier = entity[1]
378
- if for_api and identifier.lower().startswith("id"):
379
- identifier = identifier[2:]
380
346
  canonical_url = target
381
347
  if parsed.hostname.casefold() == "tgstat.ru":
382
348
  canonical_url = urllib.parse.urlunsplit(("https", "tgstat.com", parsed.path, "", ""))
@@ -390,47 +356,40 @@ def _normalize_target_for_mode(
390
356
  if target.startswith("@") and PUBLIC_USERNAME_RE.fullmatch(target[1:]):
391
357
  return target, None, None
392
358
  if re.fullmatch(r"\d+", target):
393
- return (target if for_api else f"id{target}"), None, None
359
+ return f"id{target}", None, None
394
360
  if re.fullmatch(r"id\d+", target, re.IGNORECASE):
395
- digits = target[2:]
396
- return (digits if for_api else f"id{digits}"), None, None
361
+ return f"id{target[2:]}", None, None
397
362
  if PUBLIC_USERNAME_RE.fullmatch(target):
398
363
  return f"@{target}", None, None
399
364
  raise TGStatError("target must be @username, a t.me link, TGStat entity URL, or id<number>")
400
365
 
401
366
 
402
- def _normalize_api_filters(language: str, country: str) -> Tuple[str, str]:
403
- normalized_language = language.strip().lower()
404
- normalized_country = country.strip().lower()
405
- if normalized_language and not re.fullmatch(r"[a-z][a-z0-9_-]*", normalized_language):
406
- raise TGStatError("language must be a TGStat language key such as english or russian")
407
- if normalized_country and not re.fullmatch(r"[a-z]{2}", normalized_country):
408
- raise TGStatError("country must be a two-letter code such as us or ru")
409
- return normalized_language, normalized_country
367
+ def _connected_username() -> str:
368
+ value = os.environ.get("TGSTAT_USERNAME", "").strip()
369
+ if not value:
370
+ raise TGStatError("TGSTAT_USERNAME is not configured; reconnect TGStat with a public Telegram username")
371
+ identifier, _, _ = _normalize_target(value)
372
+ if not identifier or not identifier.startswith("@"):
373
+ raise TGStatError("TGSTAT_USERNAME must be a public Telegram username such as @durov")
374
+ return identifier
410
375
 
411
376
 
412
- def command_mode(args: argparse.Namespace) -> None:
413
- has_token = bool(os.environ.get("TGSTAT_TOKEN"))
414
- effective_mode = "api" if _use_api(args) else "public"
377
+ def _target_or_profile(target: str) -> str:
378
+ return target.strip() or _connected_username()
379
+
380
+
381
+ def command_profile(args: argparse.Namespace) -> None:
382
+ username = _connected_username()
415
383
  _json_out(
416
384
  {
417
- "mode": effective_mode,
418
- "access_mode": args.access_mode,
419
- "token_configured": has_token,
420
- "public_capabilities": ["web-index discovery", "public rankings", "public entity pages"],
421
- "api_capabilities": ["quota", "structured channel/chat search", "full channel statistics"],
385
+ "mode": "public",
386
+ "username": username,
387
+ "telegram_url": f"https://t.me/{username[1:]}",
388
+ "verified": False,
422
389
  }
423
390
  )
424
391
 
425
392
 
426
- def command_quota(args: argparse.Namespace) -> None:
427
- if not _use_api(args):
428
- _json_out({"mode": "public", "token_configured": False, "quota": None})
429
- return
430
- payload = _api_get(args, "usage/stat")
431
- _json_out({"mode": "api", "quota": payload.get("response")})
432
-
433
-
434
393
  def _web_queries(query: str, peer_type: str, language: str, country: str) -> List[str]:
435
394
  suffix = " ".join(part for part in (language, country) if part).strip()
436
395
  quoted = f'"{query}"'
@@ -440,54 +399,16 @@ def _web_queries(query: str, peer_type: str, language: str, country: str) -> Lis
440
399
  return queries
441
400
 
442
401
 
443
- def _use_api(args: argparse.Namespace) -> bool:
444
- has_token = bool(os.environ.get("TGSTAT_TOKEN"))
445
- if args.access_mode == "public":
446
- return False
447
- if args.access_mode == "api" and not has_token:
448
- raise TGStatError("TGSTAT_TOKEN is required when --access-mode api is selected")
449
- return has_token
450
-
451
-
452
402
  def command_search(args: argparse.Namespace) -> None:
453
403
  query = args.query.strip()
454
- category = args.category.strip()
455
- if not query and not category:
456
- raise TGStatError("search requires a query or --category")
457
- if query and len(query) < 3:
404
+ if len(query) < 3:
458
405
  raise TGStatError("query must contain at least 3 characters")
459
- if _use_api(args):
460
- language, country = _normalize_api_filters(args.language, args.country)
461
- payload = _api_get(
462
- args,
463
- "channels/search",
464
- {
465
- "q": query,
466
- "category": category,
467
- "peer_type": args.type,
468
- "language": language,
469
- "country": country,
470
- "search_by_description": 1 if args.description else 0,
471
- "limit": min(max(args.limit, 1), 100),
472
- },
473
- )
474
- _json_out(
475
- {
476
- "mode": "api",
477
- "query": query or None,
478
- "category": category or None,
479
- "response": payload.get("response"),
480
- }
481
- )
482
- return
483
- discovery_term = query or category
484
406
  _json_out(
485
407
  {
486
408
  "mode": "public",
487
- "query": query or None,
488
- "category": category or None,
409
+ "query": query,
489
410
  "requires_web_search": True,
490
- "web_queries": _web_queries(discovery_term, args.type, args.language, args.country),
411
+ "web_queries": _web_queries(query, args.type, args.language, args.country),
491
412
  "instructions": (
492
413
  "Run web_search for each query, keep only tgstat.com and t.me results, "
493
414
  "deduplicate by username, then inspect shortlisted TGStat URLs with the info command."
@@ -588,41 +509,29 @@ def _public_info(args: argparse.Namespace, target: str, peer_type: str) -> dict:
588
509
 
589
510
 
590
511
  def command_info(args: argparse.Namespace) -> None:
591
- if _use_api(args):
592
- identifier, _, _ = _normalize_target_for_mode(args.target, for_api=True)
593
- payload = _api_get(args, "channels/get", {"channelId": identifier})
594
- _json_out({"mode": "api", "response": payload.get("response")})
595
- return
596
- _json_out({"mode": "public", **_public_info(args, args.target, args.type)})
512
+ _json_out({"mode": "public", **_public_info(args, _target_or_profile(args.target), args.type)})
597
513
 
598
514
 
599
515
  def command_stat(args: argparse.Namespace) -> None:
600
- if _use_api(args):
601
- identifier, _, _ = _normalize_target_for_mode(args.target, for_api=True)
602
- payload = _api_get(args, "channels/stat", {"channelId": identifier})
603
- _json_out({"mode": "api", "response": payload.get("response")})
604
- return
605
- _json_out({"mode": "public", "limited_metrics": True, **_public_info(args, args.target, args.type)})
516
+ _json_out(
517
+ {"mode": "public", "limited_metrics": True, **_public_info(args, _target_or_profile(args.target), args.type)}
518
+ )
606
519
 
607
520
 
608
521
  def build_parser() -> argparse.ArgumentParser:
609
522
  parser = argparse.ArgumentParser(description=__doc__)
610
- parser.add_argument("--access-mode", choices=("auto", "public", "api"), default="auto")
611
523
  parser.add_argument("--host", default=os.environ.get("TGSTAT_PUBLIC_HOST", DEFAULT_PUBLIC_BASE))
612
524
  parser.add_argument("--timeout", type=int, default=30)
613
525
  sub = parser.add_subparsers(dest="command", required=True)
614
526
 
615
- sub.add_parser("mode").set_defaults(func=command_mode)
616
- sub.add_parser("quota").set_defaults(func=command_quota)
527
+ sub.add_parser("profile").set_defaults(func=command_profile)
617
528
 
618
529
  search = sub.add_parser("search")
619
- search.add_argument("query", nargs="?", default="")
620
- search.add_argument("--category", default="")
530
+ search.add_argument("query")
621
531
  search.add_argument("--type", choices=("channel", "chat", "all"), default="all")
622
532
  search.add_argument("--language", default="")
623
533
  search.add_argument("--country", default="")
624
534
  search.add_argument("--limit", type=int, default=20)
625
- search.add_argument("--description", action="store_true")
626
535
  search.set_defaults(func=command_search)
627
536
 
628
537
  rankings = sub.add_parser("rankings")
@@ -633,7 +542,7 @@ def build_parser() -> argparse.ArgumentParser:
633
542
 
634
543
  for name, func in (("info", command_info), ("stat", command_stat)):
635
544
  command = sub.add_parser(name)
636
- command.add_argument("target")
545
+ command.add_argument("target", nargs="?", default="")
637
546
  command.add_argument("--type", choices=("auto", "channel", "chat"), default="auto")
638
547
  command.set_defaults(func=func)
639
548
  return parser