Как я смски получал с помощью ИИ

от автора

Disclaimer: эта история не про код, а про использование ИИ для упрощения и ускорения написания мелких pet-проектов (и не только).

Я живу в деревне и как практически в любой деревне на постсоветском пространстве у нас интернет идёт через модем с сим-картой.

900x1200.webp

В моём случае это NR-400

У моего модема на борту есть LUCI.

Возникла необходимость читать смски с этого модема не заходя в web-интерфейс (слишком много времени на это тратится). Но писать самому этот код не очень хотелось. Поэтому решил использовать ChatGPT как помощника.

Задача №1. Получить смски на модеме

Найти код в самом модеме отвечающий за чтение смсок. Тут я справился сам (т.к. уже копался в потрохах LUCI на этом модеме). Нашёл команду которая читает все смс: at -S modem at-sms-list all. Результат был в виде json данных. Дальше встал вопрос парсинга данных и перекодировки самих сообщений.

Задача №2. Разобрать данные

Из кода web-интерфейса я узнал что значат часть полей json. Но хотелось узнать что значат все поля. Задал этот вопрос чату. И он на удивление всё расписал как надо:

Hidden text

Данные в предоставленной JSON строке представляют собой информацию о SMS-сообщениях, включая метаданные и содержимое сообщений. Вот как расшифровать структуру данных:

  1. "mem": Объект с информацией о памяти.

    • "type": Тип памяти («ME» — внутренняя память телефона).

    • "used": Использованная память в единицах.

    • "total": Общий объем памяти в единицах.

  2. "sms": Список объектов SMS-сообщений.

    • "idx": Индекс сообщения.

    • "stat": Статус сообщения («read» — прочитано).

    • "fail": Признак ошибки отправки («False» — не было ошибки).

    • "smsc": Сервисный центр SMS.

    • "sender": Отправитель сообщения.

    • "ts": Временная метка сообщения.

    • "date": Дата сообщения.

    • "time": Время сообщения.

    • "zone": Часовой пояс сообщения.

    • "c_ref": Счетчик ссылок (если есть).

    • "c_cur": Текущий номер сообщения.

    • "c_tot": Общее количество частей сообщения.

    • "dcs": Кодировка данных.

    • "data": Содержимое SMS в виде строки.

Каждый объект в списке "sms" представляет одно SMS-сообщение. Важные поля:

  • "sender": Имя отправителя.

  • "ts", "date", "time", "zone": Дата и время отправки сообщения с учетом часового пояса.

  • "data": Содержимое сообщения (текст).

Кодировка "dcs" указывает на способ кодирования данных в поле "data". Значение 2 указывает на кодировку UCS-2 (16-бит Unicode), которая обычно используется для текстовых сообщений на кириллице.

Обратите внимание, что часть информации может быть неполной или сокращенной, так как эти данные представляют собой сжатую версию длинных SMS-сообщений.

Если у вас есть какие-либо дополнительные вопросы или вы хотите более подробно разобраться с каким-либо аспектом данных, пожалуйста, уточните.

Задача №3. Написать рабочий код

Пришло время писать код. Но писать с нуля не очень хотелось. Поэтому был взят код из web-интерфейса модема, написанного на JS. Попросил чат конвертировать этот код из JS в PHP (лучше наверно в python, но опыта маловато). Он сделал это, но предупредил что часть кода пропала, т.к. в PHP не получится работать с DOM деревом страницы?.

Дальше я попросил его переписать код для работы с объектами, а не массивами (в исходном JS коде все json данные брались через синтаксис массива).

Следующим этапом было упростить одну из функций, для увеличения удобочитаемости (кому интересно функция smsConcatenate). С чем он справился вполне не плохо.

Дальше я поигрался с выводом таблицы в html. Но это так, ради интереса. Интересное в этом то, что весь этот код тоже дописывался ИИ.

Hidden text
<!DOCTYPE html> <html> <head>     <title>SMS Data</title>     <style>         body {             font-family: Arial, sans-serif;         }         table {             border-collapse: collapse;             width: 100%;         }         th, td {             border: 1px solid #ddd;             padding: 8px;             text-align: left;         }         th {             background-color: #f2f2f2;         }         tr:nth-child(even) {             background-color: #f2f2f2;         }     </style> </head> <body>  <?php  function decodeUnicode($sub) {     // ... }  function smsTranslate($sms) {     // ... }  function smsCompare($a, $b) {     // ... }  function smsConcatenate($messages) {     // ... }  // Загрузка данных из файла test.json $jsonData = file_get_contents('test.json'); $smsData = json_decode($jsonData);  // Обработка и сортировка SMS $processedSms = smsConcatenate($smsData);  // Вывод таблицы с данными echo '<table>         <tr>             <th>idx</th>             <th>stat</th>             <th>fail</th>             <th>smsc</th>             <th>sender</th>             <th>ts</th>             <th>date</th>             <th>time</th>             <th>zone</th>             <th>c_ref</th>             <th>c_cur</th>             <th>c_tot</th>             <th>dcs</th>             <th>data</th>         </tr>';  foreach ($processedSms as $sms) {     echo '<tr>';     echo '<td>' . $sms->idx . '</td>';     echo '<td>' . $sms->stat . '</td>';     echo '<td>' . $sms->fail . '</td>';     echo '<td>' . $sms->smsc . '</td>';     echo '<td>' . $sms->sender . '</td>';     echo '<td>' . $sms->ts . '</td>';     echo '<td>' . $sms->date . '</td>';     echo '<td>' . $sms->time . '</td>';     echo '<td>' . $sms->zone . '</td>';     echo '<td>' . $sms->c_ref . '</td>';     echo '<td>' . $sms->c_cur . '</td>';     echo '<td>' . $sms->c_tot . '</td>';     echo '<td>' . $sms->dcs . '</td>';     echo '<td>' . $sms->data . '</td>';     echo '</tr>'; }  echo '</table>'; ?>  </body> </html>

Пришло время сделать то, ради чего всё начиналось.

Я удалил всё что связанно с html и сделал отправку в телеграмм через Apprise.

$client = new Client(env('APPRISE_URL'), $base_auth); $content = $newSms->data . PHP_EOL . '==========' . PHP_EOL . '<strong>Номер</strong>: <code>' . $newSms->smsc . '</code>' . PHP_EOL. '<strong>Дата</strong>: ' . $newSms->date . ' ' . $newSms->time; content, $newSms->sender)) ->setFormat(Format::HTML) ->telegram( $_ENV['TELEGRAM_TOKEN'], $_ENV['TELEGRAM_CHAT_ID'] ) ->send(); 

Попросил чат разбить весь код на отдельные файлы в соответствии с ООП. Пришлось немного поправить вручную, т.к. разделение по файлам прошло не совсем точно.

Ещё была задача придумать название пакета? с чем он в целом тоже справился.

Теперь нужно было заменить работу с файлом json на выполнение команды на удалённом компе (модеме) по ssh. Тут всё было предсказуемо. Пришлось немного побороться с ssh подключением, но в целом ничего сложного.

И на этом всё. Всё работает, смски приходят. Правда чуть позже я добавил ещё отправку определённых смсок (от МЧС) на MQTT-брокер (для Home Assistant).

Итого

Весь код можно разбить на следующие части:

  • 30% кода это исходный вариант на js;

  • 60% кода написано ChatGPT;

  • 10% кода написано мной.

Так же важно упомянуть что всё это заняло около 2-3 часов. При учёте что я, по большей части, занимался проверкой кода, на не его написанием.

И как мне кажется использование ChatGPT для таких мелких pet-проектов выглядет очень привлекательно.

Исходный код получившегося проекта можно глянуть тут.

Снимок экрана от 2023-08-11 14-54-35.png

Пример «смс» в телеграмме

P.S. Это, фактически, третий раз когда я использовал ИИ для написания каких-то мелких личных задач. Первые две, написанные на питоне с нуля, это: получение данных с модема о текущем операторе (модем поддерживает две симки и умеет автоматически переключаться между ними) и отправка инфы в HA; получение с модема статистики по трафику и также отправка этих данных в HA (отправляются download, upload и сумма).

UPD

Исходный код js, который нужно было сконвертировать в PHP
function smsTranslate(sms) { var text; if (sms.dcs == 2) { text = decodeUnicode(sms.data) sms.data = text } return sms; }  function smsCompare(a, b) { if (a.ts < b.ts) return 1; if (a.ts > b.ts) return -1;  return 0; }  function smsConcatenate(sms) { var obj = []; for (let i = 0; i < sms.length; i++) { if (sms[i]) { if (sms[i].c_ref && !sms[i].fail) { let total = sms[i].c_tot; let cur = 1; let tmp = sms[i]; let fnd = true; while (fnd && total-- > 0) { fnd = false; for (let j = i; j < sms.length; j++) { if (sms[j] && (tmp.c_ref == sms[j].c_ref) && (sms[j].c_cur == cur)) { if (cur == 1) tmp = sms[j]; else tmp.data = tmp.data + sms[j].data; cur++; sms[j] = null; fnd = true; break; } } } if (cur == (tmp.c_tot + 1)) obj.push(smsTranslate(tmp)); } else { obj.push(smsTranslate(sms[i])); } } } obj.sort(smsCompare); return obj; }  function smsRead(btn) { var ac = document.getElementById('table_sms'); var mem = document.getElementById('memory');  if (!ac || !mem) return;  while(ac.rows.length > 1 ) ac.rows[0].parentNode.deleteRow(1);  var tr = ac.rows[0].parentNode.insertRow(-1); tr.className = 'cbi-section-table-row cbi-rowstyle-2'; var td = tr.insertCell(-1); td.colSpan = 4; td.innerHTML = '<em>Чтение...</em>'; mem.innerHTML = ''; if (btn) btn.disabled = true; stxhr.get('/cgi-bin/luci/admin/services/modem_read_sms/all?block=0&', null, function(x, info) { if (info && info.code == 0 && info.out && info.out.mem && info.out.sms) { mem.innerHTML = String.format('Память: %d/%d %s', info.out.mem.used, info.out.mem.total, info.out.mem.type); td.innerHTML = '<em>OK</em>'; var sms = smsConcatenate(info.out.sms); if (sms.length == 0) { td.innerHTML = '<em>Нет сообщений</em>'; } else { while (ac.rows.length > 1) ac.rows[0].parentNode.deleteRow(1);  for (let i = 0; i < sms.length; i++) { let tr = ac.rows[0].parentNode.insertRow(-1); tr.className = (i % 2 == 0) ? 'cbi-section-table-row cbi-rowstyle-2' : tr.className = 'cbi-section-table-row cbi-rowstyle-1'; if (sms[i].fail) { tr.insertCell(-1).innerHTML = '<br /><br /><br /br>'; tr.insertCell(-1).innerHTML = ''; tr.insertCell(-1).innerHTML = 'Error decode SMS'; } else { let date = '<em>' + sms[i].time + '<br />' + sms[i].date + '<br />' + sms[i].zone + '</em>'; let sender = sms[i].sender; let text = sms[i].data; date = '<small style="font-weight: inherit;">' + date + '</small>'; sender = '<small style="font-weight: inherit;color: blue;">' + sender + '</small>'; text = '<small style="font-weight: inherit;">' + text + '</small>'; /* unread sms date = '<small style="font-weight: bold;">' + date + '</small>'; sender = '<small style="font-weight: bold;color: blue;">' + sender + '</small>'; text = '<small style="font-weight: bold;">' + text + '</small>'; */ tr.insertCell(-1).innerHTML = date; tr.insertCell(-1).innerHTML = sender; tr.insertCell(-1).innerHTML = text; } tr.cells[2].align = "left"; } } } else { td.innerHTML = '<em>Не удалось прочитать сообщения</em>'; } if (btn) btn.disabled = false; }); }  function smsDelete(btn) { var ac = document.getElementById('table_sms'); var mem = document.getElementById('memory');  if (ac && mem) { while( ac.rows.length > 1 ) ac.rows[0].parentNode.deleteRow(1);  var tr = ac.rows[0].parentNode.insertRow(-1); tr.className = 'cbi-section-table-row cbi-rowstyle-2'; var td = tr.insertCell(-1); td.colSpan = 4; td.innerHTML = '<em>Удаление SMS...</em>'; mem.innerHTML = ''; btn.disabled = true;  stxhr.get('/cgi-bin/luci/admin/services/modem_del_sms/all', null, function(x, info) { if ((info) && (info.code == 0)) { td.innerHTML = '<em>SMS сообщения удалены</em>'; smsRead(null); } else td.innerHTML = '<em>Ошибка удаления SMS</em>';  btn.disabled = false; }); } }


ссылка на оригинал статьи https://habr.com/ru/articles/754172/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *