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