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

У моего модема на борту есть LUCI.
Возникла необходимость читать смски с этого модема не заходя в web-интерфейс (слишком много времени на это тратится). Но писать самому этот код не очень хотелось. Поэтому решил использовать ChatGPT как помощника.
Задача №1. Получить смски на модеме
Найти код в самом модеме отвечающий за чтение смсок. Тут я справился сам (т.к. уже копался в потрохах LUCI на этом модеме). Нашёл команду которая читает все смс: at -S modem at-sms-list all. Результат был в виде json данных. Дальше встал вопрос парсинга данных и перекодировки самих сообщений.
Задача №2. Разобрать данные
Из кода web-интерфейса я узнал что значат часть полей json. Но хотелось узнать что значат все поля. Задал этот вопрос чату. И он на удивление всё расписал как надо:
Hidden text
Данные в предоставленной JSON строке представляют собой информацию о SMS-сообщениях, включая метаданные и содержимое сообщений. Вот как расшифровать структуру данных:
"mem": Объект с информацией о памяти."type": Тип памяти ("ME" - внутренняя память телефона)."used": Использованная память в единицах."total": Общий объем памяти в единицах.
"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-проектов выглядет очень привлекательно.
Исходный код получившегося проекта можно глянуть тут.

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; }); } }
