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

@@ -51,9 +51,10 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/sms.html">SMS</a> <a class="nav-link" 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">
<a <a
class="nav-link active" class="nav-link active"
href="/deviceinfo.html" href="/deviceinfo.html"
@@ -145,92 +146,137 @@
<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
Show Contributors type="button"
</p> class="link-info link-opacity-50-hover link-offset-2"
data-bs-toggle="modal"
data-bs-target="#staticBackdrop"
>
Show Contributors
</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
<div class="modal-dialog modal-dialog-centered"> class="modal fade"
<div class="modal-content"> id="staticBackdrop"
<div class="modal-header"> data-bs-backdrop="static"
<h1 class="modal-title fs-5" id="staticBackdropLabel">Contributors</h1> data-bs-keyboard="false"
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> tabindex="-1"
</div> aria-labelledby="staticBackdropLabel"
<div class="modal-body"> aria-hidden="true"
<table> >
<tbody> <div class="modal-dialog modal-dialog-centered">
<tr> <div class="modal-content">
<th> <div class="modal-header">
RGMII Toolkit and Documentation <h1
</th> class="modal-title fs-5"
<td class="col-md-2"> id="staticBackdropLabel"
<a href="https://github.com/iamromulan" target="_blank">iamromulan</a> >
</td> Contributors
</tr> </h1>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<table>
<tbody>
<tr>
<th>
RGMII Toolkit and Documentation
</th>
<td class="col-md-2">
<a
href="https://github.com/iamromulan"
target="_blank"
>iamromulan</a
>
</td>
</tr>
<tr> <tr>
<th> <th>Simple Admin 2.0</th>
Simple Admin 2.0 <td class="col-md-2">
</th> <a
<td class="col-md-2"> href="https://github.com/dr-dolomite"
<a href="https://github.com/dr-dolomite" target="_blank">dr-dolomite</a> target="_blank"
</td> >dr-dolomite</a
</tr> >
</td>
</tr>
<tr> <tr>
<th> <th>SMS Feature</th>
SMS Feature <td class="col-md-2">
</th> <a
<td class="col-md-2"> href="https://github.com/snjzb"
<a href="https://github.com/snjzb" target="_blank">snjzb</a> target="_blank"
</td> >snjzb</a
</tr> >
</td>
</tr>
<tr> <tr>
<th> <th>Original Simple Admin</th>
Original Simple Admin <td class="col-md-2">
</th> <a
<td class="col-md-2"> href="https://github.com/aesthernr"
<a href="https://github.com/aesthernr" target="_blank">aesthernr</a> target="_blank"
</td> >aesthernr</a
</tr> >
</td>
</tr>
<tr> <tr>
<th> <th>Original Socat Bridge</th>
Original Socat Bridge <td class="col-md-2">
</th> <a
<td class="col-md-2"> href="https://github.com/natecarlson"
<a href="https://github.com/natecarlson" target="_blank">natecarlson</a> target="_blank"
</td> >natecarlson</a
</tr> >
</td>
</tr>
<tr> <tr>
<th> <th>Original Simple Admin Fixes</th>
Original Simple Admin Fixes <td class="col-md-2">
</th> <a
<td class="col-md-2"> href="https://github.com/rbflurry/"
<a href="https://github.com/rbflurry/" target="_blank">rbflurry</a> target="_blank"
</td> >rbflurry</a
</tr> >
</td>
</tr>
<tr> <tr>
<th> <th>SA Parsing Patch</th>
SA Parsing Patch <td class="col-md-2">
</th> <a
<td class="col-md-2"> href="https://github.com/tarunVreddy"
<a href="https://github.com/tarunVreddy" target="_blank">tarunVreddy</a> target="_blank"
</td> >tarunVreddy</a
</tr> >
</tbody> </td>
</table> </tr>
</div> </tbody>
<div class="modal-footer"> </table>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> </div>
</div> <div class="modal-footer">
</div> <button
</div> type="button"
</div> class="btn btn-secondary"
data-bs-dismiss="modal"
>
Close
</button>
</div>
</div>
</div>
</div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@@ -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, "");
if (this.phoneNumber === "") { try {
this.phoneNumber = "Unknown"; this.imsi = lines[7].trim();
this.iccid = lines[9].trim().replace("+ICCID: ", "");
this.phoneNumber = lines[18]
.trim()
.split(",")[1]
.replace(/"/g, "");
if (this.phoneNumber === "") {
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,422 +1,415 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="zh-CN" data-bs-theme="light"> <html lang="en" data-bs-theme="dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Simple Admin</title>
<!-- 导入自定义和Bootstrap样式 -->
<link rel="stylesheet" href="css/styles.css" />
<link rel="stylesheet" href="css/bootstrap.min.css" />
<!-- 网站图标 -->
<link rel="icon" href="favicon.ico" />
<!-- 导入Bootstrap和Alpine.js脚本 -->
<script src="js/bootstrap.bundle.min.js"></script>
<script src="js/alpinejs.min.js" defer></script>
<!-- Contributed By: snjzb --> <head>
</head> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Simple Admin</title>
<!-- 导入自定义和Bootstrap样式 -->
<link rel="stylesheet" href="css/styles.css" />
<link rel="stylesheet" href="css/bootstrap.min.css" />
<!-- 网站图标 -->
<link rel="icon" href="favicon.ico" />
<!-- 导入Bootstrap和Alpine.js脚本 -->
<script src="js/bootstrap.bundle.min.js"></script>
<script src="js/alpinejs.min.js" defer></script>
<body> <!-- Contributed By: snjzb -->
<main> </head>
<div class="container my-4" x-data="fetchSMS()">
<nav class="navbar navbar-expand-lg mt-2"> <body>
<div class="container-fluid"> <main>
<a class="navbar-brand" href="/" <div class="container my-4" x-data="fetchSMS()">
><span class="mb-0 h4">Simple Admin</span></a <nav class="navbar navbar-expand-lg mt-2">
> <div class="container-fluid">
<button <a class="navbar-brand" href="/"><span class="mb-0 h4">Simple Admin</span></a>
class="navbar-toggler" <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText"
type="button" aria-controls="navbarText" aria-expanded="false" aria-label="切换导航">
data-bs-toggle="collapse" <span class="navbar-toggler-icon"></span>
data-bs-target="#navbarText" </button>
aria-controls="navbarText" <div class="collapse navbar-collapse" id="navbarText">
aria-expanded="false" <ul class="navbar-nav me-auto mb-2 ml-4 mb-lg-0">
aria-label="切换导航" <li class="nav-item">
> <a class="nav-link" href="/">Home</a>
<span class="navbar-toggler-icon"></span> </li>
</button> <li class="nav-item">
<div class="collapse navbar-collapse" id="navbarText"> <a class="nav-link" href="/network.html">Simple Network</a>
<ul class="navbar-nav me-auto mb-2 ml-4 mb-lg-0"> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/">Home</a> <a class="nav-link" href="/settings.html">Simple Settings</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/network.html">Simple Network</a> <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="/settings.html">Simple Settings</a> <a class="nav-link" href="/console">Console</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a <a class="nav-link" href="/deviceinfo.html">Device Information</a>
class="nav-link active" </li>
aria-current="page" </ul>
href="/sms.html" <span class="navbar-text">
>SMS</a <button class="btn btn-link text-reset" id="darkModeToggle">
> Dark Mode
</li> </button>
<li class="nav-item"> </span>
<a class="nav-link" href="/console">Console</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/deviceinfo.html"
>Device Information</a
>
</li>
</ul>
<span class="navbar-text">
<button class="btn btn-link text-reset" id="darkModeToggle">
Dark Mode
</button>
</span>
</div>
</div> </div>
</nav> </div>
<div class="row mt-5 mb-4"> </nav>
<div class="col"> <div class="row mt-5 mb-4">
<div class="card"> <div class="col">
<div class="card-header">SMS Inbox</div> <div class="card">
<div class="card-body"> <div class="card-header">SMS Inbox</div>
<div class="card-text"> <div class="card-body">
<div class="col"> <div class="card-text">
<div <div class="col">
style=" <div 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 class="table table-hover border-success" x-show="!isLoading">
<table <tbody>
class="table table-hover border-success" <!-- 没有消息时显示 -->
x-show="!isLoading" <!-- Display when there are no messages -->
> <template x-if="messages.length === 0 && !isLoading">
<tbody> <div>
<!-- 没有消息时显示 --> <p>Inbox is Empty</p>
<!-- Display when there are no messages --> </div>
<template x-if="messages.length === 0 && !isLoading"> </template>
<div> <!-- 循环显示短信消息 -->
<p>Inbox is Empty</p> <!-- "Loop display SMS messages" -->
</div> <template x-for="(message, index) in messages" :key="index">
</template> <tr>
<!-- 循环显示短信消息 --> <td>
<!-- "Loop display SMS messages" --> <div class="form-check">
<template <input class="form-check-input" type="checkbox" :value="index"
x-for="(message, index) in messages" x-model="selectedMessages" />
:key="index" <div class="row column-gap-1 mb-2">
> <div class="col-md-3">
<tr> <p x-text="'Sender: ' + senders[index]"></p>
<td>
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
:value="index"
x-model="selectedMessages"
/>
<div class="row column-gap-1 mb-2">
<div class="col-md-3">
<p
x-text="'Sender: ' + senders[index]"
></p>
</div>
<div class="col">
<p
x-text="'Date and Time: ' + dates[index]"
></p>
</div>
</div> </div>
<div class="col-md-9"> <div class="col">
<p x-text="message"></p> <p x-text="'Date and Time: ' + dates[index]"></p>
</div> </div>
</div> </div>
</td> <div class="col-md-9">
</tr> <p x-text="message.text"></p>
</template> </div>
</tbody> </div>
</table> </td>
</div> </tr>
</template>
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>
<!-- 添加判断只有当messages数组有内容时才显示全选复选框及其区域 --> </div>
<!-- Add a judgment, only when the messages array has content will the select all checkbox and its area be displayed" --> <!-- 添加判断只有当messages数组有内容时才显示全选复选框及其区域 -->
<div class="card-body border-top" x-show="messages.length > 0"> <!-- Add a judgment, only when the messages array has content will the select all checkbox and its area be displayed" -->
<div class="form-check"> <div class="card-body border-top" x-show="messages.length > 0">
<input <div class="form-check">
id="selectAllCheckbox" <input id="selectAllCheckbox" class="form-check-input" type="checkbox" @change="toggleAll($event)" />
class="form-check-input" <label class="form-check-label">Select All</label>
type="checkbox"
@change="toggleAll($event)"
/>
<label class="form-check-label">Select All</label>
</div>
</div>
<div class="card-footer">
<div class="d-grid gap-2 d-md-flex justify-content-md-start">
<!-- 刷新按钮 -->
<!-- Refresh button -->
<button class="btn btn-success" type="button" @click="init()">
Refresh
</button>
<!-- 删除选中短信按钮 -->
<!-- Delete selected SMS button -->
<button
class="btn btn-danger"
type="button"
@click="deleteSelectedSMS()"
>
Delete Selected SMS
</button>
</div>
</div> </div>
</div> </div>
</div> <div class="card-footer">
</div> <div class="d-grid gap-2 d-md-flex justify-content-md-start">
<div class="row mt-5 mb-4"> <!-- 刷新按钮 -->
<div class="col"> <!-- Refresh button -->
<div class="card"> <button class="btn btn-success" type="button" @click="init()">
<div class="card-header">Send Message</div> Refresh
<div class="card-body"> </button>
<div class="mb-3"> <!-- 删除选中短信按钮 -->
<label for="phoneNumber" class="form-label">Recipient's Number</label> <!-- Delete selected SMS button -->
<input <button class="btn btn-danger" type="button" @click="deleteSelectedSMS()">
type="text" Delete Selected SMS
class="form-control"
id="phoneNumber"
x-model="phoneNumber"
placeholder="Enter the recipient's number."
/>
</div>
<div class="mb-3">
<label for="messageToSend" class="form-label">SMS Content</label>
<textarea
class="form-control"
id="messageToSend"
rows="3"
x-model="messageToSend"
placeholder="Enter SMS content."
></textarea>
</div>
<button class="btn btn-primary" @click="sendSMS()">
Send SMS
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</main> <div class="row mt-5 mb-4">
<script src="js/dark-mode.js"></script> <div class="col">
<script> <div class="card">
function fetchSMS() { <div class="card-header">Send Message</div>
return { <div class="card-body">
isLoading: false, <div class="mb-3">
atCommandResponse: "", <label for="phoneNumber" class="form-label">Recipient's Number</label>
messages: [], <input type="text" class="form-control" id="phoneNumber" x-model="phoneNumber"
senders: [], placeholder="Enter the recipient's number." />
dates: [], </div>
selectedMessages: [], <div class="mb-3">
phoneNumber: "", <label for="messageToSend" class="form-label">SMS Content</label>
messageToSend: "", <textarea class="form-control" id="messageToSend" rows="3" x-model="messageToSend"
placeholder="Enter SMS content."></textarea>
</div>
<div id="notification" class="alert" style="display: none;"></div>
<button class="btn btn-primary" @click="sendSMS()">
Send SMS
</button>
</div>
</div>
</div>
</div>
</div>
</main>
<script src="js/dark-mode.js"></script>
<script>
function fetchSMS() {
return {
isLoading: false,
atCommandResponse: "",
messages: [],
senders: [],
dates: [],
selectedMessages: [],
phoneNumber: '',
messageToSend: '',
messageIndices: [], // 确保初始化messageIndices数组
// 请求获取短信 // 清除现有数据
// Request to get SMS clearData() {
requestSMS() { this.messages = [];
this.isLoading = true; this.senders = [];
fetch("/cgi-bin/get_sms") this.dates = [];
.then((response) => response.text()) this.selectedMessages = [];
.then((data) => { this.messageIndices = [];
this.atCommandResponse = data const selectAllCheckbox = document.getElementById('selectAllCheckbox');
.split("\n") if (selectAllCheckbox) {
.filter((line) => line.trim() !== "OK" && line.trim() !== "") selectAllCheckbox.checked = false;
.join("\n"); }
}) },
.finally(() => {
this.isLoading = false;
this.clearData();
this.parseSMSData(this.atCommandResponse);
});
},
// 清除现有数据 // 请求获取短信
// Clear existing data requestSMS() {
clearData() { this.isLoading = true;
this.messages = []; 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\"" })}`)
this.senders = []; .then(response => response.text())
this.dates = []; .then(data => {
this.selectedMessages = []; this.atCommandResponse = data.split('\n')
const selectAllCheckbox = .filter(line => line.trim() !== "OK" && line.trim() !== "")
document.getElementById("selectAllCheckbox"); .join('\n');
if (selectAllCheckbox) { })
selectAllCheckbox.checked = false; .finally(() => {
this.isLoading = false;
this.clearData();
this.parseSMSData(this.atCommandResponse);
});
},
// 解析短信数据的方法
parseSMSData(data) {
const cmglRegex = /^\s*\+CMGL:\s*(\d+),"[^"]*","([^"]*)"[^"]*,"([^"]*)"/gm;
const cscaRegex = /^\s*\+CSCA:\s*"([^"]*)"/gm;
this.messageIndices = [];
this.serviceCenters = [];
this.dates = [];
this.senders = [];
this.messages = [];
let match;
let lastIndex = null;
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();
// Parse SMS data const message = /^[0-9a-fA-F]+$/.test(messageHex) ? this.convertHexToText(messageHex) : messageHex;
parseSMSData(data) { if (lastIndex !== null && this.messages[lastIndex].sender === sender && (date - this.messages[lastIndex].date) / 1000 <= 1) {
const cmglRegex = this.messages[lastIndex].text += " " + message;
/^\s*\+CMGL:\s*(\d+),"[^"]*","([^"]*)"[^"]*,"([^"]*)"/gm; this.messages[lastIndex].indices.push(index);
const messageGroups = {}; this.dates[lastIndex] = this.formatDate(date);
this.messageIndices = []; // 确保初始化messageIndices数组
// Ensure that the messageIndices array is initialized
let match;
while ((match = cmglRegex.exec(data)) !== null) {
const index = parseInt(match[1]);
const sender = match[2];
const date = match[3].replace(/\+\d{2}$/, "");
const startIndex = cmglRegex.lastIndex;
const endIndex =
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
const message = /^[0-9a-fA-F]+$/.test(messageHex)
? this.convertHexToText(messageHex)
: messageHex;
if (!messageGroups[date]) {
messageGroups[date] = { sender, messages: [], indices: [] };
}
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() {
if (this.selectedMessages.length === 0) {
console.warn("No SMS selected");
return;
}
if (!this.messageIndices || this.messageIndices.length === 0) {
console.error("SMS index is not correctly initialized or is empty");
return;
}
// 检查是否全选
// Check if all are selected
const isAllSelected =
this.selectedMessages.length === this.messages.length;
if (isAllSelected) {
// 如果全选,则调用删除所有短信的方法
// If all are selected, call the method to delete all SMS
this.deleteAllSMS();
} else { } else {
// 否则,删除选中的短信 this.messageIndices.push([index]);
// Otherwise, delete the selected SMS this.senders.push(sender);
const deletePromises = this.selectedMessages.map((index) => { this.dates.push(this.formatDate(date));
if (index >= this.messageIndices.length) { this.messages.push({ text: message, sender: sender, date: date, indices: [index] });
console.error("SMS index out of range"); lastIndex = this.messages.length - 1;
return Promise.resolve(); // 返回已解决的Promise防止进一步错误 }
// Return a resolved Promise to prevent further errors }
} while ((match = cscaRegex.exec(data)) !== null) {
const actualIndex = this.messageIndices[index]; const serviceCenterHex = match[1];
return fetch( const serviceCenter = this.convertHexToText(serviceCenterHex);
`/cgi-bin/get_atcommand?${new URLSearchParams({ this.serviceCenters.push(serviceCenter);
atcmd: `AT+CMGD=${actualIndex}`, }
})}` },
);
});
Promise.all(deletePromises).finally(() => { // 将十六进制转换为文本(假设使用 UTF-16BE 编码)
convertHexToText(hex) {
const bytes = new Uint8Array(hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
return new TextDecoder('utf-16be').decode(bytes);
},
// 自定义解析日期函数
parseCustomDate(dateStr) {
const [datePart, timePart] = dateStr.split(',');
const [day, month, year] = datePart.split('/').map(part => parseInt(part, 10));
const [hour, minute, second] = timePart.split(':').map(part => parseInt(part, 10));
// 将日期转换为标准格式的日期对象
return new Date(Date.UTC(2000 + year, month - 1, day, hour, minute, second));
},
// 自定义格式化日期函数
formatDate(date) {
const year = date.getUTCFullYear() - 2000;
const month = (date.getUTCMonth() + 1).toString().padStart(2, '0');
const day = date.getUTCDate().toString().padStart(2, '0');
const hour = date.getUTCHours().toString().padStart(2, '0');
const minute = date.getUTCMinutes().toString().padStart(2, '0');
const second = date.getUTCSeconds().toString().padStart(2, '0');
return `${day}/${month}/${year},${hour}:${minute}:${second}`;
},
// 删除选中的短信
deleteSelectedSMS() {
if (this.selectedMessages.length === 0) {
console.warn("没有选中的短信");
return;
}
if (!this.messageIndices || this.messageIndices.length === 0) {
console.error("短信索引未正确初始化或为空");
return;
}
// 检查是否全选
const isAllSelected = this.selectedMessages.length === this.messages.length;
if (isAllSelected) {
// 如果全选,则调用删除所有短信的方法
this.deleteAllSMS();
} else {
// 否则,删除选中的短信
const indicesToDelete = [];
this.selectedMessages.forEach(index => {
indicesToDelete.push(...this.messages[index].indices);
});
if (indicesToDelete.length === 0) {
console.warn("没有有效的短信索引");
return;
}
// 拼接 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 编码) deleteAllSMS() {
// Convert hexadecimal to text (assuming UTF-16BE encoding) fetch(`/cgi-bin/get_atcommand?${new URLSearchParams({ atcmd: "AT+CMGD=,4" })}`)
convertHexToText(hex) { .finally(() => {
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() {
fetch(
`/cgi-bin/get_atcommand?${new URLSearchParams({
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 output;
return ucs2Message; },
},
sendSMS() { async sendSMS() {
const ucs2Message = this.toUCS2(this.messageToSend); let phoneNumberWithCountryCode;
const encodedMessage = encodeURIComponent(ucs2Message); if (this.phoneNumber.length < 11) {
phoneNumberWithCountryCode = this.phoneNumber;
} else {
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({ const params = new URLSearchParams({
number: this.phoneNumber, number: encodedPhoneNumber,
msg: encodedMessage, msg: encodedMessage,
Command: Command
}); });
fetch(`/cgi-bin/send_sms?${params.toString()}`) try {
.then((response) => response.text()) const response = await fetch(`/cgi-bin/send_sms?${params.toString()}`);
.then((data) => { const data = await response.text();
console.log("Response from server:", 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 { // 检查返回的数据中是否包含 '+CMS ERROR'
alert("SMS sent successfully!"); 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) {
// Initialize const segments = [];
init() { for (let i = 0; i < message.length; i += length) {
this.clearData(); segments.push(message.substring(i, i + length));
this.requestSMS(); }
}, 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秒后自动关闭
},
// 初始化
// Initialize
init() {
this.clearData();
this.requestSMS();
},
// 全选/取消全选
// Select all/deselect all
toggleAll(event) {
this.selectedMessages = event.target.checked ? this.messages.map((_, index) => index) : [];
}
};
}
</script>
</body>
// 全选/取消全选
// Select all/deselect all
toggleAll(event) {
this.selectedMessages = event.target.checked
? this.messages.map((_, index) => index)
: [];
},
};
}
</script>
</body>
</html> </html>