Telemax/docs/index.html

723 lines
57 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Telemax — мост MAX ↔ Telegram: переписка из MAX прямо в Telegram</title>
<meta name="description" content="Telemax — бесплатный мост между MAX и Telegram с открытым кодом. Входит в ваш личный аккаунт MAX по номеру и приносит все чаты в Telegram: каждый чат — отдельная тема. Сообщения, файлы до 4 ГБ, реакции, кнопки ботов. Ставится на свой сервер одной командой.">
<meta name="keywords" content="MAX в Telegram, мост MAX Telegram, Макс в Телеграм, пересылка сообщений из MAX в Telegram, читать MAX в Telegram, MAX Telegram bridge, Telemax">
<link rel="canonical" href="https://telemax.svnest.ru/">
<meta property="og:type" content="website">
<meta property="og:locale" content="ru_RU">
<meta property="og:site_name" content="Telemax">
<meta property="og:title" content="Telemax — переписка из MAX прямо в Telegram">
<meta property="og:description" content="Бесплатный мост MAX ↔ Telegram с открытым кодом: все чаты MAX — темами в вашей группе Telegram. Свой сервер, одна команда для установки.">
<meta property="og:url" content="https://telemax.svnest.ru/">
<meta property="og:image" content="https://telemax.svnest.ru/og.png">
<meta property="og:image:width" content="780">
<meta property="og:image:height" content="440">
<meta name="twitter:card" content="summary_large_image">
<link rel="icon" type="image/png" href="icon.png">
<link rel="apple-touch-icon" href="icon.png">
<link rel="alternate" type="application/rss+xml" title="Новости Telemax" href="https://telemax.svnest.ru/news/feed.xml">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Telemax",
"description": "Бесплатный мост между мессенджерами MAX и Telegram с открытым кодом. Подключается к личному аккаунту MAX по номеру и приносит все чаты в группу Telegram, каждый чат — отдельной темой.",
"applicationCategory": "CommunicationApplication",
"operatingSystem": "Linux (Ubuntu 22.04+, Debian 12+)",
"url": "https://telemax.svnest.ru/",
"downloadUrl": "https://github.com/Trollobot/Telemax",
"license": "https://www.gnu.org/licenses/agpl-3.0.html",
"image": "https://telemax.svnest.ru/og.png",
"softwareVersion": "1.3.6",
"dateModified": "2026-10-13",
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "RUB" }
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{ "@type": "Question", "name": "Как читать и отвечать на сообщения MAX в Telegram?",
"acceptedAnswer": { "@type": "Answer", "text": "Поставьте Telemax на свой сервер и войдите в свой аккаунт MAX через бота. Все чаты MAX появятся в вашей группе Telegram отдельными темами: отвечаете в теме — ответ уходит в MAX от вашего имени." } },
{ "@type": "Question", "name": "Нужно ли приложение MAX?",
"acceptedAnswer": { "@type": "Answer", "text": "Один раз — да: аккаунт MAX регистрируется в приложении, там же ставится пароль. Дальше переписка идёт через Telegram, и приложение можно не открывать." } },
{ "@type": "Question", "name": "Это бот в MAX или мой аккаунт?",
"acceptedAnswer": { "@type": "Answer", "text": "Ваш личный аккаунт: Telemax входит по номеру телефона, поэтому приходят все ваши чаты, а не только те, куда добавлен бот. Со стороны Telegram работает ваш собственный бот." } },
{ "@type": "Question", "name": "Можно ли начать новый чат из Telegram?",
"acceptedAnswer": { "@type": "Answer", "text": "Да: в пульте «Найти контакт» — по номеру в любом виде, по MAX ID или нику бота. Или отправьте в общую тему группы контакт из телефонной книги. Не найдётся только тот, кто закрыл поиск по номеру в настройках приватности MAX, — тогда пусть напишет первым." } },
{ "@type": "Question", "name": "Можно ли показать чат MAX тем, у кого нет MAX?",
"acceptedAnswer": { "@type": "Answer", "text": "Да, трансляцией: команда /broadcast в теме чата — и новые сообщения этого чата копируются в обычную группу или канал Telegram. Мест сколько угодно, бота туда Telegram добавит сам. Трансляция идёт в одну сторону: что пишут там, в MAX не уходит." } },
{ "@type": "Question", "name": "Передаются ли файлы, голосовые и кружки?",
"acceptedAnswer": { "@type": "Answer", "text": "Да, в обе стороны. Файлы больше 50 МБ из MAX приходят ссылкой на ваш сервер, а в MAX можно отправить файл до 4 ГБ через страницу загрузки. Для таких ссылок нужен белый IP сервера и открытые порты 80 и 443 — установщик проверит это сам и, если чего-то не хватает, объяснит." } },
{ "@type": "Question", "name": "Сколько это стоит?",
"acceptedAnswer": { "@type": "Answer", "text": "Telemax бесплатный, весь функционал открыт. Платите только за свой сервер — хватит самого простого VPS." } },
{ "@type": "Question", "name": "Заработает ли на российском VPS?",
"acceptedAnswer": { "@type": "Answer", "text": "Да. Если Telegram с сервера не открывается, мост может работать через прокси. Сам прокси нужен свой (SOCKS5 или HTTP) — его адрес указывают при установке, MAX при этом идёт напрямую." } },
{ "@type": "Question", "name": "Можно ли подключить два аккаунта MAX?",
"acceptedAnswer": { "@type": "Answer", "text": "Да: второй мост ставится той же командой в соседний каталог, со своим ботом и своей группой. Автообновление обслуживает все мосты на сервере." } },
{ "@type": "Question", "name": "Что будет, если GitHub недоступен?",
"acceptedAnswer": { "@type": "Answer", "text": "Установка и обновления сами переключатся на зеркало проекта. Каждый релиз подписан, и мост проверяет подпись перед установкой — неважно, откуда пришёл код." } },
{ "@type": "Question", "name": "Это безопасно?",
"acceptedAnswer": { "@type": "Answer", "text": "Соединения шифруются: MAX ↔ сервер по TLS, сервер ↔ Telegram по HTTPS. Код и данные — на вашем сервере, посредников нет, код открыт. Но шифрование не сквозное: сервер видит переписку, иначе не смог бы её пересылать, — поэтому его стоит защищать. Проект неофициальный — используйте на свой страх и риск." } }
]
}
</script>
<style>
:root{
--paper:#f5ecd9; --paper-2:#faf4e6; --card:#fffdf8;
--ink:#2b2119; --muted:#6f6152; --faint:#948676;
--line:#e4d4b6; --line-soft:#eaddc4;
--terra:#c2571f; --terra-dark:#9d4315; --terra-tint:#f0dcbb;
--olive:#7a7433; --ok:#4d7c2f;
--serif:Georgia,"Times New Roman","PT Serif",serif;
--radius:14px; --shadow:0 1px 2px rgba(70,45,20,.05),0 14px 34px rgba(90,55,20,.09);
}
*{box-sizing:border-box}
body{margin:0;background:var(--paper);color:var(--ink);
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
line-height:1.62;-webkit-font-smoothing:antialiased}
a{color:var(--terra-dark);text-decoration:none}
a:hover{text-decoration:underline}
.wrap{max-width:1000px;margin:0 auto;padding:0 22px}
h1,h2,h3{font-family:var(--serif);line-height:1.16;margin:0 0 .4em;font-weight:700}
h2{font-size:clamp(1.6rem,3.6vw,2.3rem)}
.sub{color:var(--muted)}
.btn{display:inline-block;padding:13px 22px;border-radius:12px;font-weight:700;border:1.5px solid transparent;transition:transform .05s}
.btn:active{transform:translateY(1px)}
.btn-primary{background:var(--terra);color:#fff8ef}
.btn-primary:hover{background:var(--terra-dark);text-decoration:none}
.btn-ghost{background:transparent;border-color:var(--line);color:var(--ink)}
.btn-ghost:hover{border-color:var(--terra);text-decoration:none}
header.nav{position:sticky;top:0;z-index:10;background:rgba(245,236,217,.86);
backdrop-filter:blur(8px);border-bottom:1px solid var(--line)}
.nav .wrap{display:flex;align-items:center;gap:16px;height:62px}
.brand{font-family:var(--serif);font-weight:700;font-size:1.35rem;letter-spacing:-.01em}
.brand .dot{color:var(--terra)}
.nav .spacer{flex:1}
.nav a.navlink{color:var(--muted);font-weight:600;font-size:.94rem;font-family:inherit}
.nav a.support{color:var(--terra-dark)}
@media(max-width:680px){.nav a.navlink{display:none}.nav a.support{display:inline}}
.eye{font-size:.82rem;font-weight:700;color:var(--terra-dark);letter-spacing:.02em;margin-bottom:14px}
.chip{display:inline-flex;align-items:center;gap:7px;background:var(--terra-tint);color:var(--terra-dark);
border:1px solid var(--line);padding:6px 13px;border-radius:999px;font-size:.82rem;font-weight:700;margin-bottom:20px}
/* hero */
.hero{padding:66px 22px 44px;text-align:center}
.hero h1{font-size:clamp(2.1rem,5.8vw,3.5rem);letter-spacing:-.01em;margin-bottom:.35em}
.hero h1 .hl{color:var(--terra)}
.hero p.lead{font-size:clamp(1.05rem,2.3vw,1.32rem);color:var(--muted);max-width:720px;margin:0 auto 28px}
.cta{display:flex;gap:12px;justify-content:center;flex-wrap:wrap;margin-bottom:38px}
.newsbar{display:inline-flex;align-items:center;gap:10px;max-width:100%;margin:-10px auto 34px;padding:6px 8px 6px 6px;
background:var(--card);border:1px solid var(--line);border-radius:999px;box-shadow:var(--shadow);color:var(--ink);font-size:.92rem}
.newsbar:hover{text-decoration:none;border-color:var(--terra)}
.newsbar .nb-tag{background:var(--terra);color:#fff8ef;font-weight:800;font-size:.76rem;border-radius:999px;padding:3px 10px;white-space:nowrap}
.newsbar .nb-text{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.newsbar .nb-go{color:var(--terra);font-weight:800;padding-right:6px}
section{padding:58px 0;border-top:1px solid var(--line-soft)}
section.alt{background:var(--paper-2)}
.kicker{color:var(--terra);font-weight:700;text-transform:uppercase;letter-spacing:.09em;font-size:.76rem;margin-bottom:10px;font-family:inherit}
/* differentiator */
.callout{background:var(--card);border:1px solid var(--line);border-left:5px solid var(--terra);
border-radius:0 var(--radius) var(--radius) 0;padding:28px 32px;box-shadow:var(--shadow)}
.callout .vs{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:22px}
@media(max-width:560px){.callout .vs{grid-template-columns:1fr}.callout{padding:22px 20px}}
.vs .c{background:var(--paper-2);border:1px solid var(--line);border-radius:12px;padding:16px 18px;font-size:.95rem;color:var(--muted)}
.vs .c.good{background:#f2f6e9;border-color:#d3e0bd}
.vs .tag{font-weight:700;font-size:.85rem;margin-bottom:6px;font-family:var(--serif)}
.vs .good .tag{color:var(--ok)} .vs .bad .tag{color:var(--faint)}
/* features */
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
@media(max-width:820px){.grid{grid-template-columns:repeat(2,1fr)}}
@media(max-width:520px){.grid{grid-template-columns:1fr}}
.feat{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:22px;box-shadow:var(--shadow)}
.feat .ic{font-size:1.6rem;margin-bottom:10px}
.feat h3{font-size:1.08rem;margin-bottom:.3em}
.feat p{color:var(--muted);font-size:.94rem;margin:0}
/* steps */
.steps{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;counter-reset:s}
@media(max-width:720px){.steps{grid-template-columns:1fr;gap:28px}}
.step{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:24px 22px 22px;position:relative}
.step::before{counter-increment:s;content:counter(s);position:absolute;top:-15px;left:22px;
width:34px;height:34px;border-radius:50%;background:var(--terra);color:#fff8ef;font-weight:800;font-family:var(--serif);
display:flex;align-items:center;justify-content:center}
.step h3{margin-top:8px;font-size:1.08rem}
.step p{color:var(--muted);margin:0;font-size:.94rem}
code{background:var(--terra-tint);color:var(--terra-dark);padding:2px 7px;border-radius:6px;
font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.88em}
.cmd{margin:26px 0 0}
.cmd .label{font-size:.88rem;color:var(--muted);margin:14px 0 6px}
.cmd pre{margin:0;background:#2b2119;color:#f6e6d2;border-radius:12px;padding:14px 16px;overflow-x:auto;
font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.9rem;line-height:1.5}
/* screenshots */
.shots{display:grid;grid-template-columns:repeat(2,1fr);gap:22px;align-items:start}
@media(max-width:700px){.shots{grid-template-columns:1fr}}
.shot{margin:0}
.shot.wide{grid-column:1/-1;justify-self:center;width:100%;max-width:640px}
.shot img{width:100%;height:auto;display:block;border-radius:14px;border:1px solid var(--line);box-shadow:var(--shadow);background:#0e1621}
.shot figcaption{text-align:center;font-size:.88rem;color:var(--muted);margin-top:10px}
/* limits */
.limits{margin:22px 0 0;padding:0;list-style:none;display:grid;gap:10px}
.limits li{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:12px 16px;color:var(--muted)}
/* support */
.support-box{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
padding:34px;text-align:center;box-shadow:var(--shadow)}
.support-box .heart{font-size:2rem;margin-bottom:8px}
.support-box p{color:var(--muted);max-width:560px;margin:0 auto 22px}
.crypto{margin-top:18px;font-size:.85rem;color:var(--faint)}
.crypto code{word-break:break-all}
/* faq */
details{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:2px 18px;margin-bottom:12px}
details summary{cursor:pointer;font-weight:700;padding:15px 0;list-style:none;font-family:var(--serif)}
details summary::-webkit-details-marker{display:none}
details summary::before{content:"+";color:var(--terra);font-weight:800;margin-right:10px}
details[open] summary::before{content:"-"}
details p{color:var(--muted);margin:0 0 15px;padding-left:24px}
footer{padding:44px 0 64px;color:var(--muted);border-top:1px solid var(--line)}
footer .links{display:flex;gap:12px 20px;flex-wrap:wrap;margin-bottom:18px}
.disc{font-size:.85rem;background:var(--paper-2);border:1px solid var(--line);border-radius:12px;padding:15px 18px}
.center{text-align:center}
/* hero animation: MAX ↔ your server ↔ Telegram (scenes: переписка, файлы, трансляция, всё ваше) */
.flow{margin:0 auto;max-width:760px;background:var(--card);border:1px solid var(--line);
border-radius:var(--radius);box-shadow:var(--shadow);padding:18px 18px 16px;text-align:center}
.flow-stage{position:relative;height:clamp(250px,42vw,320px);overflow:hidden;border-radius:10px;
background:
radial-gradient(circle at 50% 38%,rgba(194,87,31,.07),transparent 46%),
linear-gradient(var(--paper-2),var(--card))}
.flow-lines{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
.flow-lines line{stroke-linecap:round;vector-effect:non-scaling-stroke}
.flow-lines .track{stroke:var(--terra);stroke-width:3;stroke-dasharray:2 9;animation:march 1.2s linear infinite}
.flow-lines .branch{stroke:var(--terra);stroke-width:2;stroke-dasharray:2 8;opacity:0;transition:opacity .5s;animation:march 1.2s linear infinite}
.flow[data-scene="cast"] .flow-lines .branch{opacity:.75}
@keyframes march{to{stroke-dashoffset:-11}}
.fnode{position:absolute;transform:translate(-50%,-50%);display:flex;flex-direction:column;align-items:center;justify-content:center;
z-index:2;font-family:var(--serif);font-weight:800;line-height:1.1;user-select:none}
.fnode small{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;font-weight:500;font-size:.68rem;color:var(--muted);margin-top:3px}
.fnode.round{width:clamp(74px,14vw,104px);height:clamp(74px,14vw,104px);border-radius:50%;background:var(--paper-2);border:2px solid var(--ink)}
.fmax{left:13%;top:38%;color:var(--olive);border-color:var(--olive)!important;font-size:clamp(1rem,2.6vw,1.3rem)}
.ftg{left:87%;top:38%;font-size:clamp(.82rem,2.1vw,1.02rem)}
.fsrv{left:50%;top:38%;width:clamp(104px,20vw,142px);height:clamp(58px,10vw,74px);border-radius:16px;background:var(--terra);color:#fff8ef;
font-size:clamp(.95rem,2.4vw,1.15rem);box-shadow:0 8px 22px rgba(157,67,21,.28)}
.fsrv small{color:#f6e6d2}
.flow[data-scene="own"] .fsrv{animation:glow 1.8s ease-in-out infinite}
@keyframes glow{50%{box-shadow:0 0 0 10px rgba(194,87,31,.14),0 8px 22px rgba(157,67,21,.28)}}
.cnt{position:absolute;top:2px;right:2px;min-width:22px;height:22px;padding:0 6px;border-radius:999px;background:var(--terra);color:#fff8ef;
font:800 .74rem/22px -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;opacity:0;transform:scale(.4);transition:opacity .2s,transform .25s}
.cnt.on{opacity:1;transform:scale(1)}
.ftarget{width:auto;height:auto;padding:7px 12px;border-radius:12px;background:var(--card);border:1.5px solid var(--terra);font-size:1.1rem;
flex-direction:row;gap:6px;opacity:0;transition:opacity .45s,transform .45s;transform:translate(-50%,-30%)}
.ftarget small{margin:0;font-size:.74rem;color:var(--ink);font-weight:700}
.fgroup{left:62%;top:84%}
.fchan{left:86%;top:84%}
.flow[data-scene="cast"] .ftarget{opacity:1;transform:translate(-50%,-50%)}
.fbadge{position:absolute;transform:translate(-50%,-50%) translateY(8px);z-index:3;opacity:0;transition:opacity .4s,transform .4s;
background:var(--card);border:1px solid var(--line);border-radius:999px;padding:4px 11px;font-size:clamp(.66rem,1.7vw,.8rem);font-weight:700;
color:var(--ink);white-space:nowrap;box-shadow:0 4px 12px rgba(90,55,20,.1)}
.fbadge.store{left:50%;top:72%;display:flex;align-items:center;gap:8px}
.fbadge.store .bar{display:inline-block;width:64px;height:6px;border-radius:9px;background:var(--terra-tint);overflow:hidden}
.fbadge.store .bar i{display:block;height:100%;width:0;background:var(--terra);border-radius:9px}
.flow[data-scene="files"] .fbadge.store{opacity:1;transform:translate(-50%,-50%)}
.fbadge.own{border-color:var(--terra);color:var(--terra-dark)}
.b-acc{left:13%;top:72%}.b-srv{left:50%;top:72%}.b-bot{left:87%;top:72%}
.b-free{left:50%;top:11%;background:var(--terra);color:#fff8ef;border-color:var(--terra);font-size:clamp(.72rem,1.9vw,.9rem);padding:5px 14px}
.flow[data-scene="own"] .fbadge.own{opacity:1;transform:translate(-50%,-50%)}
.flow[data-scene="own"] .b-srv{transition-delay:.15s}.flow[data-scene="own"] .b-bot{transition-delay:.3s}.flow[data-scene="own"] .b-free{transition-delay:.55s}
.fchip{position:absolute;left:0;top:0;z-index:4;white-space:nowrap;pointer-events:none;will-change:transform,opacity;
background:var(--card);border:1px solid var(--line);border-radius:14px 14px 14px 4px;padding:5px 11px;
font-size:clamp(.68rem,1.8vw,.86rem);font-weight:600;color:var(--ink);box-shadow:0 6px 16px rgba(90,55,20,.14);opacity:0}
.fchip.back{border-radius:14px 14px 4px 14px;background:#f2f6e9;border-color:#d3e0bd}
.fchip.big{border-color:var(--terra);font-weight:700}
.fchip.btn{background:var(--terra);color:#fff8ef;border-color:var(--terra);border-radius:10px}
.fchip.copy{font-size:clamp(.6rem,1.5vw,.72rem);padding:3px 8px}
.flow-cap{min-height:74px;margin-top:14px;transition:opacity .25s}
.flow-cap.fade{opacity:0}
.flow-title{font-family:var(--serif);font-weight:700;font-size:clamp(1.15rem,2.8vw,1.45rem);line-height:1.2}
.flow-sub{color:var(--muted);font-size:.94rem;max-width:560px;margin:4px auto 0}
.flow-steps{display:flex;justify-content:center;gap:8px;flex-wrap:wrap;margin-top:12px}
.flow-steps button{font:inherit;font-size:.82rem;font-weight:700;color:var(--muted);background:transparent;border:1px solid var(--line);
border-radius:999px;padding:5px 12px;cursor:pointer;position:relative;overflow:hidden}
.flow-steps button:hover{border-color:var(--terra);color:var(--terra-dark)}
.flow-steps button.on{color:#fff8ef;background:var(--terra);border-color:var(--terra)}
.flow-steps button.on::after{content:"";position:absolute;left:0;bottom:0;height:3px;background:rgba(255,248,239,.7);
animation:prog var(--dur,8s) linear forwards}
@keyframes prog{from{width:0}to{width:100%}}
@media(max-width:560px){
.flow{padding:12px 10px 14px}
.ftarget{padding:5px 8px;font-size:.92rem;gap:4px}.ftarget small{font-size:.64rem}
.fgroup{left:55%}.fchan{left:84%}
.b-acc{left:19%}.b-bot{left:83%}
.fbadge{padding:3px 8px}
}
@media(prefers-reduced-motion:reduce){.flow-lines .track,.flow-lines .branch{animation:none}.flow[data-scene="own"] .fsrv{animation:none}.flow-steps button.on::after{display:none}}
</style>
</head>
<body>
<header class="nav">
<div class="wrap">
<span class="brand" style="display:inline-flex;align-items:center;gap:9px">
<img src="icon.png" alt="Telemax" width="30" height="30" style="border-radius:8px;display:block">
<span>Tele<span class="dot">max</span></span>
</span>
<span class="spacer"></span>
<a class="navlink" href="#features">Возможности</a>
<a class="navlink" href="max-v-telegram/">Инструкция</a>
<a class="navlink" href="news/">Новости</a>
<a class="navlink" href="#faq">Вопросы</a>
<a class="navlink" href="#contact">Связаться</a>
<a class="navlink" href="https://github.com/Trollobot/Telemax">GitHub</a>
<a class="navlink support" href="#support">♥ Поддержать</a>
</div>
</header>
<main>
<div class="hero wrap">
<div class="eye">бесплатно · открытый код · свой сервер</div>
<span class="chip">🔑 ваш личный аккаунт MAX, а не бот</span>
<h1>Переписка из MAX —<br>прямо в <span class="hl">Telegram</span></h1>
<p class="lead">
<b>Telemax</b> — бесплатный мост между <b>MAX</b> и <b>Telegram</b> с открытым кодом.
Он входит в ваш <b>личный аккаунт MAX по номеру</b> и приносит все чаты в Telegram:
каждый чат — отдельная тема в вашей группе. Читаете и отвечаете из привычного Телеграма,
а мост работает на вашем сервере.
</p>
<div class="cta">
<a class="btn btn-primary" href="#how">Как установить</a>
<a class="btn btn-ghost" href="https://github.com/Trollobot/Telemax">Открыть на GitHub</a>
</div>
<!-- news:latest -->
<a class="newsbar" href="news/#v1.3.6"><span class="nb-tag">Новое · v1.3.6</span><span class="nb-text">🖼️ Фото из MAX больше не теряются</span><span class="nb-go" aria-hidden="true">→</span></a>
<!-- /news:latest -->
<div class="flow" id="flow" data-scene="chat" role="region" aria-label="Как работает Telemax: MAX, ваш сервер и Telegram">
<div class="flow-stage" aria-hidden="true">
<svg class="flow-lines" viewBox="0 0 100 100" preserveAspectRatio="none">
<line class="track" x1="13" y1="38" x2="87" y2="38"/>
<line class="branch" data-to="group" x1="50" y1="38" x2="62" y2="84"/>
<line class="branch" data-to="chan" x1="50" y1="38" x2="86" y2="84"/>
</svg>
<div class="fnode round fmax" data-node="max">MAX<small>ваш аккаунт</small><span class="cnt"></span></div>
<div class="fnode fsrv" data-node="srv">Telemax<small>ваш сервер</small></div>
<div class="fnode round ftg" data-node="tg">Telegram<small>ваша группа</small><span class="cnt"></span></div>
<div class="fnode ftarget fgroup" data-node="group">👥<small>группа</small></div>
<div class="fnode ftarget fchan" data-node="chan">📢<small>канал</small></div>
<div class="fbadge store">💾 файл на вашем сервере <span class="bar"><i></i></span></div>
<div class="fbadge own b-acc">👤 ваш аккаунт</div>
<div class="fbadge own b-srv">🔑 ваш сервер</div>
<div class="fbadge own b-bot">🤖 ваш бот</div>
<div class="fbadge own b-free">🔓 открытый код · 0 ₽</div>
</div>
<div class="flow-cap" aria-live="polite"><div class="flow-title">Переписка в обе стороны</div><div class="flow-sub">Текст, голосовые, фото, кружки и стикеры идут из MAX в Telegram через ваш сервер, а ответы уходят обратно от вашего имени.</div></div>
<div class="flow-steps"></div>
</div>
</div>
<section id="why" class="alt">
<div class="wrap">
<div class="kicker">Зачем это</div>
<h2>Все переписки — в одном мессенджере</h2>
<p class="sub" style="max-width:760px">
Работа, школьный чат, родня — в MAX, а всё остальное — в Telegram. Telemax собирает их вместе:
сообщения из Макса приходят в Телеграм, а ваши ответы уходят обратно от вашего имени.
Не нужно переключаться между приложениями и держать открытыми два мессенджера.
</p>
</div>
</section>
<section id="diff">
<div class="wrap">
<div class="callout">
<div class="kicker">Главное отличие</div>
<h2>Личный аккаунт, а не бот</h2>
<p class="sub" style="margin:0">
Telemax входит вашим <b>личным аккаунтом MAX по номеру</b> — поэтому в Telegram
сами приходят <b>все</b> ваши чаты, диалоги и группы. Мостам-ботам это недоступно.
</p>
<div class="vs">
<div class="c good">
<div class="tag">Telemax — личный аккаунт</div>
Все чаты MAX приходят сами, ничего не нужно добавлять в каждый чат. Видите то же, что видели бы в приложении MAX.
</div>
<div class="c bad">
<div class="tag">Мосты-боты</div>
Работают только там, куда добавили бота. Личные диалоги и чужие группы — мимо. Везде подключать вручную.
</div>
</div>
</div>
</div>
</section>
<section id="features" class="alt">
<div class="wrap">
<div class="kicker">Возможности</div>
<h2>Полноценная переписка в обе стороны</h2>
<div class="grid" style="margin-top:26px">
<div class="feat"><div class="ic">💬</div><h3>Все типы сообщений</h3><p>Текст, фото, видео, файлы, голосовые, кружки, стикеры (и анимированные), опросы, геолокация и контакты — туда и обратно.</p></div>
<div class="feat"><div class="ic">🗂️</div><h3>Чат = тема</h3><p>Каждый чат MAX — отдельная тема в группе Telegram. В групповых чатах видно, кто написал, есть список участников и личка в одно нажатие.</p></div>
<div class="feat"><div class="ic">↩️</div><h3>Ответы, правки, реакции</h3><p>Ответы с переходом к оригиналу, правки с пометкой, удаление в обе стороны. Реакции тоже синхронизируются.</p></div>
<div class="feat"><div class="ic">📁</div><h3>Большие файлы</h3><p>Из MAX больше 50 МБ — кнопкой «⬇️ Скачать» с вашего сервера. В MAX — до 4 ГБ через страницу загрузки, в обход ограничений бота.</p></div>
<div class="feat"><div class="ic">📤</div><h3>Свой файлообменник</h3><p>Положите на свой сервер файл любого размера и делитесь ссылкой где угодно — или отправьте его в любой чат MAX командой <code>/file</code>, не загружая заново.</p></div>
<div class="feat"><div class="ic">🤖</div><h3>Кнопки ботов MAX</h3><p>Ссылки открываются, «Скопировать код» копирует, нажатия уходят боту от вашего имени — как в приложении.</p></div>
<div class="feat"><div class="ic">📡</div><h3>Трансляция</h3><p>Новые сообщения чата MAX — в любые группы и каналы Telegram, для тех, у кого нет MAX. Командой <code>/broadcast</code>, мест сколько угодно.</p></div>
<div class="feat"><div class="ic">📇</div><h3>Контакты MAX</h3><p>Найденного человека — в контакты MAX одной кнопкой, и в <code>/info</code> тоже. Контакт из Telegram сохранится под тем же именем.</p></div>
<div class="feat"><div class="ic">👥</div><h3>Группы MAX из Telegram</h3><p>Создать группу, пригласить или удалить участника, переименовать, выйти — командами прямо из темы.</p></div>
<div class="feat"><div class="ic">🎛️</div><h3>Пульт управления</h3><p>Команда <code>/panel</code>: поиск по номеру, MAX ID или нику, новый чат в одно нажатие, файлы, статус сервера, пауза и бан чатов.</p></div>
<div class="feat"><div class="ic">🔐</div><h3>Вход в MAX в личке бота</h3><p>Номер, код из SMS и пароль вводятся в личных сообщениях бота — в группу они не попадают.</p></div>
<div class="feat"><div class="ic">⬆️</div><h3>Обновления по кнопке</h3><p>Бот сам сообщает о новой версии. Релизы подписаны, а если обновление сорвётся — мост вернётся на прежнюю версию.</p></div>
<div class="feat"><div class="ic">🔁</div><h3>Ничего не теряется</h3><p>Пропущенное, пока мост был недоступен, догружается само. Сбои связи не сбрасывают вход в MAX.</p></div>
<div class="feat"><div class="ic">🌐</div><h3>Прокси для Telegram</h3><p>Если сервер выходит в Telegram только через прокси — подключите свой SOCKS5 или HTTP. MAX при этом идёт напрямую.</p></div>
<div class="feat"><div class="ic">🖥️</div><h3>Несколько мостов</h3><p>Второй номер MAX — второй мост на том же сервере, той же командой. Обновляются все сразу.</p></div>
</div>
</div>
</section>
<section id="privacy">
<div class="wrap">
<div class="kicker">Приватность</div>
<h2>Свой сервер и открытый код</h2>
<p class="sub" style="max-width:780px">
Telemax работает на <b>вашем</b> сервере с <b>вашим</b> Telegram-ботом: переписка идёт только между MAX,
вашим сервером и Telegram. Код открыт под лицензией AGPL-3.0 — можно прочитать каждую строчку.
Мост не держит открытых портов: 80 и 443 открываются, только пока живёт ссылка на файл.
Установщик предлагает поставить Fail2ban от подбора паролей SSH.
</p>
<p class="sub" style="max-width:780px;font-size:.94rem">
Разработчику уходят только анонимная статистика установок и отчёты об ошибках — без текста переписки,
названий чатов и номера телефона. Отключаются одной строкой <code>TELEMETRY=off</code>.
</p>
</div>
</section>
<section id="how" class="alt">
<div class="wrap">
<div class="kicker">Установка</div>
<h2>Три шага — и мост работает</h2>
<div class="steps" style="margin-top:30px">
<div class="step"><h3>Сервер</h3><p>VPS на Ubuntu 22.04+ или Debian 12+. Хватит и меньше 1 ГБ памяти и 3 ГБ свободного места.</p></div>
<div class="step"><h3>Бот и группа</h3><p>Бот в <code>@BotFather</code>, группа с включёнными темами, бот в ней — администратор с правом «Управление темами».</p></div>
<div class="step"><h3>Одна команда</h3><p>Запустите от root команду ниже, затем <code>/login</code> в личке бота — и чаты MAX начнут появляться в группе.</p></div>
</div>
<div class="cmd">
<pre>curl -fsSL https://raw.githubusercontent.com/Trollobot/Telemax/main/install.sh | bash</pre>
<div class="label">Если GitHub с сервера недоступен — та же установка с зеркала:</div>
<pre>curl -fsSL https://zergont-gate.duckdns.org/install.sh | bash</pre>
</div>
<p class="center" style="margin-top:32px">
<a class="btn btn-primary" href="max-v-telegram/">Пошаговая инструкция</a>
<a class="btn btn-ghost" href="https://github.com/Trollobot/Telemax#быстрый-старт">README на GitHub</a>
</p>
</div>
</section>
<section id="shots">
<div class="wrap">
<div class="kicker">Как это выглядит</div>
<h2>Скриншоты</h2>
<div class="shots" style="margin-top:22px">
<figure class="shot"><img src="shot-group.png" alt="Группа Telegram сразу после установки Telemax: бот подключился и закрепил пульт" width="1432" height="994" loading="lazy"><figcaption>Сразу после установки: бот подключился и закрепил пульт</figcaption></figure>
<figure class="shot"><img src="shot-files.png" alt="Раздел «Файлы» в пульте Telemax: файлы на сервере и кнопка «Загрузить файл»" width="876" height="858" loading="lazy"><figcaption>«📁 Файлы»: большие файлы и свой файлообменник</figcaption></figure>
<figure class="shot"><img src="shot-chat.webp" alt="Переписка MAX в теме Telegram: геолокация и видеокружок" width="1344" height="1564" loading="lazy"><figcaption>Геолокация ушла в MAX, в ответ пришёл кружок</figcaption></figure>
<figure class="shot"><img src="shot-bot.png" alt="Бот MAX с кнопками в теме Telegram" width="1412" height="1832" loading="lazy"><figcaption>Бот MAX отвечает в теме, его кнопки работают в Telegram</figcaption></figure>
<figure class="shot wide"><img src="shot-status.png" alt="Карточка «Статус» в пульте Telemax: связь с MAX и Telegram, диск и память сервера" width="964" height="472" loading="lazy"><figcaption>«Статус»: связь, последние сообщения, диск и память сервера</figcaption></figure>
</div>
</div>
</section>
<section id="limits" class="alt">
<div class="wrap">
<div class="kicker">Честно об ограничениях</div>
<h2>Что стоит знать заранее</h2>
<ul class="limits">
<li>Проект неофициальный и работает с вашим аккаунтом MAX на ваш страх и риск.</li>
<li>Соединения шифруются: MAX ↔ сервер по TLS, сервер ↔ Telegram по HTTPS. Но шифрование не сквозное: ваш сервер видит переписку — иначе он не смог бы её пересылать, — поэтому его стоит защищать.</li>
<li>Любой участник группы в Telegram видит все ваши чаты MAX — держите группу закрытой.</li>
<li>Аккаунт MAX нужен свой: зарегистрироваться и поставить пароль можно только в приложении MAX.</li>
<li>Для больших файлов нужен белый IP сервера и открытые порты 80 и 443. Если сервер дома за роутером без проброса портов — установщик предложит выключить большие файлы, всё остальное работает.</li>
<li>Звонки приходят только уведомлениями, мини-приложения ботов открываются в MAX.</li>
<li>Разработчик один: баги бывают, зато чинятся быстро.</li>
</ul>
</div>
</section>
<section id="faq">
<div class="wrap">
<div class="kicker">Вопросы</div>
<h2>Частые вопросы</h2>
<div style="margin-top:26px">
<details open><summary>Как читать и отвечать на сообщения MAX в Telegram?</summary><p>Поставьте Telemax на свой сервер и войдите в свой аккаунт MAX через бота. Все чаты MAX появятся в вашей группе Telegram отдельными темами: отвечаете в теме — ответ уходит в MAX от вашего имени.</p></details>
<details><summary>Нужно ли приложение MAX?</summary><p>Один раз — да: аккаунт MAX регистрируется в приложении, там же ставится пароль. Дальше переписка идёт через Telegram, и приложение можно не открывать.</p></details>
<details><summary>Это бот в MAX или мой аккаунт?</summary><p>Ваш личный аккаунт: Telemax входит по номеру телефона, поэтому приходят все ваши чаты, а не только те, куда добавлен бот. Со стороны Telegram работает ваш собственный бот.</p></details>
<details><summary>Можно ли начать новый чат из Telegram?</summary><p>Да: в пульте «Найти контакт» — по номеру в любом виде, по MAX ID или нику бота. Или отправьте в общую тему группы контакт из телефонной книги. Не найдётся только тот, кто закрыл поиск по номеру в настройках приватности MAX, — тогда пусть напишет первым.</p></details>
<details><summary>Можно ли показать чат MAX тем, у кого нет MAX?</summary><p>Да, трансляцией: команда <code>/broadcast</code> в теме чата — и новые сообщения этого чата копируются в обычную группу или канал Telegram. Мест сколько угодно, бота туда Telegram добавит сам. Трансляция идёт в одну сторону: что пишут там, в MAX не уходит.</p></details>
<details><summary>Передаются ли файлы, голосовые и кружки?</summary><p>Да, в обе стороны. Файлы больше 50 МБ из MAX приходят ссылкой на ваш сервер, а в MAX можно отправить файл до 4 ГБ через страницу загрузки. Для таких ссылок нужен белый IP сервера и открытые порты 80 и 443 — установщик проверит это сам и, если чего-то не хватает, объяснит.</p></details>
<details><summary>Сколько это стоит?</summary><p>Telemax бесплатный, весь функционал открыт. Платите только за свой сервер — хватит самого простого VPS.</p></details>
<details><summary>Заработает ли на российском VPS?</summary><p>Да. Если Telegram с сервера не открывается, мост может работать через прокси. Сам прокси нужен свой (SOCKS5 или HTTP) — его адрес указывают при установке, MAX при этом идёт напрямую.</p></details>
<details><summary>Можно ли подключить два аккаунта MAX?</summary><p>Да: второй мост ставится той же командой в соседний каталог, со своим ботом и своей группой. Автообновление обслуживает все мосты на сервере.</p></details>
<details><summary>Что будет, если GitHub недоступен?</summary><p>Установка и обновления сами переключатся на зеркало проекта. Каждый релиз подписан, и мост проверяет подпись перед установкой — неважно, откуда пришёл код.</p></details>
<details><summary>Это безопасно?</summary><p>Соединения шифруются: MAX ↔ сервер по TLS, сервер ↔ Telegram по HTTPS. Код и данные — на вашем сервере, посредников нет, код открыт. Но шифрование не сквозное: сервер видит переписку, иначе не смог бы её пересылать, — поэтому его стоит защищать. Проект неофициальный — используйте на свой страх и риск.</p></details>
</div>
</div>
</section>
<section id="contact" class="alt">
<div class="wrap">
<div class="support-box">
<div class="heart">💬</div>
<div class="kicker">Обратная связь</div>
<h2>Нашли баг, есть идея или вопрос?</h2>
<p>
Напишите боту <b>@TelemaxSvv_bot</b> в Telegram: сообщение придёт разработчику, а ответ вернётся вам
в тот же чат. Вопросы по установке тоже сюда. Если мост уже стоит, ссылка на бота есть и в <code>/help</code>.
</p>
<a class="btn btn-primary" href="https://t.me/TelemaxSvv_bot">✉️ Написать в Telegram</a>
<a class="btn btn-ghost" href="https://github.com/Trollobot/Telemax/issues">Issues на GitHub</a>
</div>
</div>
</section>
<section id="support">
<div class="wrap">
<div class="support-box">
<div class="heart">♥</div>
<div class="kicker">Поддержка</div>
<h2>Поддержать развитие проекта</h2>
<p>
Весь функционал бесплатный. Если мост пригодился — можно подогреть мотивацию разработчика:
здесь или командой <code>/donate</code> прямо в боте.
</p>
<a class="btn btn-primary" href="https://pay.cloudtips.ru/p/3c71b5e8">☕ Поддержать развитие проекта</a>
<div class="crypto">
или GRAM (TON): <code>UQALpK2PKI90-XpupOM8sRJGdwrFMsPcwQZPR0k180umXBcA</code>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="wrap">
<div class="links">
<a href="https://github.com/Trollobot/Telemax">GitHub</a>
<a href="news/">📰 Новости</a>
<a href="https://habr.com/ru/posts/1070812/">Первый пост на Хабре</a>
<a href="https://habr.com/ru/articles/1092754/">Статья на Хабре: бан на GitHub</a>
<a href="https://vc.ru/id973711/3179706-telemax-integratsiya-max-i-telegram">Статья на vc.ru</a>
<a href="https://t.me/TelemaxSvv_bot">Баг или идея — боту @TelemaxSvv_bot</a>
<a href="https://github.com/Trollobot/Telemax/issues">Issues</a>
<a href="https://telemax.svnest.ru/stats/">📊 Статистика посещений</a>
</div>
<div class="disc">
⚠️ Неофициальный проект с открытым кодом. Не связан с MAX и Telegram. Работает с вашим собственным
аккаунтом — на ваш страх и риск.
</div>
<p style="margin-top:16px">© Telemax — бесплатный мост MAX ↔ Telegram · AGPL-3.0</p>
</div>
</footer>
<script>
(function () {
var root = document.getElementById('flow');
if (!root || !root.animate) return;
var stage = root.querySelector('.flow-stage');
var reduce = window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches;
var token = 0, visible = true, timers = [];
var SCENES = [
{ id: 'chat', label: 'Переписка', title: 'Переписка в обе стороны',
sub: 'Текст, голосовые, фото, кружки и стикеры идут из MAX в Telegram через ваш сервер, а ответы уходят обратно от вашего имени.', dur: 7600,
play: function () {
at(0, function () { fly('💬 Привет! Ты где?', 'max', 'tg'); });
at(800, function () { fly('🎤 Голосовое · 0:14', 'max', 'tg'); });
at(1700, function () { fly('💬 Уже еду 🚗', 'tg', 'max'); });
at(2500, function () { fly('🖼️ Фото', 'max', 'tg'); });
at(3400, function () { fly('😄 Стикер', 'tg', 'max'); });
at(4200, function () { fly('⭕ Кружок · 0:09', 'max', 'tg'); });
} },
{ id: 'files', label: 'Файлы', title: 'Большие файлы — по ссылке',
sub: 'Видео на гигабайт ложится на ваш сервер, а в Telegram приходит кнопка «Скачать». В MAX уходят файлы до 4 ГБ.', dur: 8200,
play: function () {
var bar = stage.querySelector('.store .bar i');
bar.getAnimations().forEach(function (a) { a.cancel(); });
at(0, function () { fly('🎬 Видео · 1,2 ГБ', 'max', 'srv', { direct: true, dur: 1500, kind: 'big' }); });
at(1300, function () { bar.animate([{ width: '0%' }, { width: '100%' }], { duration: 1300, easing: 'ease-in-out', fill: 'forwards' }); });
at(2800, function () { fly('⬇️ Скачать · видео.mp4', 'srv', 'tg', { direct: true, dur: 1600, kind: 'btn' }); });
at(4300, function () { fly('📎 Отчёт.zip · 3 ГБ', 'tg', 'srv', { direct: true, dur: 1500, kind: 'big' }); });
at(5800, function () { fly('📎 Отчёт.zip', 'srv', 'max', { direct: true, dur: 1500 }); });
} },
{ id: 'cast', label: 'Трансляция', title: 'Трансляция',
sub: 'Новые сообщения чата копируются в любые группы и каналы Telegram — для тех, у кого нет MAX.', dur: 7800,
play: function () {
at(300, function () { cast('💬 Собрание в субботу'); });
at(1900, function () { cast('🖼️ Фото объявления'); });
at(3500, function () { cast('📊 Опрос: кто придёт?'); });
} },
{ id: 'own', label: 'Всё ваше', title: 'Всё ваше — и бесплатно',
sub: 'Ваш аккаунт, ваш сервер, ваш бот и открытый код. Никаких подписок — платите только за свой VPS.', dur: 7200,
play: function () {
at(900, function () { fly('💬 Спасибо!', 'max', 'tg'); });
at(2300, function () { fly('👍', 'tg', 'max'); });
at(3700, function () { fly('🎤 Голосовое · 0:05', 'max', 'tg'); });
} }
];
// --- helpers -----------------------------------------------------------------------------
function at(ms, fn) {
var t = token;
timers.push(setTimeout(function () { if (t === token) fn(); }, ms));
}
function node(name) { return stage.querySelector('[data-node="' + name + '"]'); }
function pt(name) {
var s = stage.getBoundingClientRect(), r = node(name).getBoundingClientRect();
return { x: r.left - s.left + r.width / 2, y: r.top - s.top + r.height / 2 };
}
function pulse(name) {
node(name).animate([{ transform: 'translate(-50%,-50%) scale(1)' }, { transform: 'translate(-50%,-50%) scale(1.09)' }, { transform: 'translate(-50%,-50%) scale(1)' }],
{ duration: 420, easing: 'ease-out' });
}
function bump(name) {
var c = node(name).querySelector('.cnt');
if (!c) return;
c.textContent = String((parseInt(c.textContent, 10) || 0) + 1);
c.classList.add('on');
c.animate([{ transform: 'scale(1.35)' }, { transform: 'scale(1)' }], { duration: 260, easing: 'ease-out' });
}
function resetCounters() {
stage.querySelectorAll('.cnt').forEach(function (c) { c.textContent = ''; c.classList.remove('on'); });
}
/** A message bubble from one node to another — through the server unless `direct`. Rightward rides above the line, leftward below. */
function fly(text, from, to, o) {
o = o || {};
var el = document.createElement('div');
var back = pt(to).x < pt(from).x;
el.className = 'fchip' + (back ? ' back' : '') + (o.kind ? ' ' + o.kind : '');
el.textContent = text;
stage.appendChild(el);
var W = stage.clientWidth, w = el.offsetWidth, h = el.offsetHeight;
var lift = o.dy != null ? o.dy : back ? 26 : -26;
function tf(p, sc) {
var x = Math.min(Math.max(p.x - w / 2, 4), W - w - 4);
return 'translate(' + x + 'px,' + (p.y - h / 2 + lift) + 'px) scale(' + sc + ')';
}
var A = pt(from), B = pt(to), dur = o.dur || 2700;
var frames;
if (o.direct) {
frames = [
{ transform: tf(A, .6), opacity: 0, offset: 0 },
{ transform: tf(A, 1), opacity: 1, offset: .14, easing: 'cubic-bezier(.4,0,.3,1)' },
{ transform: tf(B, 1), opacity: 1, offset: .86 },
{ transform: tf(B, .6), opacity: 0, offset: 1 }
];
} else {
var S = pt('srv');
frames = [
{ transform: tf(A, .6), opacity: 0, offset: 0 },
{ transform: tf(A, 1), opacity: 1, offset: .1, easing: 'cubic-bezier(.4,0,.3,1)' },
{ transform: tf(S, .8), opacity: 1, offset: .46 },
{ transform: tf(S, .8), opacity: 1, offset: .54, easing: 'cubic-bezier(.4,0,.3,1)' },
{ transform: tf(B, 1), opacity: 1, offset: .9 },
{ transform: tf(B, .6), opacity: 0, offset: 1 }
];
at(dur * .48, function () { pulse('srv'); });
}
var anim = el.animate(frames, { duration: dur, fill: 'forwards' });
at(dur * (o.direct ? .86 : .9), function () { pulse(to); bump(to); if (o.onArrive) o.onArrive(); });
anim.finished.then(function () { el.remove(); }, function () { el.remove(); });
}
/** A message of a broadcast chat: MAX → server → Telegram, and copies from the server to the group and the channel. */
function cast(text) {
fly(text, 'max', 'tg', { dur: 2700 });
at(1350, function () {
fly(text, 'srv', 'group', { direct: true, dur: 1500, kind: 'copy', dy: 0 });
fly(text, 'srv', 'chan', { direct: true, dur: 1600, kind: 'copy', dy: 0 });
});
}
/** Branch lines end where the targets actually are (their spot differs on narrow screens). */
function placeBranches() {
var s = stage.getBoundingClientRect();
if (!s.width) return;
stage.querySelectorAll('.branch').forEach(function (l) {
var p = pt(l.getAttribute('data-to'));
l.setAttribute('x2', (p.x / s.width * 100).toFixed(1));
l.setAttribute('y2', (p.y / s.height * 100).toFixed(1));
});
}
window.addEventListener('resize', placeBranches);
// --- scenes ------------------------------------------------------------------------------
var steps = root.querySelector('.flow-steps');
var cap = root.querySelector('.flow-cap');
SCENES.forEach(function (s, i) {
var b = document.createElement('button');
b.type = 'button';
b.textContent = s.label;
b.addEventListener('click', function () { show(i); });
steps.appendChild(b);
});
function clearStage() {
timers.forEach(clearTimeout);
timers = [];
stage.querySelectorAll('.fchip').forEach(function (c) { c.remove(); });
}
function show(i) {
token++;
clearStage();
resetCounters();
var s = SCENES[i];
root.dataset.scene = s.id;
steps.querySelectorAll('button').forEach(function (b, k) {
b.classList.toggle('on', k === i);
b.style.setProperty('--dur', s.dur + 'ms');
if (k === i) { b.classList.remove('on'); void b.offsetWidth; b.classList.add('on'); } // restart the progress line
});
cap.classList.add('fade');
setTimeout(function () {
cap.querySelector('.flow-title').textContent = s.title;
cap.querySelector('.flow-sub').textContent = s.sub;
cap.classList.remove('fade');
}, 180);
setTimeout(placeBranches, 500); // after the targets' slide-in
if (reduce) return; // a still picture per scene, no flying and no auto-advance
s.play();
at(s.dur, function () { next(i); });
}
function next(i) {
if (!visible || document.hidden) { setTimeout(function () { next(i); }, 600); return; }
show((i + 1) % SCENES.length);
}
if ('IntersectionObserver' in window) {
new IntersectionObserver(function (e) { visible = e[0].isIntersecting; }).observe(root);
}
placeBranches();
show(reduce ? 3 : 0);
})();
</script>
<script>
// Счётчик посещений без cookie: сообщает только, откуда пришли и на каком адресе открыт сайт.
// Сервер сразу раскладывает это по счётчикам и IP не хранит — см. /stats/.
(function () {
var h = location.hostname;
if (location.protocol !== 'https:' || h === 'localhost' || h === '127.0.0.1') return;
try {
var body = JSON.stringify({ r: document.referrer || '', h: h });
var url = 'https://telemax.svnest.ru/api/site/hit';
if (navigator.sendBeacon) navigator.sendBeacon(url, new Blob([body], { type: 'text/plain' }));
else fetch(url, { method: 'POST', body: body, keepalive: true, mode: 'no-cors' });
} catch (e) {}
})();
</script>
</body>
</html>