Merge pull request #59 from dr-dolomite/development-socat

Changes for Beta 1.0
This commit is contained in:
Cameron Thompson
2024-05-31 18:24:02 -04:00
committed by GitHub
8 changed files with 1310 additions and 1118 deletions

View File

@@ -1,31 +0,0 @@
#!/bin/sh
# Check if the required parameters are provided
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <phone_number> <message>"
exit 1
fi
# Assign the provided parameters to variables
phone_number="$1"
message="$2"
# Send the AT command to set the message format to text mode
echo -ne "AT+CMGF=1\r" > microcom -s /dev/ttyOUT2
sleep 1
echo -ne "AT+CNMI=2,1\r" > microcom /dev/ttyOUT2
sleep 1
echo -ne 'AT+CMGS="09938931024"\r' > microcom /dev/ttyOUT2
sleep 1
# Send the message
echo -ne "$message" > microcom /dev/ttyOUT2
echo -ne "\032" > microcom /dev/ttyOUT2
# Wait for the response
sleep 1
# Capture and output the response
runcmd=$(microcom /dev/ttyOUT2)
# echo "Content-type: text/plain"
echo "$runcmd"

View File

@@ -1,12 +0,0 @@
#!/bin/bash
SMS_FORMAT="AT+CMGF=1"
SMS_LIST="AT+CMGL=\"ALL\""
send_at_command() {
local command="$1"
echo -en "$command\r\n" | microcom -t 2000 /dev/ttyOUT2
}
send_at_command "$SMS_FORMAT"
runcmd=$(send_at_command "$SMS_LIST")
echo "Content-type: text/plain"
echo "$runcmd"

View File

@@ -20,35 +20,12 @@ fi
phone_number="$number" phone_number="$number"
message_encoded="$msg" message_encoded="$msg"
phone_number="+86$phone_number"
send_at_command() { send_at_command() {
local cmd=$1 local cmd=$1
echo "Sending command: $cmd" >&2 echo -en "$cmd\r" | microcom -t 100 /dev/ttyOUT2
echo -en "$cmd\r" | microcom -t 1000 /dev/ttyOUT2
sleep 2
local response=$(microcom -t 1000 /dev/ttyOUT2)
echo "Response: $response" >&2
echo "$response"
} }
send_at_command "AT+CMGF=1" send_at_command "AT+CMGS=\"$phone_number\","$Command""
send_at_command "AT+CSCS=\"UCS2\"" runcmd=$((echo -en "$message_encoded"; echo -en "\x1A") | microcom -t 500 /dev/ttyOUT2)
encode_ucs2() {
local input="$1"
local output=""
local i
for ((i=0; i<${#input}; i++)); do
hex=$(printf "%04X" "'${input:$i:1}")
output="$output$hex"
done
echo "$output"
}
phone_number_ucs2=$(encode_ucs2 "$phone_number")
ATCMD="AT+CMGS=\"$phone_number_ucs2\""
send_at_command "$ATCMD"
runcmd=$((echo -en "$message_encoded"; sleep 1; echo -en "\x1A") | microcom -t 1000 /dev/ttyOUT2)
echo "$runcmd" echo "$runcmd"

View File

@@ -54,6 +54,7 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/console">Console</a> <a class="nav-link" href="/console">Console</a>
</li> </li>
<li class="nav-item">
<a <a
class="nav-link active" class="nav-link active"
href="/deviceinfo.html" href="/deviceinfo.html"
@@ -145,17 +146,40 @@
<th>Project Contributors</th> <th>Project Contributors</th>
<td> <td>
<!-- Button trigger modal --> <!-- Button trigger modal -->
<p type="button" class="link-info link-opacity-50-hover link-offset-2" data-bs-toggle="modal" data-bs-target="#staticBackdrop"> <p
type="button"
class="link-info link-opacity-50-hover link-offset-2"
data-bs-toggle="modal"
data-bs-target="#staticBackdrop"
>
Show Contributors Show Contributors
</p> </p>
<!-- Modal --> <!-- Modal -->
<div class="modal fade" id="staticBackdrop" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true"> <div
class="modal fade"
id="staticBackdrop"
data-bs-backdrop="static"
data-bs-keyboard="false"
tabindex="-1"
aria-labelledby="staticBackdropLabel"
aria-hidden="true"
>
<div class="modal-dialog modal-dialog-centered"> <div class="modal-dialog modal-dialog-centered">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h1 class="modal-title fs-5" id="staticBackdropLabel">Contributors</h1> <h1
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> class="modal-title fs-5"
id="staticBackdropLabel"
>
Contributors
</h1>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<table> <table>
@@ -165,68 +189,90 @@
RGMII Toolkit and Documentation RGMII Toolkit and Documentation
</th> </th>
<td class="col-md-2"> <td class="col-md-2">
<a href="https://github.com/iamromulan" target="_blank">iamromulan</a> <a
href="https://github.com/iamromulan"
target="_blank"
>iamromulan</a
>
</td> </td>
</tr> </tr>
<tr> <tr>
<th> <th>Simple Admin 2.0</th>
Simple Admin 2.0
</th>
<td class="col-md-2"> <td class="col-md-2">
<a href="https://github.com/dr-dolomite" target="_blank">dr-dolomite</a> <a
href="https://github.com/dr-dolomite"
target="_blank"
>dr-dolomite</a
>
</td> </td>
</tr> </tr>
<tr> <tr>
<th> <th>SMS Feature</th>
SMS Feature
</th>
<td class="col-md-2"> <td class="col-md-2">
<a href="https://github.com/snjzb" target="_blank">snjzb</a> <a
href="https://github.com/snjzb"
target="_blank"
>snjzb</a
>
</td> </td>
</tr> </tr>
<tr> <tr>
<th> <th>Original Simple Admin</th>
Original Simple Admin
</th>
<td class="col-md-2"> <td class="col-md-2">
<a href="https://github.com/aesthernr" target="_blank">aesthernr</a> <a
href="https://github.com/aesthernr"
target="_blank"
>aesthernr</a
>
</td> </td>
</tr> </tr>
<tr> <tr>
<th> <th>Original Socat Bridge</th>
Original Socat Bridge
</th>
<td class="col-md-2"> <td class="col-md-2">
<a href="https://github.com/natecarlson" target="_blank">natecarlson</a> <a
href="https://github.com/natecarlson"
target="_blank"
>natecarlson</a
>
</td> </td>
</tr> </tr>
<tr> <tr>
<th> <th>Original Simple Admin Fixes</th>
Original Simple Admin Fixes
</th>
<td class="col-md-2"> <td class="col-md-2">
<a href="https://github.com/rbflurry/" target="_blank">rbflurry</a> <a
href="https://github.com/rbflurry/"
target="_blank"
>rbflurry</a
>
</td> </td>
</tr> </tr>
<tr> <tr>
<th> <th>SA Parsing Patch</th>
SA Parsing Patch
</th>
<td class="col-md-2"> <td class="col-md-2">
<a href="https://github.com/tarunVreddy" target="_blank">tarunVreddy</a> <a
href="https://github.com/tarunVreddy"
target="_blank"
>tarunVreddy</a
>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> <button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
Close
</button>
</div> </div>
</div> </div>
</div> </div>
@@ -388,19 +434,29 @@
this.manufacturer = lines[1].trim(); this.manufacturer = lines[1].trim();
this.modelName = lines[3].trim(); this.modelName = lines[3].trim();
this.firmwareVersion = lines[5].trim(); this.firmwareVersion = lines[5].trim();
this.imsi = lines[7].trim();
this.iccid = lines[9].trim().replace("+ICCID: ", "");
this.imei = lines[11].trim(); this.imei = lines[11].trim();
this.lanIp = lines[13].trim().split(",")[3]; this.lanIp = lines[13].trim().split(",")[3];
this.wwanIpv4 = lines[15].trim().split(",")[4].replace(/"/g, ""); this.wwanIpv4 = lines[15].trim().split(",")[4].replace(/"/g, "");
this.wwanIpv6 = lines[16].trim().split(",")[4].replace(/"/g, ""); this.wwanIpv6 = lines[16].trim().split(",")[4].replace(/"/g, "");
this.phoneNumber = lines[18].trim().split(",")[1].replace(/"/g, "");
try {
this.imsi = lines[7].trim();
this.iccid = lines[9].trim().replace("+ICCID: ", "");
this.phoneNumber = lines[18]
.trim()
.split(",")[1]
.replace(/"/g, "");
if (this.phoneNumber === "") { if (this.phoneNumber === "") {
this.phoneNumber = "Unknown"; this.phoneNumber = "Unknown";
} }
} catch (error) {
this.phoneNumber = "No SIM Card Inserted or Detected";
this.imsi = " ";
this.iccid = " ";
}
this.simpleAdminVersion = "SimpleAdminRev-Alpha-0.6"; this.simpleAdminVersion = "SimpleAdminRev-Beta-1.0";
this.isLoading = false; this.isLoading = false;
}, },

File diff suppressed because it is too large Load Diff

View File

@@ -604,24 +604,27 @@
atcmd = `AT+QNWPREFCFG="lte_band",${newCheckedValues.join( atcmd = `AT+QNWPREFCFG="lte_band",${newCheckedValues.join(
":" ":"
)}`; )}`;
console.log(atcmd);
this.sendATcommand(atcmd); this.sendATcommand(atcmd);
} else if (selectedMode === "NSA") { } else if (selectedMode === "NSA") {
atcmd = `AT+QNWPREFCFG="nsa_nr5g_band",${newCheckedValues.join( atcmd = `AT+QNWPREFCFG="nsa_nr5g_band",${newCheckedValues.join(
":" ":"
)}`; )}`;
console.log(atcmd);
this.sendATcommand(atcmd); this.sendATcommand(atcmd);
} else if (selectedMode === "SA") { } else if (selectedMode === "SA") {
atcmd = `AT+QNWPREFCFG="nr5g_band",${newCheckedValues.join( atcmd = `AT+QNWPREFCFG="nr5g_band",${newCheckedValues.join(
":" ":"
)}`; )}`;
console.log(atcmd);
this.sendATcommand(atcmd); this.sendATcommand(atcmd);
} else { } else {
alert("Invalid network mode selected"); alert("Invalid network mode selected");
} }
// Do a 5 second countdown // Do a 2 second countdown
this.showModal = true; this.showModal = true;
this.countdown = 5; this.countdown = 2;
const interval = setInterval(() => { const interval = setInterval(() => {
this.countdown--; this.countdown--;
if (this.countdown === 0) { if (this.countdown === 0) {
@@ -814,7 +817,7 @@
}, },
cellLockDisableLTE() { cellLockDisableLTE() {
// Send the atcmd command to reset the locked bands // Send the atcmd command to reset the locked bands
const atcmd = 'AT+QNWLOCK="common/4g,0"'; const atcmd = 'AT+QNWLOCK="common/4g",0';
this.showModal = true; this.showModal = true;
this.sendATcommand(atcmd); this.sendATcommand(atcmd);

View File

@@ -326,7 +326,7 @@
Go to Cell Scanner Go to Cell Scanner
</a> --> </a> -->
<p><a class="link-info link-opacity-50-hover link-offset-2" href="/scanner.html">Go to Cell Scanner</a></p> <p><a class="link-info link-opacity-50-hover link-offset-2" href="/scanner.html">Go to Cell Scanner</a></p>
<p><a class="link-info link-opacity-50-hover link-offset-2" href="/watchcat.html">Go to WatchCat</a></p> <!-- <p><a class="link-info link-opacity-50-hover link-offset-2" href="/watchcat.html">Go to WatchCat</a></p> -->
<!-- </a> --> <!-- </a> -->
</div> </div>
</div> </div>

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="zh-CN" data-bs-theme="light"> <html lang="en" data-bs-theme="dark">
<head>
<head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Simple Admin</title> <title>Simple Admin</title>
@@ -14,25 +15,16 @@
<script src="js/alpinejs.min.js" defer></script> <script src="js/alpinejs.min.js" defer></script>
<!-- Contributed By: snjzb --> <!-- Contributed By: snjzb -->
</head> </head>
<body> <body>
<main> <main>
<div class="container my-4" x-data="fetchSMS()"> <div class="container my-4" x-data="fetchSMS()">
<nav class="navbar navbar-expand-lg mt-2"> <nav class="navbar navbar-expand-lg mt-2">
<div class="container-fluid"> <div class="container-fluid">
<a class="navbar-brand" href="/" <a class="navbar-brand" href="/"><span class="mb-0 h4">Simple Admin</span></a>
><span class="mb-0 h4">Simple Admin</span></a <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText"
> aria-controls="navbarText" aria-expanded="false" aria-label="切换导航">
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarText"
aria-controls="navbarText"
aria-expanded="false"
aria-label="切换导航"
>
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarText"> <div class="collapse navbar-collapse" id="navbarText">
@@ -47,20 +39,13 @@
<a class="nav-link" href="/settings.html">Simple Settings</a> <a class="nav-link" href="/settings.html">Simple Settings</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a <a class="nav-link active" aria-current="page" href="/sms.html">SMS</a>
class="nav-link active"
aria-current="page"
href="/sms.html"
>SMS</a
>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/console">Console</a> <a class="nav-link" href="/console">Console</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/deviceinfo.html" <a class="nav-link" href="/deviceinfo.html">Device Information</a>
>Device Information</a
>
</li> </li>
</ul> </ul>
<span class="navbar-text"> <span class="navbar-text">
@@ -78,20 +63,15 @@
<div class="card-body"> <div class="card-body">
<div class="card-text"> <div class="card-text">
<div class="col"> <div class="col">
<div <div style="
style="
max-height: 400px; max-height: 400px;
overflow-y: scroll; overflow-y: scroll;
overflow-x: hidden; overflow-x: hidden;
" ">
>
<div x-show="isLoading"> <div x-show="isLoading">
<h4>Fetching SMS...</h4> <h4>Fetching SMS...</h4>
</div> </div>
<table <table class="table table-hover border-success" x-show="!isLoading">
class="table table-hover border-success"
x-show="!isLoading"
>
<tbody> <tbody>
<!-- 没有消息时显示 --> <!-- 没有消息时显示 -->
<!-- Display when there are no messages --> <!-- Display when there are no messages -->
@@ -102,33 +82,22 @@
</template> </template>
<!-- 循环显示短信消息 --> <!-- 循环显示短信消息 -->
<!-- "Loop display SMS messages" --> <!-- "Loop display SMS messages" -->
<template <template x-for="(message, index) in messages" :key="index">
x-for="(message, index) in messages"
:key="index"
>
<tr> <tr>
<td> <td>
<div class="form-check"> <div class="form-check">
<input <input class="form-check-input" type="checkbox" :value="index"
class="form-check-input" x-model="selectedMessages" />
type="checkbox"
:value="index"
x-model="selectedMessages"
/>
<div class="row column-gap-1 mb-2"> <div class="row column-gap-1 mb-2">
<div class="col-md-3"> <div class="col-md-3">
<p <p x-text="'Sender: ' + senders[index]"></p>
x-text="'Sender: ' + senders[index]"
></p>
</div> </div>
<div class="col"> <div class="col">
<p <p x-text="'Date and Time: ' + dates[index]"></p>
x-text="'Date and Time: ' + dates[index]"
></p>
</div> </div>
</div> </div>
<div class="col-md-9"> <div class="col-md-9">
<p x-text="message"></p> <p x-text="message.text"></p>
</div> </div>
</div> </div>
</td> </td>
@@ -144,12 +113,7 @@
<!-- Add a judgment, only when the messages array has content will the select all checkbox and its area be displayed" --> <!-- Add a judgment, only when the messages array has content will the select all checkbox and its area be displayed" -->
<div class="card-body border-top" x-show="messages.length > 0"> <div class="card-body border-top" x-show="messages.length > 0">
<div class="form-check"> <div class="form-check">
<input <input id="selectAllCheckbox" class="form-check-input" type="checkbox" @change="toggleAll($event)" />
id="selectAllCheckbox"
class="form-check-input"
type="checkbox"
@change="toggleAll($event)"
/>
<label class="form-check-label">Select All</label> <label class="form-check-label">Select All</label>
</div> </div>
</div> </div>
@@ -162,11 +126,7 @@
</button> </button>
<!-- 删除选中短信按钮 --> <!-- 删除选中短信按钮 -->
<!-- Delete selected SMS button --> <!-- Delete selected SMS button -->
<button <button class="btn btn-danger" type="button" @click="deleteSelectedSMS()">
class="btn btn-danger"
type="button"
@click="deleteSelectedSMS()"
>
Delete Selected SMS Delete Selected SMS
</button> </button>
</div> </div>
@@ -181,24 +141,15 @@
<div class="card-body"> <div class="card-body">
<div class="mb-3"> <div class="mb-3">
<label for="phoneNumber" class="form-label">Recipient's Number</label> <label for="phoneNumber" class="form-label">Recipient's Number</label>
<input <input type="text" class="form-control" id="phoneNumber" x-model="phoneNumber"
type="text" placeholder="Enter the recipient's number." />
class="form-control"
id="phoneNumber"
x-model="phoneNumber"
placeholder="Enter the recipient's number."
/>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="messageToSend" class="form-label">SMS Content</label> <label for="messageToSend" class="form-label">SMS Content</label>
<textarea <textarea class="form-control" id="messageToSend" rows="3" x-model="messageToSend"
class="form-control" placeholder="Enter SMS content."></textarea>
id="messageToSend"
rows="3"
x-model="messageToSend"
placeholder="Enter SMS content."
></textarea>
</div> </div>
<div id="notification" class="alert" style="display: none;"></div>
<button class="btn btn-primary" @click="sendSMS()"> <button class="btn btn-primary" @click="sendSMS()">
Send SMS Send SMS
</button> </button>
@@ -218,20 +169,32 @@
senders: [], senders: [],
dates: [], dates: [],
selectedMessages: [], selectedMessages: [],
phoneNumber: "", phoneNumber: '',
messageToSend: "", messageToSend: '',
messageIndices: [], // 确保初始化messageIndices数组
// 清除现有数据
clearData() {
this.messages = [];
this.senders = [];
this.dates = [];
this.selectedMessages = [];
this.messageIndices = [];
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
if (selectAllCheckbox) {
selectAllCheckbox.checked = false;
}
},
// 请求获取短信 // 请求获取短信
// Request to get SMS
requestSMS() { requestSMS() {
this.isLoading = true; this.isLoading = true;
fetch("/cgi-bin/get_sms") fetch(`/cgi-bin/get_atcommand?${new URLSearchParams({ atcmd: "AT+CSMS=1;+CSDH=0;+CNMI=2,1,0,0,0;+CMGF=1;+CSCA?;+CSMP=17,167,0,8;+CPMS=\"ME\",\"ME\",\"ME\";+CSCS=\"UCS2\";+CMGL=\"ALL\"" })}`)
.then((response) => response.text()) .then(response => response.text())
.then((data) => { .then(data => {
this.atCommandResponse = data this.atCommandResponse = data.split('\n')
.split("\n") .filter(line => line.trim() !== "OK" && line.trim() !== "")
.filter((line) => line.trim() !== "OK" && line.trim() !== "") .join('\n');
.join("\n");
}) })
.finally(() => { .finally(() => {
this.isLoading = false; this.isLoading = false;
@@ -240,165 +203,196 @@
}); });
}, },
// 清除现有数据 // 解析短信数据的方法
// Clear existing data parseSMSData(data) {
clearData() { const cmglRegex = /^\s*\+CMGL:\s*(\d+),"[^"]*","([^"]*)"[^"]*,"([^"]*)"/gm;
this.messages = []; const cscaRegex = /^\s*\+CSCA:\s*"([^"]*)"/gm;
this.senders = []; this.messageIndices = [];
this.serviceCenters = [];
this.dates = []; this.dates = [];
this.selectedMessages = []; this.senders = [];
const selectAllCheckbox = this.messages = [];
document.getElementById("selectAllCheckbox"); let match;
if (selectAllCheckbox) { let lastIndex = null;
selectAllCheckbox.checked = false; while ((match = cmglRegex.exec(data)) !== null) {
const index = parseInt(match[1]);
const senderHex = match[2];
const sender = senderHex.startsWith("003") ? this.convertHexToText(senderHex) : senderHex;
const dateStr = match[3].replace(/\+\d{2}$/, "");
const date = this.parseCustomDate(dateStr);
if (isNaN(date)) {
console.error(`Invalid Date: ${dateStr}`);
continue;
}
const startIndex = cmglRegex.lastIndex;
const endIndex = data.indexOf("+CMGL:", startIndex) !== -1 ? data.indexOf("+CMGL:", startIndex) : data.length;
const messageHex = data.substring(startIndex, endIndex).trim();
const message = /^[0-9a-fA-F]+$/.test(messageHex) ? this.convertHexToText(messageHex) : messageHex;
if (lastIndex !== null && this.messages[lastIndex].sender === sender && (date - this.messages[lastIndex].date) / 1000 <= 1) {
this.messages[lastIndex].text += " " + message;
this.messages[lastIndex].indices.push(index);
this.dates[lastIndex] = this.formatDate(date);
} else {
this.messageIndices.push([index]);
this.senders.push(sender);
this.dates.push(this.formatDate(date));
this.messages.push({ text: message, sender: sender, date: date, indices: [index] });
lastIndex = this.messages.length - 1;
}
}
while ((match = cscaRegex.exec(data)) !== null) {
const serviceCenterHex = match[1];
const serviceCenter = this.convertHexToText(serviceCenterHex);
this.serviceCenters.push(serviceCenter);
} }
}, },
// 解析短信数据 // 将十六进制转换为文本(假设使用 UTF-16BE 编码)
// Parse SMS data convertHexToText(hex) {
parseSMSData(data) { const bytes = new Uint8Array(hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
const cmglRegex = return new TextDecoder('utf-16be').decode(bytes);
/^\s*\+CMGL:\s*(\d+),"[^"]*","([^"]*)"[^"]*,"([^"]*)"/gm; },
const messageGroups = {};
this.messageIndices = []; // 确保初始化messageIndices数组
// Ensure that the messageIndices array is initialized
let match; // 自定义解析日期函数
while ((match = cmglRegex.exec(data)) !== null) { parseCustomDate(dateStr) {
const index = parseInt(match[1]); const [datePart, timePart] = dateStr.split(',');
const sender = match[2]; const [day, month, year] = datePart.split('/').map(part => parseInt(part, 10));
const date = match[3].replace(/\+\d{2}$/, ""); const [hour, minute, second] = timePart.split(':').map(part => parseInt(part, 10));
const startIndex = cmglRegex.lastIndex; // 将日期转换为标准格式的日期对象
const endIndex = return new Date(Date.UTC(2000 + year, month - 1, day, hour, minute, second));
data.indexOf("+CMGL:", startIndex) !== -1 },
? data.indexOf("+CMGL:", startIndex)
: data.length;
const messageHex = data.substring(startIndex, endIndex).trim();
// 判断 messageHex 是否为 UCS2 格式 // 自定义格式化日期函数
// Determine if messageHex is in UCS2 format formatDate(date) {
const message = /^[0-9a-fA-F]+$/.test(messageHex) const year = date.getUTCFullYear() - 2000;
? this.convertHexToText(messageHex) const month = (date.getUTCMonth() + 1).toString().padStart(2, '0');
: messageHex; const day = date.getUTCDate().toString().padStart(2, '0');
const hour = date.getUTCHours().toString().padStart(2, '0');
if (!messageGroups[date]) { const minute = date.getUTCMinutes().toString().padStart(2, '0');
messageGroups[date] = { sender, messages: [], indices: [] }; const second = date.getUTCSeconds().toString().padStart(2, '0');
} return `${day}/${month}/${year},${hour}:${minute}:${second}`;
messageGroups[date].messages.push(message);
messageGroups[date].indices.push(index);
this.messageIndices.push(index); // 填充 messageIndices 数组
// Fill the messageIndices array
}
for (const date in messageGroups) {
this.dates.push(date);
this.senders.push(messageGroups[date].sender);
this.messages.push(messageGroups[date].messages.join(" "));
}
}, },
// 删除选中的短信 // 删除选中的短信
// Delete selected SMS
deleteSelectedSMS() { deleteSelectedSMS() {
if (this.selectedMessages.length === 0) { if (this.selectedMessages.length === 0) {
console.warn("No SMS selected"); console.warn("没有选中的短信");
return; return;
} }
if (!this.messageIndices || this.messageIndices.length === 0) { if (!this.messageIndices || this.messageIndices.length === 0) {
console.error("SMS index is not correctly initialized or is empty"); console.error("短信索引未正确初始化或为空");
return; return;
} }
// 检查是否全选 // 检查是否全选
// Check if all are selected const isAllSelected = this.selectedMessages.length === this.messages.length;
const isAllSelected =
this.selectedMessages.length === this.messages.length;
if (isAllSelected) { if (isAllSelected) {
// 如果全选,则调用删除所有短信的方法 // 如果全选,则调用删除所有短信的方法
// If all are selected, call the method to delete all SMS
this.deleteAllSMS(); this.deleteAllSMS();
} else { } else {
// 否则,删除选中的短信 // 否则,删除选中的短信
// Otherwise, delete the selected SMS const indicesToDelete = [];
const deletePromises = this.selectedMessages.map((index) => { this.selectedMessages.forEach(index => {
if (index >= this.messageIndices.length) { indicesToDelete.push(...this.messages[index].indices);
console.error("SMS index out of range");
return Promise.resolve(); // 返回已解决的Promise防止进一步错误
// Return a resolved Promise to prevent further errors
}
const actualIndex = this.messageIndices[index];
return fetch(
`/cgi-bin/get_atcommand?${new URLSearchParams({
atcmd: `AT+CMGD=${actualIndex}`,
})}`
);
}); });
if (indicesToDelete.length === 0) {
console.warn("没有有效的短信索引");
return;
}
Promise.all(deletePromises).finally(() => { // 拼接 AT 命令
const atCommands = indicesToDelete.map((index, i) => i === 0 ? `AT+CMGD=${index}` : `+CMGD=${index}`).join(';');
fetch(`/cgi-bin/get_atcommand?${new URLSearchParams({ atcmd: atCommands })}`)
.finally(() => {
this.selectedMessages = []; this.selectedMessages = [];
this.requestSMS(); this.requestSMS();
}); });
} }
}, },
// 将十六进制转换为文本(假设使用 UTF-16BE 编码)
// Convert hexadecimal to text (assuming UTF-16BE encoding)
convertHexToText(hex) {
const bytes = new Uint8Array(
hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
);
return new TextDecoder("utf-16be").decode(bytes);
},
// 删除所有短信 // 删除所有短信
// Delete all SMS
deleteAllSMS() { deleteAllSMS() {
fetch( fetch(`/cgi-bin/get_atcommand?${new URLSearchParams({ atcmd: "AT+CMGD=,4" })}`)
`/cgi-bin/get_atcommand?${new URLSearchParams({ .finally(() => {
atcmd: "AT+CMGD=,4",
})}`
).finally(() => {
this.init(); this.init();
}); });
}, },
// 发送短信 // 发送短信
// Send SMS encodeUCS2(input) {
toUCS2(message) { let output = '';
let ucs2Message = ""; for (let i = 0; i < input.length; i++) {
for (let i = 0; i < message.length; i++) { const hex = input.charCodeAt(i).toString(16).toUpperCase().padStart(4, '0');
const code = message.charCodeAt(i).toString(16).toUpperCase(); output += hex;
ucs2Message += ("0000" + code).slice(-4); // Ensure each code is 4 digits
} }
return ucs2Message; return output;
}, },
sendSMS() { async sendSMS() {
const ucs2Message = this.toUCS2(this.messageToSend); let phoneNumberWithCountryCode;
const encodedMessage = encodeURIComponent(ucs2Message); if (this.phoneNumber.length < 11) {
const params = new URLSearchParams({ phoneNumberWithCountryCode = this.phoneNumber;
number: this.phoneNumber,
msg: encodedMessage,
});
fetch(`/cgi-bin/send_sms?${params.toString()}`)
.then((response) => response.text())
.then((data) => {
console.log("Response from server:", data);
// 检查返回的数据中是否包含 '+CMS ERROR'
// Check if the returned data contains '+CMS ERROR'
if (data.includes("+CMS ERROR")) {
// 解析错误代码,如果存在,获取更具体的错误信息
// Parse the error code, if it exists, get more specific error information
const errorCode = data.match(/\+CMS ERROR: (\d+)/)?.[1];
console.error("SMS send error:", data);
alert(`SMS sending failed!: ${errorCode}`);
} else { } else {
alert("SMS sent successfully!"); const serviceCenterPrefix = this.serviceCenters[0].substring(0, 3);
phoneNumberWithCountryCode = `${serviceCenterPrefix}${this.phoneNumber}`;
} }
const encodedPhoneNumber = this.encodeUCS2(phoneNumberWithCountryCode);
const messageSegments = this.splitMessage(this.messageToSend, 70); // 将消息分段
const uid = Math.floor(Math.random() * 256); // 生成随机的UID
const totalSegments = messageSegments.length;
let allSegmentsSent = true;
let errorCode = null;
for (let i = 0; i < totalSegments; i++) {
const segment = messageSegments[i];
const encodedMessage = this.encodeUCS2(segment);
const currentSegment = i + 1;
const Command = `${uid},${currentSegment},${totalSegments}`;
const params = new URLSearchParams({
number: encodedPhoneNumber,
msg: encodedMessage,
Command: Command
}); });
try {
const response = await fetch(`/cgi-bin/send_sms?${params.toString()}`);
const data = await response.text();
console.log("Response from server:", data);
// 检查返回的数据中是否包含 '+CMS ERROR'
if (data.includes('+CMS ERROR')) {
errorCode = data.match(/\+CMS ERROR: (\d+)/)?.[1];
console.error("SMS send error:", data);
allSegmentsSent = false;
break; // 停止发送剩余的段
}
} catch (error) {
console.error("Fetch error:", error);
allSegmentsSent = false;
break; // 停止发送剩余的段
}
}
if (allSegmentsSent) {
this.showNotification("SMS sent successfully!");
} else {
this.showNotification(`SMS sending failed!: ${errorCode}`);
}
},
splitMessage(message, length) {
const segments = [];
for (let i = 0; i < message.length; i += length) {
segments.push(message.substring(i, i + length));
}
return segments;
},
showNotification(message, type) {
const notification = document.getElementById('notification');
notification.innerText = message;
notification.className = `alert alert-${type}`;
notification.style.display = 'block';
setTimeout(() => {
notification.style.display = 'none';
}, 3000); // 3秒后自动关闭
}, },
// 初始化 // 初始化
@@ -411,12 +405,11 @@
// 全选/取消全选 // 全选/取消全选
// Select all/deselect all // Select all/deselect all
toggleAll(event) { toggleAll(event) {
this.selectedMessages = event.target.checked this.selectedMessages = event.target.checked ? this.messages.map((_, index) => index) : [];
? this.messages.map((_, index) => index) }
: [];
},
}; };
} }
</script> </script>
</body> </body>
</html> </html>