优化:自定义模板可用变量标签支持正则替换 #421 #423

This commit is contained in:
pppscn 2024-03-11 21:27:30 +08:00
parent 6097cb9258
commit 0650e8b9bf
7 changed files with 128 additions and 122 deletions

View File

@ -17,7 +17,7 @@ import com.idormy.sms.forwarder.utils.task.TaskUtils
import com.xuexiang.xutil.resource.ResUtils.getString import com.xuexiang.xutil.resource.ResUtils.getString
import java.io.Serializable import java.io.Serializable
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.Date
@Suppress("unused") @Suppress("unused")
data class MsgInfo( data class MsgInfo(
@ -32,9 +32,6 @@ data class MsgInfo(
var uid: Int = 0, //APP通知的UID var uid: Int = 0, //APP通知的UID
) : Serializable { ) : Serializable {
private val titleForSend: String
get() = getTitleForSend("", "")
//通话类型1.来电挂机 2.去电挂机 3.未接来电 4.来电提醒 5.来电接通 6.去电拨出 //通话类型1.来电挂机 2.去电挂机 3.未接来电 4.来电提醒 5.来电接通 6.去电拨出
private val callTypeMap = mapOf( private val callTypeMap = mapOf(
//"0" to getString(R.string.unknown_call), //"0" to getString(R.string.unknown_call),
@ -46,53 +43,18 @@ data class MsgInfo(
"6" to getString(R.string.outgoing_call_started), "6" to getString(R.string.outgoing_call_started),
) )
fun getTitleForSend(titleTemplate: String): String { val titleForSend = getTitleForSend()
return getTitleForSend(titleTemplate, "")
}
@SuppressLint("SimpleDateFormat") val smsVoForSend = getContentForSend()
fun getTitleForSend(titleTemplate: String, regexReplace: String): String {
fun getTitleForSend(titleTemplate: String = "", regexReplace: String = ""): String {
var template = titleTemplate.replace("null", "") var template = titleTemplate.replace("null", "")
if (TextUtils.isEmpty(template)) template = getString(R.string.tag_from) if (TextUtils.isEmpty(template)) template = getString(R.string.tag_from)
val deviceMark = extraDeviceMark.trim()
val versionName = AppUtils.getAppVersionName() return replaceTemplate(template, regexReplace)
val splitSimInfo = simInfo.split("#####")
val title = splitSimInfo.getOrElse(0) { simInfo }
val scheme = splitSimInfo.getOrElse(1) { "" }
val titleForSend: String = template.replace(getString(R.string.tag_from), from)
.replace(getString(R.string.tag_package_name), from)
.replace(getString(R.string.tag_sms), content)
.replace(getString(R.string.tag_msg), content)
.replace(getString(R.string.tag_card_slot), title)
.replace(getString(R.string.tag_card_subid), subId.toString())
.replace(getString(R.string.tag_title), title)
.replace(getString(R.string.tag_scheme), scheme)
.replace(getString(R.string.tag_uid), uid.toString())
.replace(getString(R.string.tag_receive_time), SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date))
.replace(getString(R.string.tag_current_time), SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date()))
.replace(getString(R.string.tag_device_name), deviceMark)
.replace(getString(R.string.tag_app_version), versionName)
.replace(getString(R.string.tag_call_type), callTypeMap[callType.toString()] ?: getString(R.string.unknown_call))
.replace(getString(R.string.tag_ipv4), TaskUtils.ipv4)
.replace(getString(R.string.tag_ipv6), TaskUtils.ipv6)
.replace(getString(R.string.tag_battery_pct), "%.2f".format(TaskUtils.batteryPct))
.replace(getString(R.string.tag_battery_status), BatteryUtils.getStatus(TaskUtils.batteryStatus))
.replace(getString(R.string.tag_battery_plugged), BatteryUtils.getPlugged(TaskUtils.batteryPlugged))
.replace(getString(R.string.tag_battery_info), TaskUtils.batteryInfo)
.trim()
return replaceLocationTag(replaceAppName(regexReplace(titleForSend, regexReplace), from))
} }
val smsVoForSend: String fun getContentForSend(ruleSmsTemplate: String = "", regexReplace: String = ""): String {
get() = getContentForSend("", "")
fun getContentForSend(ruleSmsTemplate: String): String {
return getContentForSend(ruleSmsTemplate, "")
}
@SuppressLint("SimpleDateFormat")
fun getContentForSend(ruleSmsTemplate: String, regexReplace: String): String {
val deviceMark = extraDeviceMark.trim()
var customSmsTemplate: String = getString(R.string.tag_from).toString() + "\n" + var customSmsTemplate: String = getString(R.string.tag_from).toString() + "\n" +
getString(R.string.tag_sms) + "\n" + getString(R.string.tag_sms) + "\n" +
getString(R.string.tag_card_slot) + "\n" + getString(R.string.tag_card_slot) + "\n" +
@ -110,76 +72,52 @@ data class MsgInfo(
customSmsTemplate = smsTemplate.replace("null", "") customSmsTemplate = smsTemplate.replace("null", "")
} }
} }
val versionName = AppUtils.getAppVersionName()
val splitSimInfo = simInfo.split("#####") return replaceTemplate(customSmsTemplate, regexReplace)
val title = splitSimInfo.getOrElse(0) { simInfo }
val scheme = splitSimInfo.getOrElse(1) { "" }
val smsVoForSend: String = customSmsTemplate.replace(getString(R.string.tag_from), from)
.replace(getString(R.string.tag_package_name), from)
.replace(getString(R.string.tag_sms), content)
.replace(getString(R.string.tag_msg), content)
.replace(getString(R.string.tag_card_slot), title)
.replace(getString(R.string.tag_card_subid), subId.toString())
.replace(getString(R.string.tag_title), title)
.replace(getString(R.string.tag_scheme), scheme)
.replace(getString(R.string.tag_uid), uid.toString())
.replace(getString(R.string.tag_receive_time), SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date))
.replace(getString(R.string.tag_current_time), SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date()))
.replace(getString(R.string.tag_device_name), deviceMark)
.replace(getString(R.string.tag_app_version), versionName)
.replace(getString(R.string.tag_call_type), callTypeMap[callType.toString()] ?: getString(R.string.unknown_call))
.replace(getString(R.string.tag_ipv4), TaskUtils.ipv4)
.replace(getString(R.string.tag_ipv6), TaskUtils.ipv6)
.replace(getString(R.string.tag_battery_pct), "%.2f".format(TaskUtils.batteryPct))
.replace(getString(R.string.tag_battery_status), BatteryUtils.getStatus(TaskUtils.batteryStatus))
.replace(getString(R.string.tag_battery_plugged), BatteryUtils.getPlugged(TaskUtils.batteryPlugged))
.replace(getString(R.string.tag_battery_info), TaskUtils.batteryInfo)
.trim()
return replaceLocationTag(replaceAppName(regexReplace(smsVoForSend, regexReplace), from))
} }
@SuppressLint("SimpleDateFormat")
fun getContentFromJson(jsonTemplate: String): String { fun getContentFromJson(jsonTemplate: String): String {
var template = jsonTemplate.replace("null", "") var template = jsonTemplate.replace("null", "")
if (TextUtils.isEmpty(template)) template = getString(R.string.tag_from) if (TextUtils.isEmpty(template)) template = getString(R.string.tag_from)
val deviceMark = extraDeviceMark.trim() return replaceTemplate(template, "", true)
val versionName = AppUtils.getAppVersionName() }
val splitSimInfo = simInfo.split("#####")
var title = splitSimInfo.getOrElse(0) { simInfo }
title = jsonInnerStr(title)
var scheme = splitSimInfo.getOrElse(1) { "" }
scheme = jsonInnerStr(scheme)
from = jsonInnerStr(from)
content = jsonInnerStr(content)
val msgForSend: String = template.replace(getString(R.string.tag_from), from) @SuppressLint("SimpleDateFormat")
.replace(getString(R.string.tag_package_name), from) fun replaceTemplate(template: String, regexReplace: String, needJson: Boolean = false): String {
.replace(getString(R.string.tag_sms), content) val splitSimInfo = simInfo.split("#####")
.replace(getString(R.string.tag_msg), content) val title = splitSimInfo.getOrElse(0) { simInfo }
.replace(getString(R.string.tag_card_slot), title) val scheme = splitSimInfo.getOrElse(1) { "" }
.replace(getString(R.string.tag_card_subid), subId.toString())
.replace(getString(R.string.tag_title), title) return template.replaceTag(getString(R.string.tag_from), from, needJson)
.replace(getString(R.string.tag_scheme), scheme) .replaceTag(getString(R.string.tag_package_name), from, needJson)
.replace(getString(R.string.tag_uid), uid.toString()) .replaceTag(getString(R.string.tag_sms), content, needJson)
.replace(getString(R.string.tag_receive_time), jsonInnerStr(SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date))) .replaceTag(getString(R.string.tag_msg), content, needJson)
.replace(getString(R.string.tag_current_time), jsonInnerStr(SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date()))) .replaceTag(getString(R.string.tag_card_slot), title, needJson)
.replace(getString(R.string.tag_device_name), jsonInnerStr(deviceMark)) .replaceTag(getString(R.string.tag_card_subid), subId.toString(), needJson)
.replace(getString(R.string.tag_app_version), jsonInnerStr(versionName)) .replaceTag(getString(R.string.tag_title), title, needJson)
.replace(getString(R.string.tag_call_type), jsonInnerStr(callTypeMap[callType.toString()] ?: getString(R.string.unknown_call))) .replaceTag(getString(R.string.tag_scheme), scheme, needJson)
.replace(getString(R.string.tag_ipv4), jsonInnerStr(TaskUtils.ipv4)) .replaceTag(getString(R.string.tag_uid), uid.toString(), needJson)
.replace(getString(R.string.tag_ipv6), jsonInnerStr(TaskUtils.ipv6)) .replaceTag(getString(R.string.tag_receive_time), SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date), needJson)
.replace(getString(R.string.tag_battery_pct), jsonInnerStr("%.2f".format(TaskUtils.batteryPct))) .replaceTag(getString(R.string.tag_current_time), SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date()), needJson)
.replace(getString(R.string.tag_battery_status), jsonInnerStr(BatteryUtils.getStatus(TaskUtils.batteryStatus))) .replaceTag(getString(R.string.tag_device_name), extraDeviceMark.trim(), needJson)
.replace(getString(R.string.tag_battery_plugged), jsonInnerStr(BatteryUtils.getPlugged(TaskUtils.batteryPlugged))) .replaceTag(getString(R.string.tag_app_version), AppUtils.getAppVersionName(), needJson)
.replace(getString(R.string.tag_battery_info), jsonInnerStr(TaskUtils.batteryInfo)) .replaceTag(getString(R.string.tag_call_type), callTypeMap[callType.toString()] ?: getString(R.string.unknown_call), needJson)
.replaceTag(getString(R.string.tag_ipv4), TaskUtils.ipv4, needJson)
.replaceTag(getString(R.string.tag_ipv6), TaskUtils.ipv6, needJson)
.replaceTag(getString(R.string.tag_battery_pct), "%.2f".format(TaskUtils.batteryPct), needJson)
.replaceTag(getString(R.string.tag_battery_status), BatteryUtils.getStatus(TaskUtils.batteryStatus), needJson)
.replaceTag(getString(R.string.tag_battery_plugged), BatteryUtils.getPlugged(TaskUtils.batteryPlugged), needJson)
.replaceTag(getString(R.string.tag_battery_info), TaskUtils.batteryInfo, needJson)
.replaceAppNameTag(from, needJson)
.replaceLocationTag(needJson)
.regexReplace(regexReplace)
.trim() .trim()
return replaceLocationTag(replaceAppName(msgForSend, from, true), true)
} }
//正则替换内容 //正则替换内容
private fun regexReplace(content: String, regexReplace: String): String { private fun String.regexReplace(regexReplace: String): String {
return if (TextUtils.isEmpty(regexReplace)) content else try { return if (TextUtils.isEmpty(regexReplace)) this else try {
var newContent = content var newContent = this
val lineArray = regexReplace.split("\\n".toRegex()).toTypedArray() val lineArray = regexReplace.split("\\n".toRegex()).toTypedArray()
for (line in lineArray) { for (line in lineArray) {
val lineSplit = line.split("===".toRegex()).toTypedArray() val lineSplit = line.split("===".toRegex()).toTypedArray()
@ -192,14 +130,44 @@ data class MsgInfo(
newContent newContent
} catch (e: Exception) { } catch (e: Exception) {
Log.e("RegexReplace", "Failed to get the receiving phone number:" + e.message) Log.e("RegexReplace", "Failed to get the receiving phone number:" + e.message)
content this
} }
} }
//替换标签(支持正则替换)
private fun String.replaceTag(tag: String, info: String, needJson: Boolean = false, ignoreCase: Boolean = true): String {
var result = if (needJson) {
this.replace(tag, toJsonStr(info), ignoreCase)
} else {
this.replace(tag, info, ignoreCase)
}
val tagName = tag.removePrefix("{{").removeSuffix("}}")
val tagRegex = "\\{\\{${tagName}###([^=]+)===(.*?)\\}\\}".toRegex()
tagRegex.findAll(result).forEach {
try {
Log.d("MsgInfo", "tagRegex: ${it.value}, ${it.groupValues}")
val regex = it.groupValues[1]
val replacement = it.groupValues[2]
val temp = info.replace(regex.toRegex(), replacement)
Log.d("MsgInfo", "tagRegex: regex=$regex, replacement=$replacement, temp=$temp")
result = if (needJson) {
result.replace(it.value, toJsonStr(temp))
} else {
result.replace(it.value, temp)
}
} catch (e: Exception) {
Log.e("MsgInfo", "Failed to replace tagRegex: ${e.message}")
}
}
return result
}
//替换{{APP名称}}标签 //替换{{APP名称}}标签
private fun replaceAppName(content: String, packageName: String, needJson: Boolean = false): String { private fun String.replaceAppNameTag(packageName: String, needJson: Boolean = false): String {
if (TextUtils.isEmpty(content)) return content if (TextUtils.isEmpty(this)) return this
if (content.indexOf(getString(R.string.tag_app_name)) == -1) return content if (this.indexOf(getString(R.string.tag_app_name)) == -1) return this
var appName = "" var appName = ""
if (SettingUtils.enableLoadUserAppList && App.UserAppList.isNotEmpty()) { if (SettingUtils.enableLoadUserAppList && App.UserAppList.isNotEmpty()) {
@ -219,30 +187,30 @@ data class MsgInfo(
} }
} }
if (needJson) { if (needJson) {
appName = jsonInnerStr(appName) appName = toJsonStr(appName)
} }
return content.replace(getString(R.string.tag_app_name), appName) return this.replaceTag(getString(R.string.tag_app_name), appName)
} }
//替换 {{定位信息}} 标签 //替换 {{定位信息}} 标签
private fun replaceLocationTag(content: String, needJson: Boolean = false): String { private fun String.replaceLocationTag(needJson: Boolean = false): String {
if (TextUtils.isEmpty(content)) return content if (TextUtils.isEmpty(this)) return this
//if (content.indexOf(getString(R.string.tag_location)) == -1) return content
val location = HttpServerUtils.apiLocationCache val location = HttpServerUtils.apiLocationCache
var locationStr = location.toString() var locationStr = location.toString()
var address = location.address var address = location.address
if (needJson) { if (needJson) {
locationStr = jsonInnerStr(locationStr) locationStr = toJsonStr(locationStr)
address = jsonInnerStr(address) address = toJsonStr(address)
} }
return content.replace(getString(R.string.tag_location), locationStr) return this.replaceTag(getString(R.string.tag_location), locationStr)
.replace(getString(R.string.tag_location_longitude), location.longitude.toString()) .replaceTag(getString(R.string.tag_location_longitude), location.longitude.toString())
.replace(getString(R.string.tag_location_latitude), location.latitude.toString()) .replaceTag(getString(R.string.tag_location_latitude), location.latitude.toString())
.replace(getString(R.string.tag_location_address), address) .replaceTag(getString(R.string.tag_location_address), address)
} }
private fun jsonInnerStr(string: String?): String { //直接插入json字符串需要转义
private fun toJsonStr(string: String?): String {
if (string == null) return "null" if (string == null) return "null"
val jsonStr: String = Gson().toJson(string) val jsonStr: String = Gson().toJson(string)

View File

@ -702,6 +702,10 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
} }
val smsTemplate = binding!!.etSmsTemplate.text.toString().trim() val smsTemplate = binding!!.etSmsTemplate.text.toString().trim()
val checkResult = CommonUtils.checkTemplateTag(smsTemplate)
if (checkResult.isNotEmpty()) {
throw Exception(checkResult)
}
val regexReplace = binding!!.etRegexReplace.text.toString().trim() val regexReplace = binding!!.etRegexReplace.text.toString().trim()
val lineNum = checkRegexReplace(regexReplace) val lineNum = checkRegexReplace(regexReplace)
if (lineNum > 0) { if (lineNum > 0) {
@ -766,6 +770,14 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
for (line in lineArray!!) { for (line in lineArray!!) {
val position = line.indexOf("===") val position = line.indexOf("===")
if (position < 1) return lineNum if (position < 1) return lineNum
// 校验正则表达式部分是否合法
try {
line.substring(0, position).toRegex()
} catch (e: Exception) {
return lineNum
}
lineNum++ lineNum++
} }
return 0 return 0

View File

@ -207,6 +207,24 @@ class CommonUtils private constructor() {
PageOption.to(MarkdownFragment::class.java).putString(MarkdownFragment.KEY_MD_TITLE, title).putString(MarkdownFragment.KEY_MD_URL, url).putBoolean(MarkdownFragment.KEY_IS_IMMERSIVE, isImmersive).open(fragment!!) PageOption.to(MarkdownFragment::class.java).putString(MarkdownFragment.KEY_MD_TITLE, title).putString(MarkdownFragment.KEY_MD_URL, url).putBoolean(MarkdownFragment.KEY_IS_IMMERSIVE, isImmersive).open(fragment!!)
} }
//检查自定义模板中的标签是否合法
fun checkTemplateTag(template: String): String {
val tagRegex = "\\{\\{[^#]+###([^=]+)===(.*?)\\}\\}".toRegex()
tagRegex.findAll(template).forEach {
try {
it.groupValues[1].toRegex()
//TODO:怎么测试反向引用是否正确?
/*val replacement = it.groupValues[2]
if (replacement.isNotEmpty()) {
"pppscn/SmsForwarder".replace(regex, replacement)
}*/
} catch (e: Exception) {
return String.format(getString(R.string.invalid_tag), it.value, e.message)
}
}
return ""
}
//是否合法的url //是否合法的url
fun checkUrl(urls: String?): Boolean { fun checkUrl(urls: String?): Boolean {
return checkUrl(urls, false) return checkUrl(urls, false)

View File

@ -1383,4 +1383,6 @@
<string name="alarm_music_tips">Optional, download mp3/ogg/wav to the Download directory.</string> <string name="alarm_music_tips">Optional, download mp3/ogg/wav to the Download directory.</string>
<string name="alarm_volume">Alarm Volume</string> <string name="alarm_volume">Alarm Volume</string>
<string name="alarm_play_times">Play Times(0=Infinite)</string> <string name="alarm_play_times">Play Times(0=Infinite)</string>
<string name="invalid_tag">%s tag is invalid: %s</string>
</resources> </resources>

View File

@ -1384,4 +1384,6 @@
<string name="alarm_music_tips">可选,下载 mp3/ogg/wav 到 Download 目录</string> <string name="alarm_music_tips">可选,下载 mp3/ogg/wav 到 Download 目录</string>
<string name="alarm_volume">播放音量</string> <string name="alarm_volume">播放音量</string>
<string name="alarm_play_times">播放次数(0=无限)</string> <string name="alarm_play_times">播放次数(0=无限)</string>
<string name="invalid_tag">%s 标签无效:%s</string>
</resources> </resources>

View File

@ -1384,4 +1384,6 @@
<string name="alarm_music_tips">可選,下載 mp3/ogg/wav 到 Download 目錄</string> <string name="alarm_music_tips">可選,下載 mp3/ogg/wav 到 Download 目錄</string>
<string name="alarm_volume">播放音量</string> <string name="alarm_volume">播放音量</string>
<string name="alarm_play_times">播放次數(0=無限)</string> <string name="alarm_play_times">播放次數(0=無限)</string>
<string name="invalid_tag">%s 標籤無效:%s</string>
</resources> </resources>

View File

@ -1384,4 +1384,6 @@
<string name="alarm_music_tips">可选,下载 mp3/ogg/wav 到 Download 目录</string> <string name="alarm_music_tips">可选,下载 mp3/ogg/wav 到 Download 目录</string>
<string name="alarm_volume">播放音量</string> <string name="alarm_volume">播放音量</string>
<string name="alarm_play_times">播放次数(0=无限)</string> <string name="alarm_play_times">播放次数(0=无限)</string>
<string name="invalid_tag">%s 标签无效:%s</string>
</resources> </resources>