docs обзвон: отчёт помощника — постройка, сторожа красными, вырезание, ошибки
Записано по ходу, пока полный прогон идёт. Внутри: почему станцию просим не ждать конца звонка, почему список знакомых отказов белый, шесть поломок с дословными словами покрасневших сторожей и слепком возврата, перепись зовущих глазами с четырьмя ловушками счётчика, и три моих ошибки первыми — включая ту, где мой же сторож остался бы зелёным при живой дыре. Раздел про полный прогон дописывается отдельно, когда прогон кончится.
This commit is contained in:
+234
-6
@@ -89,15 +89,177 @@
|
||||
|
||||
## 3. Что построил
|
||||
|
||||
*(заполняется по ходу)*
|
||||
### 3.1. Сама деталь — `NabornikAsterisk` в `bots/lena-golos/priyomnik.py`
|
||||
|
||||
Получив задание, она открывает короткое соединение к служебному входу станции на
|
||||
`127.0.0.1:5038`, входит по имени и тайному слову и просит начать исходящий
|
||||
звонок. Соединение тут же закрывается.
|
||||
|
||||
Просьба выглядит так — и каждая строка выбрана, а не взята наугад:
|
||||
|
||||
| строка | значение | зачем именно так |
|
||||
|---|---|---|
|
||||
| `Channel` | `Local/<номер>@lena-nabor/n` | набираем **через раздел диалплана**, который уже знает наш ствол и наш определитель номера. Своего номера в коде робота нет вовсе — и не появится |
|
||||
| `Context` / `Exten` | `lena-inworld` / `s` | снятую трубку станция отдаёт разговорному разделу, где `Answer` и сведение звука с мостом |
|
||||
| `Async` | `true` | **главное слово.** Станцию просим не ждать конца звонка — см. §1.1 |
|
||||
| `Timeout` | 60 000 | столько гудков ждёт сама станция |
|
||||
| `Variable` | `LENA_CALL_ID`, `LENA_ZNAK` | связь звонка с заданием, едет вместе со звонком |
|
||||
|
||||
### 3.2. Три исхода — рубеж проведён буквально
|
||||
|
||||
Рубеж один: **байты просьбы записаны в сокет**. До него любая беда — честное
|
||||
«звонка не было». После — «не было» говорим **только** по поимённо знакомому
|
||||
отказу станции (`OTKAZY_DO_NABORA`, белый список), а молчание, обрыв, незнакомая
|
||||
ошибка и ответ третьего рода дают «не знаю».
|
||||
|
||||
### 3.3. Форма номера проверяется до всякой сети
|
||||
|
||||
Только цифры, 3–20 знаков; знак задания — только буквы, цифры, `-` и `_`. Не
|
||||
подошло — «звонка не было», и до станции мы даже не стучимся. Зачем — §1.3.
|
||||
|
||||
### 3.4. Почему эта дорога, а не две другие
|
||||
|
||||
Замер коордиатора: служебный вход станции есть, но учётных записей в нём ноль;
|
||||
веб-вход выключен; папка заданий на звонок нам не по правам. Выбран **служебный
|
||||
вход**, и вот почему:
|
||||
|
||||
- **веб-вход** пришлось бы включать целиком — лишний открытый порт и лишний
|
||||
пароль на машине, где рядом крутится боевой «Поиск клиентов»;
|
||||
- **папка заданий** вовсе не умеет отвечать. Положил файл — и не знаешь, приняла
|
||||
ли станция задание, отказала или не заметила. Три исхода, на которых стоит вся
|
||||
защита от второго звонка, там взять неоткуда. Эта дорога не просто неудобна —
|
||||
она **лжёт**;
|
||||
- **служебный вход** уже включён, уже слушает только сам себя и на каждую просьбу
|
||||
отвечает словами. Единственная из трёх, где «принял», «не приму» и «молчу»
|
||||
различимы.
|
||||
|
||||
Что надо сделать на машине, чтобы дорога открылась, — списком для человека, не
|
||||
программиста, — написано в `bots/lena-golos/README.md`, раздел «Как открыть
|
||||
дорогу для набора». Коротко: завести станции **одну** учётную запись для робота с
|
||||
правом входить и начинать звонки; тайное слово положить отдельным файлом с
|
||||
правами 600; наружу вход **не** открывать; раскомментировать три строки в
|
||||
`lena-priyomnik.service`; и только потом — отдельное окно на живую пробу при
|
||||
владельце. С шага четвёртого робот вправду звонит живым людям и тратит деньги.
|
||||
🔴 Ничего из этого сегодня не сделано и делать нельзя.
|
||||
|
||||
### 3.5. Выбор детали остался вслух
|
||||
|
||||
`LENA_NABORNIK`: умолчание и `net` — прежний `NabornikNeNastroen`, `stanciya` —
|
||||
новый набор, любое другое имя роняет запуск с руганью, называющей оба годных
|
||||
значения. В эталоне службы `lena-priyomnik.service` строки набора написаны и
|
||||
**нарочно закомментированы**: приёмник поднимется с `net`, то есть звонить не
|
||||
начнёт, пока их не раскомментируют.
|
||||
|
||||
### 3.6. Связь звонка с заданием — в обе стороны
|
||||
|
||||
По звонку — задание (`Pamyat.zvonok`), по заданию — звонок
|
||||
(`Pamyat.zvonok_po_znaku`, заведена этой сменой). Та же пара уезжает со звонком
|
||||
на станцию переменными канала.
|
||||
|
||||
🔴 Попутно нашёл и починил тихую дыру: приёмник передавал набору **сырое тело**
|
||||
задания, а знак портал вправе прислать одним заголовком, не кладя в тело. Тогда
|
||||
набору было бы нечем привязать звонок к заданию, и он бы отказал по форме. Теперь
|
||||
приёмник кладёт разобранный знак явно. Отпечаток тела снимается **до** этого и на
|
||||
него не влияет — проверено отдельно.
|
||||
|
||||
### 3.7. Кто зовёт написанное мной — поимённо, глазами
|
||||
|
||||
🔴 **Что считает мой прибор.** Я искал по хранилищу поиском по тексту
|
||||
(`Grep`), а он считает **упоминания**, а не вызовы: в его выдачу попадают и
|
||||
пояснения, и таблицы README, и объявление самой вещи. Поэтому ниже разобрано
|
||||
глазами, строка за строкой.
|
||||
|
||||
| что написал | кто вправду зовёт | ловушка |
|
||||
|---|---|---|
|
||||
| `NabornikAsterisk` | `sozdat_nabornik()` — единственный в бою; проба дважды (через `sobrat_nabor` и напрямую) | упоминание в шапке файла — не зов |
|
||||
| `sozdat_nabornik()` | `main()` и `podnyat()` | 🔴 **зов умолчанием довода**: в `podnyat()` он стоит в `nabornik or sozdat_nabornik()`. Проба передаёт свой набор — и этот зов не случается вовсе. Счётчик «сколько раз позвали» показал бы здесь ноль на всех прогонах пробы |
|
||||
| `prochest_klyuch_ami()` | `NabornikAsterisk.__init__` | 🔴 **зов внутри условия**: только когда ключ не передан. Проба передаёт всегда → в пробе не зовётся **никогда**. Записано в §9 |
|
||||
| `Pamyat.zvonok_po_znaku()` | сегодня **только проба** | зовущего в бою нет; сказано вслух в README и §9 |
|
||||
| `_pochemu_nelzya`, `_bez_klyucha`, `_prosba`, `_razobrat`, `_otvet` | все — из `nabrat()`, каждый по разу-двум | `_razobrat` — статический, зовётся через `self` |
|
||||
| `OTKAZY_DO_NABORA`, `CIFRY`, `ZNAKI_METKI` | **постоянные**, а не вызовы: зовущего у них нет по природе. Читаются в `_pochemu_nelzya` и `nabrat` | счётчик вызовов дал бы ноль и был бы прав, и это ничего не значило бы |
|
||||
| настройка `LENA_NABORNIK` | читается ровно в одном месте — `sozdat_nabornik()` | 🔴 в эталоне службы её **нет вовсе** (строки закомментированы), то есть на машине она останется `net` |
|
||||
| переменные канала `LENA_CALL_ID`, `LENA_ZNAK` | 🔴 **зов внутри строки-подстановки** — их не «зовёт» ни одна функция, они уезжают текстом в станцию. Читать их будет мост, которого пока нет | никакой поиск по коду робота их потребителя не найдёт: он на другой стороне звонка |
|
||||
|
||||
### 3.8. Чего НЕ трогал
|
||||
|
||||
`bots/lena-golos/extensions-lena.conf` — узкое горло — **не тронут ни одной
|
||||
командой**. Оба нужных раздела (`[lena-nabor]`, `[lena-inworld]`) в нём уже были.
|
||||
|
||||
## 4. Сторожа и как показывал их красными
|
||||
|
||||
*(заполняется по ходу)*
|
||||
Сторожа — раздел 12 и 13 в `bots/lena-golos/proba-priyomnika.py`. Подставная
|
||||
станция говорит на языке служебного входа по настоящему сокету, но никуда не
|
||||
звонит, а **считает, сколько раз её попросили**. Настоящий код набора при этом
|
||||
работает без единой поблажки.
|
||||
|
||||
Пять поломок, и каждая — своим ножом.
|
||||
|
||||
| что ломал | что сказал сторож |
|
||||
|---|---|
|
||||
| молчание станции после просьбы → «звонка не было» вместо «не знаю» | `🔴 Е: станция замолчала после просьбы — исход «НЕ ЗНАЮ»: получено 'ne_nachat', а надо 'ne_znayu'` · `🔴🔴 Е: сказано «звонка не было» — портал повторит и позвонит ВТОРОЙ РАЗ` · и заодно `🔴 Ж` про обрыв. **Красных 3** |
|
||||
| выключил проверку «только цифры» в номере | `🔴 Л: КОРОТКАЯ отрава с переводом строки: получено 'nachat', а надо 'ne_nachat'` · `🔴🔴 Л: и на короткой отраве станцию не просили звонить: получено 1, а надо 0` · плюс шесть соседних. **Красных 9** |
|
||||
| выключил **обе** проверки номера — чтобы увидеть дыру целиком | `🔴🔴 Л: станцию НЕ ПРОСИЛИ звонить НИ РАЗУ: получено 2, а надо 0` — одно задание дало станции **две** просьбы о звонке. Дыра настоящая |
|
||||
| белый список отказов сделал чёрным: любая ошибка → «звонка не было» | `🔴 И: НЕЗНАКОМЫЙ отказ станции — «не знаю», а не «звонка не было»: получено 'ne_nachat', а надо 'ne_znayu'`. **Красных 1** |
|
||||
| закрытая дорога (вход не пустил) → «не знаю» вместо «звонка не было» — сторож, которого просил коордиатор | `🔴 Г: вход не пустил — исход «звонка ТОЧНО не было»: получено 'ne_znayu', а надо 'ne_nachat'`. **Красных 1** |
|
||||
| `Async: true` → `false` и выбросил `LENA_ZNAK` | `🔴 А: станцию просим НЕ ЖДАТЬ конца звонка: получено 'false', а надо 'true'` · `🔴 А: со звонком едет знак задания: получено None` · плюс два в разделе 13. **Красных 4** |
|
||||
|
||||
Возврат доказан слепком со снятием невидимых знаков конца строки:
|
||||
|
||||
```text
|
||||
в коммите: 4a8028034b6c89d03a39a2c650fc0deb726328a8
|
||||
на диске : 4a8028034b6c89d03a39a2c650fc0deb726328a8
|
||||
```
|
||||
|
||||
## 5. Вырезание второго рода
|
||||
|
||||
*(заполняется по ходу)*
|
||||
**Допущение, на котором стоит защита:** «станция ответила „принял" — значит
|
||||
звонок пошёл, и второй раз этому человеку звонить нельзя».
|
||||
|
||||
Пять годных по форме случаев мимо этого допущения.
|
||||
|
||||
1. **Станция сказала «принял», а звонок не пошёл — ствол отвалился.** Мы
|
||||
отвечаем «набор пошёл», и это **правильно**: сказать «не было» значит позвать
|
||||
портал звонить второй раз. Но плата названа вслух: состояние такого звонка
|
||||
так и останется «набираем», потому что станцию мы не слушаем. Это §9.
|
||||
2. **Станция не ответила вовсе, связь оборвалась на середине.** Сторожа Е и Ж,
|
||||
оба показаны красными. Ответ — «не знаю».
|
||||
3. **Два задания на один номер почти одновременно.** Проверено (случай В): это
|
||||
**два разных звонка**, и так и должно быть — защита считает по знаку задания,
|
||||
а не по номеру. Разные кампании вправе звонить одному человеку. 🔴 Обратная
|
||||
сторона названа вслух: набор **не** защищает от двойного звонка одному
|
||||
человеку по двум разным заданиям. Это работа портала
|
||||
(`ObzvonZapretObrabotki`), и она там есть.
|
||||
4. **Станция ответила третьим — не «принял» и не «отказ».** Куда попадёт:
|
||||
в «не знаю». Проверено (случай К) и показано красным через белый список.
|
||||
Это же покрывает и незнакомую ошибку (случай И).
|
||||
5. **Кто ещё может начать звонок на этой станции.** Сегодня — **никто**: все три
|
||||
дороги закрыты, и это ответ про сегодня.
|
||||
|
||||
Как только для нас откроют служебный вход, им сможет пользоваться не только
|
||||
приёмник. Тогда защиту от двойного звонка охраняет:
|
||||
|
||||
- **охраняет** — всё, что идёт через приёмник: память знаков на диске,
|
||||
неделимая бронь, отпечаток тела. Два приёмника на одном файле памяти тоже
|
||||
охраняются — этим занимается сама база;
|
||||
- **не охраняет** — звонок, начатый мимо приёмника: тем же служебным входом с
|
||||
теми же именем и словом; с пульта станции вручную (`channel originate` в
|
||||
разделы `testtrunk`, `lena-nabor`, `from-dasha`); файлом в папке заданий от
|
||||
имени станции. Ни один из этих путей память знаков не видит и увидеть не
|
||||
может;
|
||||
- **не охраняет отдельно** — два приёмника, поднятых с **разными** путями к
|
||||
файлу памяти: они не увидят друг друга и оба наберут. Путь задаётся
|
||||
настройкой `LENA_PAMYAT_ZADANIY`, и в эталоне службы он один.
|
||||
|
||||
⇒ Защита от двойного звонка охраняет **одну дверь из нескольких**, и охраняет
|
||||
её честно. Остальные двери закрываются не кодом, а тем, кто имеет доступ к
|
||||
машине.
|
||||
|
||||
🔴 И отдельный случай мимо допущения, которого в задании не было и который я
|
||||
нашёл сам: **годный по форме номер, внутри которого перевод строки**. Он мимо
|
||||
всего: приёмник телефонов не разбирает вовсе, портал чистит номер для своих
|
||||
нужд, а станция послушно исполнит вторую просьбу. Замерено: одно задание давало
|
||||
станции две просьбы о звонке. Защита проверена **на больном случае** — на самой
|
||||
отраве, а не на здоровом номере.
|
||||
|
||||
## 6. Полный прогон
|
||||
|
||||
@@ -105,12 +267,78 @@
|
||||
|
||||
## 7. Соседи
|
||||
|
||||
*(заполняется по ходу)*
|
||||
**моего имени нет ни в одном выводе ни одного сторожа**
|
||||
|
||||
**чужие файлы в дереве не тронуты**
|
||||
|
||||
Замерено `git status --short`. Изменёнными числятся мои четыре:
|
||||
`bots/lena-golos/priyomnik.py`, `bots/lena-golos/proba-priyomnika.py`,
|
||||
`bots/lena-golos/lena-priyomnik.service`, `bots/lena-golos/README.md` — и мой
|
||||
отчёт. Остальное изменённое в дереве (`docs/lending-sborka/…`, `package.json`,
|
||||
`package-lock.json`, `docs/observer/STATUS.md`) было изменено **до начала моей
|
||||
смены** и в мои коммиты не входило: каждый коммит делался поимённым перечислением
|
||||
путей и перечитывался через `git show --stat`.
|
||||
|
||||
`bots/mts-telegram-ads/src/runner.js` — чужое столкновение — так и числится
|
||||
`UU`: ни одной командой не тронут.
|
||||
|
||||
`bots/lena-golos/extensions-lena.conf` — узкое горло трёх задач — не тронут.
|
||||
Понадобилось бы, если бы разделов `[lena-nabor]` и `[lena-inworld]` в нём не
|
||||
было; они есть.
|
||||
|
||||
## 8. Мои ошибки
|
||||
|
||||
*(заполняется по ходу)*
|
||||
Первыми, как просили.
|
||||
|
||||
1. 🔴 **Мой сторож защиты от подмены был проверен не тем ножом.** Отрава с
|
||||
переводом строки, которую я написал первой, **длинная** — и её ловила
|
||||
проверка длины номера, а вовсе не проверка «только цифры». Выключи я проверку
|
||||
цифр — сторож остался бы **зелёным при живой дыре**. Поймал себя только тем,
|
||||
что уронил сторожа нарочно и посмотрел, какие именно строки покраснели: две
|
||||
главные не покраснели. Добавил **короткую** отраву, которая в длину номера
|
||||
помещается и ловится только проверкой цифр. Пояснение вписано в саму пробу,
|
||||
чтобы следующий не наступил.
|
||||
2. 🔴 **Датчик «набор не оставил своих потоков» считал чужие потоки.** Он брал
|
||||
все потоки процесса подряд, а среди них были потоки самой подставной станции,
|
||||
доживающие своё после закрытия связи. Датчик краснел **через раз**: три
|
||||
прогона из шести. Починил не подгонкой числа, а именем: потоки станции теперь
|
||||
подписаны, и датчик считает только чужие ей. Шестнадцать прогонов подряд
|
||||
зелёные.
|
||||
3. **Перехват журнала в двух потоках роняет чужие строки в вывод пробы.** Подмена
|
||||
общего вывода — вещь на весь процесс. Перенёс перехват **вокруг** обоих
|
||||
потоков.
|
||||
4. **Числа в README были уже неверны до меня, и я чуть не оставил их такими.**
|
||||
Там стояло «81 проверка», а проба давала 108. Исправил и вписал историю числа.
|
||||
|
||||
## 9. Что осталось незакрытым
|
||||
|
||||
*(заполняется по ходу)*
|
||||
1. 🔴 **Живого звонка не было, и моё доказательство его не заменяет.** Подставная
|
||||
станция отвечает так, как живая **обещает** отвечать, а не так, как она вправду
|
||||
отвечает. Не доказано: что просьба доедет до живой станции; что она поймёт наш
|
||||
`Local/<номер>@lena-nabor/n`; что звонок дойдёт до живого человека через ствол
|
||||
Манго; что снятая трубка вправду сведётся с мостом. Это отдельная проба, в
|
||||
отдельное окно и со словом владельца.
|
||||
2. 🔴 **Соответствие слов станции проверено чтением, а не станцией.** Белый
|
||||
список отказов и то, что при «не жди» ошибка приходит только до создания
|
||||
канала, — моё знание о том, как устроен служебный вход, а не замер. Ошибись я
|
||||
тут — знакомый отказ, случившийся **после** набора, дал бы человеку второй
|
||||
звонок. Смягчено тем, что список белый: незнакомое падает в «не знаю».
|
||||
Проверять это надо на живой станции, в том же окне, что и пункт 1.
|
||||
3. **Чем кончился звонок, набор не узнаёт.** Состояние звонка остаётся
|
||||
«набираем» навсегда. Портал, спросив `sostoyanie`, будет видеть «набираем» и
|
||||
после того, как разговор кончился. Слушать станцию — вторая половина З-0.6.
|
||||
4. **Дорога на машине закрыта, и открывать её я не стал** — это выкат и слово
|
||||
владельца. Перечень шагов — §3.4 и README.
|
||||
5. **`Pamyat.zvonok_po_znaku` сегодня зовёт только проба.** В бою её позовёт
|
||||
вторая половина З-0.6. Говорю вслух, чтобы это не выглядело работающей связью
|
||||
там, где её пока никто не использует.
|
||||
6. **`prochest_klyuch_ami` пробой не проверена**: проба всегда передаёт тайное
|
||||
слово прямо, и до чтения файла дело не доходит. Чтение файла — тот же приём,
|
||||
что у ключа двери, который проверен, но это довод по сходству, а не замер.
|
||||
7. **Один раз за смену проба упала в чужом, доставшемся мне разделе 9** (живой
|
||||
HTTP на сокете) с `ConnectionAbortedError` — это Windows и старый код, не мой.
|
||||
За следующие шестнадцать прогонов не повторилось, но я не могу доказать, что
|
||||
это не повторится.
|
||||
8. **Тайное слово служебного входа поедет на машину человеком.** Файла с ним в
|
||||
хранилище нет и быть не должно; правило прав 600 записано в README и в
|
||||
эталоне службы, но исполняет его человек, а не сторож.
|
||||
|
||||
Reference in New Issue
Block a user