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;
});
}
}
Комментарии (6)
Pooper123
13.08.2023 04:45Шикарно. ИИ очень упрощает жизнь и обычным людям, и программистам. Учитывая скорости его развития, мне кажется, лет через десять большая часть интеллектуальных должностей будет связана либо с проверкой и доработкой того, что написал ИИ, либо с усовершенствованием самого ИИ.
tormozedison
13.08.2023 04:45Я как-то попробовал автоматически сгенерировать код для имитатора 6Е3П на SSD1306. Получилось нечто не имеющее ничего общего с тем, что требовалось. Потом забил на этот проект.
action52champion
Выглядит как будто статью писал ИИ.
Промтов, скриншотов диалога с ИИ нет
Зачем перегонять код из JS в PHP - непонятно, особенно учитывая что у вас в гитхабе лежат несколько проектов на Vue.js
исходного кода на JS, который был перегнан в PHP тоже нет, чтобы оценить качество трансляции.
andreybold Автор
Статью писал сам, думаю ИИ лучше бы написал ). Про скриншоты и диалоги — не думаю что здесь они уместны, т.к. это была бы портянка, а не статья.
Перегонять нужно было для того, чтобы потом этот скрипт запустить на сервере через крон.
Про исходный js не подумал, спасибо за уточнение.
Rishquer
Но js можно было бы запустить черезез node js, естественно убрав\заменив те части кода которые требуют браузерное api.
andreybold Автор
Не хотелось ещё и ноду разворачивать на серваке. PHP уже был там. К тому же, как я и писал в статье, лучше всего было бы конвертировать в питон, т.к. он есть практически на любом линуксе по умолчанию. Но в данном случае мне больше подошёл php (опыт и знания в нём были больше чем в js и питоне).