From 17df392ae4e3ac1233e40a046e5ddf5840976b81 Mon Sep 17 00:00:00 2001 From: pppscn <35696959@qq.com> Date: Thu, 18 Aug 2022 15:45:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A`=E9=A3=9E=E4=B9=A6?= =?UTF-8?q?=E4=BC=81=E4=B8=9A=E5=BA=94=E7=94=A8`=E5=8F=91=E9=80=81?= =?UTF-8?q?=E9=80=9A=E9=81=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- README_en.md | 2 +- .../sms/forwarder/database/entity/Sender.kt | 2 + .../entity/result/FeishuAppResult.kt | 11 + .../entity/setting/FeishuAppSetting.kt | 21 ++ .../sms/forwarder/fragment/SendersFragment.kt | 2 + .../fragment/senders/FeishuAppFragment.kt | 246 ++++++++++++++++ .../fragment/senders/FeishuFragment.kt | 2 +- .../idormy/sms/forwarder/utils/Constants.kt | 2 + .../idormy/sms/forwarder/utils/SendUtils.kt | 4 + .../forwarder/utils/sender/FeishuAppUtils.kt | 163 +++++++++++ .../main/res/drawable/icon_feishu_app.webp | Bin 0 -> 1822 bytes .../layout/fragment_senders_feishu_app.xml | 268 ++++++++++++++++++ app/src/main/res/values-en/strings.xml | 11 +- app/src/main/res/values/strings.xml | 17 +- 15 files changed, 741 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/com/idormy/sms/forwarder/entity/result/FeishuAppResult.kt create mode 100644 app/src/main/java/com/idormy/sms/forwarder/entity/setting/FeishuAppSetting.kt create mode 100644 app/src/main/java/com/idormy/sms/forwarder/fragment/senders/FeishuAppFragment.kt create mode 100644 app/src/main/java/com/idormy/sms/forwarder/utils/sender/FeishuAppUtils.kt create mode 100644 app/src/main/res/drawable/icon_feishu_app.webp create mode 100644 app/src/main/res/layout/fragment_senders_feishu_app.xml diff --git a/README.md b/README.md index 886b9be7..67038fae 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ 短信转发器——不仅只转发短信,备用机必备神器! -监控Android手机短信、来电、APP通知,并根据指定规则转发到其他手机:钉钉群自定义机器人、钉钉企业内机器人、企业微信群机器人、飞书机器人、企业微信应用消息、邮箱、bark、webhook、Telegram机器人、Server酱、PushPlus、手机短信等。 +监控Android手机短信、来电、APP通知,并根据指定规则转发到其他手机:钉钉群自定义机器人、钉钉企业内机器人、企业微信群机器人、企业微信应用消息、飞书群机器人、飞书企业应用、邮箱、bark、webhook、Telegram机器人、Server酱、PushPlus、手机短信等。 包括主动控制服务端与客户端,让你轻松远程发短信、查短信、查通话、查话簿、查电量等。(V3.0 新增) diff --git a/README_en.md b/README_en.md index 918d2642..2388a1c4 100644 --- a/README_en.md +++ b/README_en.md @@ -10,7 +10,7 @@ SmsForwarder - Not only forwarding text messages, but also a must-have for backup devices! -listens to SMS, incoming calls, and App notifications on Android mobile devices, and forward according to user defined rules to another App/device, including DingTalk, WeCom and WeCom Group Bot, Feishi Bot, E-mail, Bark, Webhook, Telegram Bot, ServerChan, PushPlus, SMS, etc. +listens to SMS, incoming calls, and App notifications on Android mobile devices, and forward according to user defined rules to another App/device, including DingTalk, WeCom and WeCom Group Bot, Feishu App and Feishu Group Bot, E-mail, Bark, Webhook, Telegram Bot, ServerChan, PushPlus, SMS, etc. Including active control of the server and client, allowing you to easily and remotely send text messages, check text messages, check calls, check the phone book, check the battery, etc. diff --git a/app/src/main/java/com/idormy/sms/forwarder/database/entity/Sender.kt b/app/src/main/java/com/idormy/sms/forwarder/database/entity/Sender.kt index cf97405d..11f44326 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/database/entity/Sender.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/database/entity/Sender.kt @@ -36,6 +36,7 @@ data class Sender( TYPE_GOTIFY -> R.drawable.icon_gotify TYPE_SMS -> R.drawable.icon_sms TYPE_DINGTALK_INNER_ROBOT -> R.drawable.icon_dingtalk_inner + TYPE_FEISHU_APP -> R.drawable.icon_feishu_app else -> R.drawable.icon_sms } @@ -56,6 +57,7 @@ data class Sender( TYPE_GOTIFY -> R.drawable.icon_gotify TYPE_SMS -> R.drawable.icon_sms TYPE_DINGTALK_INNER_ROBOT -> R.drawable.icon_dingtalk_inner + TYPE_FEISHU_APP -> R.drawable.icon_feishu_app else -> R.drawable.icon_sms } diff --git a/app/src/main/java/com/idormy/sms/forwarder/entity/result/FeishuAppResult.kt b/app/src/main/java/com/idormy/sms/forwarder/entity/result/FeishuAppResult.kt new file mode 100644 index 00000000..025004ef --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/entity/result/FeishuAppResult.kt @@ -0,0 +1,11 @@ +package com.idormy.sms.forwarder.entity.result + +data class FeishuAppResult( + var code: Long, + var msg: String, + //获取access_token返回 + var tenant_access_token: String?, + var expire: Long?, + //发送接口返回 + var content: String?, +) \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/entity/setting/FeishuAppSetting.kt b/app/src/main/java/com/idormy/sms/forwarder/entity/setting/FeishuAppSetting.kt new file mode 100644 index 00000000..6ae98b68 --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/entity/setting/FeishuAppSetting.kt @@ -0,0 +1,21 @@ +package com.idormy.sms.forwarder.entity.setting + +import com.idormy.sms.forwarder.R +import java.io.Serializable + +data class FeishuAppSetting( + var appId: String = "", + val appSecret: String = "", + val receiveId: String = "", + val msgType: String = "interactive", + val titleTemplate: String = "", +) : Serializable { + + fun getMsgTypeCheckId(): Int { + return if (msgType == null || msgType == "interactive") { + R.id.rb_msg_type_interactive + } else { + R.id.rb_msg_type_text + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/SendersFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/SendersFragment.kt index 3cca41c5..4861a9c6 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/fragment/SendersFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/SendersFragment.kt @@ -108,6 +108,7 @@ class SendersFragment : BaseFragment(), SenderPagingAda TYPE_PUSHPLUS -> PushplusFragment::class.java TYPE_GOTIFY -> GotifyFragment::class.java TYPE_DINGTALK_INNER_ROBOT -> DingtalkInnerRobotFragment::class.java + TYPE_FEISHU_APP -> FeishuAppFragment::class.java else -> DingtalkGroupRobotFragment::class.java } ).setNewActivity(true) @@ -132,6 +133,7 @@ class SendersFragment : BaseFragment(), SenderPagingAda TYPE_PUSHPLUS -> PushplusFragment::class.java TYPE_GOTIFY -> GotifyFragment::class.java TYPE_DINGTALK_INNER_ROBOT -> DingtalkInnerRobotFragment::class.java + TYPE_FEISHU_APP -> FeishuAppFragment::class.java else -> DingtalkGroupRobotFragment::class.java } ).setNewActivity(true) diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/senders/FeishuAppFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/senders/FeishuAppFragment.kt new file mode 100644 index 00000000..208acc53 --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/senders/FeishuAppFragment.kt @@ -0,0 +1,246 @@ +package com.idormy.sms.forwarder.fragment.senders + +import android.os.Looper +import android.text.TextUtils +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.EditText +import androidx.fragment.app.viewModels +import com.google.gson.Gson +import com.idormy.sms.forwarder.R +import com.idormy.sms.forwarder.core.BaseFragment +import com.idormy.sms.forwarder.database.AppDatabase +import com.idormy.sms.forwarder.database.entity.Sender +import com.idormy.sms.forwarder.database.viewmodel.BaseViewModelFactory +import com.idormy.sms.forwarder.database.viewmodel.SenderViewModel +import com.idormy.sms.forwarder.databinding.FragmentSendersFeishuAppBinding +import com.idormy.sms.forwarder.entity.MsgInfo +import com.idormy.sms.forwarder.entity.setting.FeishuAppSetting +import com.idormy.sms.forwarder.utils.* +import com.idormy.sms.forwarder.utils.sender.FeishuAppUtils +import com.jeremyliao.liveeventbus.LiveEventBus +import com.xuexiang.xaop.annotation.SingleClick +import com.xuexiang.xpage.annotation.Page +import com.xuexiang.xrouter.annotation.AutoWired +import com.xuexiang.xrouter.launcher.XRouter +import com.xuexiang.xui.utils.CountDownButtonHelper +import com.xuexiang.xui.widget.actionbar.TitleBar +import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction +import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog +import io.reactivex.SingleObserver +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import java.util.* + +@Page(name = "飞书企业应用") +@Suppress("PrivatePropertyName") +class FeishuAppFragment : BaseFragment(), View.OnClickListener { + + private val TAG: String = FeishuAppFragment::class.java.simpleName + var titleBar: TitleBar? = null + private val viewModel by viewModels { BaseViewModelFactory(context) } + private var mCountDownHelper: CountDownButtonHelper? = null + + @JvmField + @AutoWired(name = KEY_SENDER_ID) + var senderId: Long = 0 + + @JvmField + @AutoWired(name = KEY_SENDER_TYPE) + var senderType: Int = 0 + + @JvmField + @AutoWired(name = KEY_SENDER_CLONE) + var isClone: Boolean = false + + override fun initArgs() { + XRouter.getInstance().inject(this) + } + + override fun viewBindingInflate( + inflater: LayoutInflater, + container: ViewGroup, + ): FragmentSendersFeishuAppBinding { + return FragmentSendersFeishuAppBinding.inflate(inflater, container, false) + } + + override fun initTitle(): TitleBar? { + titleBar = super.initTitle()!!.setImmersive(false).setTitle(R.string.feishu_app) + return titleBar + } + + /** + * 初始化控件 + */ + override fun initViews() { + //测试按钮增加倒计时,避免重复点击 + mCountDownHelper = CountDownButtonHelper(binding!!.btnTest, SettingUtils.requestTimeout) + mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener { + override fun onCountDown(time: Int) { + binding!!.btnTest.text = String.format(getString(R.string.seconds_n), time) + } + + override fun onFinished() { + binding!!.btnTest.text = getString(R.string.test) + } + }) + + //新增 + if (senderId <= 0) { + titleBar?.setSubTitle(getString(R.string.add_sender)) + binding!!.btnDel.setText(R.string.discard) + return + } + + //编辑 + binding!!.btnDel.setText(R.string.del) + AppDatabase.getInstance(requireContext()) + .senderDao() + .get(senderId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : SingleObserver { + override fun onSubscribe(d: Disposable) {} + + override fun onError(e: Throwable) { + e.printStackTrace() + } + + override fun onSuccess(sender: Sender) { + if (isClone) { + titleBar?.setSubTitle(getString(R.string.clone_sender) + ": " + sender.name) + binding!!.btnDel.setText(R.string.discard) + } else { + titleBar?.setSubTitle(getString(R.string.edit_sender) + ": " + sender.name) + } + binding!!.etName.setText(sender.name) + binding!!.sbEnable.isChecked = sender.status == 1 + val settingVo = Gson().fromJson(sender.jsonSetting, FeishuAppSetting::class.java) + Log.d(TAG, settingVo.toString()) + if (settingVo != null) { + binding!!.etAppId.setText(settingVo.appId) + binding!!.etAppSecret.setText(settingVo.appSecret) + binding!!.etUserId.setText(settingVo.receiveId) + binding!!.rgMsgType.check(settingVo.getMsgTypeCheckId()) + binding!!.etTitleTemplate.setText(settingVo.titleTemplate) + } + } + }) + } + + override fun initListeners() { + binding!!.btInsertSender.setOnClickListener(this) + binding!!.btInsertExtra.setOnClickListener(this) + binding!!.btInsertTime.setOnClickListener(this) + binding!!.btInsertDeviceName.setOnClickListener(this) + binding!!.btnTest.setOnClickListener(this) + binding!!.btnDel.setOnClickListener(this) + binding!!.btnSave.setOnClickListener(this) + LiveEventBus.get(KEY_SENDER_TEST, String::class.java).observe(this) { mCountDownHelper?.finish() } + } + + @SingleClick + override fun onClick(v: View) { + try { + val etTitleTemplate: EditText = binding!!.etTitleTemplate + when (v.id) { + R.id.bt_insert_sender -> { + CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_from)) + return + } + R.id.bt_insert_extra -> { + CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_card_slot)) + return + } + R.id.bt_insert_time -> { + CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_receive_time)) + return + } + R.id.bt_insert_device_name -> { + CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_device_name)) + return + } + R.id.btn_test -> { + mCountDownHelper?.start() + Thread { + try { + val settingVo = checkSetting() + Log.d(TAG, settingVo.toString()) + val msgInfo = MsgInfo("sms", getString(R.string.test_phone_num), getString(R.string.test_sender_sms), Date(), getString(R.string.test_sim_info)) + FeishuAppUtils.sendMsg(settingVo, msgInfo) + } catch (e: Exception) { + e.printStackTrace() + if (Looper.myLooper() == null) Looper.prepare() + XToastUtils.error(e.message.toString()) + Looper.loop() + } + LiveEventBus.get(KEY_SENDER_TEST, String::class.java).post("finish") + }.start() + return + } + R.id.btn_del -> { + if (senderId <= 0 || isClone) { + popToBack() + return + } + + MaterialDialog.Builder(requireContext()) + .title(R.string.delete_sender_title) + .content(R.string.delete_sender_tips) + .positiveText(R.string.lab_yes) + .negativeText(R.string.lab_no) + .onPositive { _: MaterialDialog?, _: DialogAction? -> + viewModel.delete(senderId) + XToastUtils.success(R.string.delete_sender_toast) + popToBack() + } + .show() + return + } + R.id.btn_save -> { + val name = binding!!.etName.text.toString().trim() + if (TextUtils.isEmpty(name)) { + throw Exception(getString(R.string.invalid_name)) + } + + val status = if (binding!!.sbEnable.isChecked) 1 else 0 + val settingVo = checkSetting() + if (isClone) senderId = 0 + val senderNew = Sender(senderId, senderType, name, Gson().toJson(settingVo), status) + Log.d(TAG, senderNew.toString()) + + viewModel.insertOrUpdate(senderNew) + XToastUtils.success(R.string.tipSaveSuccess) + popToBack() + return + } + } + } catch (e: Exception) { + XToastUtils.error(e.message.toString()) + e.printStackTrace() + } + } + + private fun checkSetting(): FeishuAppSetting { + val appId = binding!!.etAppId.text.toString().trim() + val appSecret = binding!!.etAppSecret.text.toString().trim() + val receiveId = binding!!.etUserId.text.toString().trim() + if (TextUtils.isEmpty(appId) || TextUtils.isEmpty(appSecret) || TextUtils.isEmpty(receiveId)) { + throw Exception(getString(R.string.invalid_feishu_app_parameter)) + } + + val msgType = if (binding!!.rgMsgType.checkedRadioButtonId == R.id.rb_msg_type_interactive) "interactive" else "text" + val title = binding!!.etTitleTemplate.text.toString().trim() + + return FeishuAppSetting(appId, appSecret, receiveId, msgType, title) + } + + override fun onDestroyView() { + if (mCountDownHelper != null) mCountDownHelper!!.recycle() + super.onDestroyView() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/senders/FeishuFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/senders/FeishuFragment.kt index a74d0671..2132e19e 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/fragment/senders/FeishuFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/senders/FeishuFragment.kt @@ -35,7 +35,7 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import java.util.* -@Page(name = "飞书机器人") +@Page(name = "飞书群机器人") @Suppress("PrivatePropertyName") class FeishuFragment : BaseFragment(), View.OnClickListener { diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/Constants.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/Constants.kt index d7666a59..3380c651 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/utils/Constants.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/Constants.kt @@ -161,6 +161,7 @@ const val TYPE_FEISHU = 9 const val TYPE_PUSHPLUS = 10 const val TYPE_GOTIFY = 11 const val TYPE_DINGTALK_INNER_ROBOT = 12 +const val TYPE_FEISHU_APP = 13 var SENDER_FRAGMENT_LIST = listOf( PageInfo(getString(R.string.dingtalk_robot), "com.idormy.sms.forwarder.fragment.senders.DingtalkGroupRobotFragment", "{\"\":\"\"}", CoreAnim.slide, R.drawable.icon_dingtalk), PageInfo(getString(R.string.email), "com.idormy.sms.forwarder.fragment.senders.EmailFragment", "{\"\":\"\"}", CoreAnim.slide, R.drawable.icon_email), @@ -175,6 +176,7 @@ var SENDER_FRAGMENT_LIST = listOf( PageInfo(getString(R.string.pushplus), "com.idormy.sms.forwarder.fragment.senders.PushplusFragment", "{\"\":\"\"}", CoreAnim.slide, R.drawable.icon_pushplus), PageInfo(getString(R.string.gotify), "com.idormy.sms.forwarder.fragment.senders.GotifyFragment", "{\"\":\"\"}", CoreAnim.slide, R.drawable.icon_gotify), PageInfo(getString(R.string.dingtalk_inner_robot), "com.idormy.sms.forwarder.fragment.senders.DingtalkInnerRobotFragment", "{\"\":\"\"}", CoreAnim.slide, R.drawable.icon_dingtalk_inner), + PageInfo(getString(R.string.feishu_app), "com.idormy.sms.forwarder.fragment.senders.FeishuAppFragment", "{\"\":\"\"}", CoreAnim.slide, R.drawable.icon_feishu_app), ) //前台服务 diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/SendUtils.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/SendUtils.kt index 933ab0e7..88cd4936 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/utils/SendUtils.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/SendUtils.kt @@ -123,6 +123,10 @@ object SendUtils { val settingVo = Gson().fromJson(sender.jsonSetting, DingtalkInnerRobotSetting::class.java) DingtalkInnerRobotUtils.sendMsg(settingVo, msgInfo, rule, logId) } + TYPE_FEISHU_APP -> { + val settingVo = Gson().fromJson(sender.jsonSetting, FeishuAppSetting::class.java) + FeishuAppUtils.sendMsg(settingVo, msgInfo, rule, logId) + } else -> { updateLogs(logId, 0, "未知发送通道") } diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/sender/FeishuAppUtils.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/sender/FeishuAppUtils.kt new file mode 100644 index 00000000..10eb5d07 --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/sender/FeishuAppUtils.kt @@ -0,0 +1,163 @@ +package com.idormy.sms.forwarder.utils.sender + +import android.text.TextUtils +import android.util.Log +import com.google.gson.Gson +import com.idormy.sms.forwarder.R +import com.idormy.sms.forwarder.database.entity.Rule +import com.idormy.sms.forwarder.entity.MsgInfo +import com.idormy.sms.forwarder.entity.result.FeishuAppResult +import com.idormy.sms.forwarder.entity.setting.FeishuAppSetting +import com.idormy.sms.forwarder.utils.MMKVUtils +import com.idormy.sms.forwarder.utils.SendUtils +import com.idormy.sms.forwarder.utils.SettingUtils +import com.xuexiang.xhttp2.XHttp +import com.xuexiang.xhttp2.cache.model.CacheMode +import com.xuexiang.xhttp2.callback.SimpleCallBack +import com.xuexiang.xhttp2.exception.ApiException +import com.xuexiang.xui.utils.ResUtils.getString + +//飞书企业应用 +@Suppress("PrivatePropertyName", "UNUSED_PARAMETER") +class FeishuAppUtils private constructor() { + companion object { + + private val TAG: String = FeishuAppUtils::class.java.simpleName + + fun sendMsg( + setting: FeishuAppSetting, + msgInfo: MsgInfo, + rule: Rule?, + logId: Long?, + ) { + + val accessToken: String? = MMKVUtils.getString("feishu_access_token_" + setting.appId, "") + val expiresIn: Long = MMKVUtils.getLong("feishu_expires_in_" + setting.appId, 0L) + if (!TextUtils.isEmpty(accessToken) && expiresIn > System.currentTimeMillis()) { + return sendTextMsg(setting, msgInfo, rule, logId) + } + + val requestUrl = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal" + Log.d(TAG, "requestUrl:$requestUrl") + + val msgMap: MutableMap = mutableMapOf() + msgMap["app_id"] = setting.appId + msgMap["app_secret"] = setting.appSecret + val requestMsg: String = Gson().toJson(msgMap) + Log.i(TAG, "requestMsg:$requestMsg") + + XHttp.post(requestUrl) + .upJson(requestMsg) + .keepJson(true) + .ignoreHttpsCert() + .timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s + .cacheMode(CacheMode.NO_CACHE) + .timeStamp(true) + .execute(object : SimpleCallBack() { + + override fun onError(e: ApiException) { + Log.e(TAG, e.detailMessage) + SendUtils.updateLogs(logId, 0, e.displayMessage) + } + + override fun onSuccess(response: String) { + Log.i(TAG, response) + + val resp = Gson().fromJson(response, FeishuAppResult::class.java) + if (!TextUtils.isEmpty(resp.tenant_access_token)) { + MMKVUtils.put("feishu_access_token_" + setting.appId, resp.tenant_access_token) + MMKVUtils.put("feishu_expires_in_" + setting.appId, System.currentTimeMillis() + ((resp.expire ?: 7010) - 120) * 1000L) //提前2分钟过期 + sendTextMsg(setting, msgInfo, rule, logId) + } else { + SendUtils.updateLogs(logId, 0, String.format(getString(R.string.request_failed_tips), response)) + } + } + + }) + + } + + //发送文本消息 + private fun sendTextMsg( + setting: FeishuAppSetting, + msgInfo: MsgInfo, + rule: Rule?, + logId: Long?, + ) { + val requestUrl = "https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=user_id" + Log.d(TAG, "requestUrl:$requestUrl") + + val content: String = if (rule != null) { + msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace) + } else { + msgInfo.getContentForSend(SettingUtils.smsTemplate.toString()) + } + + val msgContent = if ("interactive" == setting.msgType) { + val title = if (rule != null) { + msgInfo.getTitleForSend(setting.titleTemplate, rule.regexReplace) + } else { + msgInfo.getTitleForSend(setting.titleTemplate) + } + "{\"elements\":[{\"tag\":\"markdown\",\"content\":\"**[{{MSG_TITLE}}]({{MSG_URL}})**\\n --------------\\n{{MSG_CONTENT}}\"}]}".trimIndent().replace("{{MSG_TITLE}}", jsonInnerStr(title)) + .replace("{{MSG_URL}}", jsonInnerStr("https://github.com/pppscn/SmsForwarder")) + .replace("{{MSG_CONTENT}}", jsonInnerStr(content)) + } else { + "{\"text\":\"{{MSG_CONTENT}}\"}".trimIndent().replace("{{MSG_CONTENT}}", jsonInnerStr(content)) + } + + val textMsgMap: MutableMap = mutableMapOf() + textMsgMap["receive_id"] = setting.receiveId + textMsgMap["msg_type"] = setting.msgType + textMsgMap["content"] = msgContent + + val requestMsg: String = Gson().toJson(textMsgMap) + Log.i(TAG, "requestMsg:$requestMsg") + + XHttp.post(requestUrl) + .upJson(requestMsg) + .headers("Authorization", "Bearer " + MMKVUtils.getString("feishu_access_token_" + setting.appId, "")) + .keepJson(true) + //.ignoreHttpsCert() + .timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s + .cacheMode(CacheMode.NO_CACHE) + .retryCount(SettingUtils.requestRetryTimes) //超时重试的次数 + .retryDelay(SettingUtils.requestDelayTime) //超时重试的延迟时间 + .retryIncreaseDelay(SettingUtils.requestDelayTime) //超时重试叠加延时 + .timeStamp(true) + .execute(object : SimpleCallBack() { + + override fun onError(e: ApiException) { + Log.e(TAG, e.detailMessage) + SendUtils.updateLogs(logId, 0, e.displayMessage) + } + + override fun onSuccess(response: String) { + Log.i(TAG, response) + //Log.d(TAG, "tlsVersion=" + response.handshake().tlsVersion()) + //Log.d(TAG, "cipherSuite=" + response.handshake().cipherSuite().toString()) + + val resp = Gson().fromJson(response, FeishuAppResult::class.java) + if (resp.code == 0L) { + SendUtils.updateLogs(logId, 2, response) + } else { + SendUtils.updateLogs(logId, 0, response) + } + } + + }) + } + + fun sendMsg(setting: FeishuAppSetting, msgInfo: MsgInfo) { + sendMsg(setting, msgInfo, null, null) + } + + private fun jsonInnerStr(string: String?): String { + if (string == null) return "null" + + val jsonStr: String = Gson().toJson(string) + return if (jsonStr.length >= 2) jsonStr.substring(1, jsonStr.length - 1) else jsonStr + } + + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/icon_feishu_app.webp b/app/src/main/res/drawable/icon_feishu_app.webp new file mode 100644 index 0000000000000000000000000000000000000000..e5b554afc37e819f8c8618e8a69d216d228193c4 GIT binary patch literal 1822 zcmV+(2jTcqNk&E%2LJ$9MM6+kP&il$0000G0001w0055w06|PpNb~^!01Y3*xNRFr zBiVE8J{Q}6xl=<#{ht8*rIuvlNg5vdWK!b+&Y6ZIREF;EpoU{ySWgIX z7<@lmyK>MuG<{0bubzMOmd_8)J;0}Iy7t~Mc*` zpTCZ(ha%FUYCU`LF2o$HTK7?HK`~oZ>*1|cDuYkg5PK+SD;~S>#K#QDUq*2+y{%9` z)7ArUWpOyNxJcEjP;l4xEJANg4_SsWyNr5QR-NRU|6I z|NsC0|NsC0g2)n48AP*oaVqDPW5Mkbiy|%$x;fm(nZw1I+19iA;L`hxDDB9ukU{3& zoIVsoL1Jh2!_LI|kXmc^Zig6(5xcm%f5#w)`km3qJHbE}Ktcz1kM92%GDxXvznr?Y zA4McFcklS&e%m3VQjFcv$7j#(+ul;cv09lOA3J&MV+hQXtm*^-4JX&%dg{cM zjh1)FDvK670n1AfP49Q@VC)C)nMr2RU9 zG(YJ)i6In}lpv_l2*QyVQbE{!YSZx>J62FQAp8UX08k$QodGJ40FVGakv^G8C8HuC zDzVKduo4MCZ~1768e`+{yo9{x8%LAzBPpa`d z()57Cu%5<`*NUu$prD`Ob`gP}bY-HW^tl=XMgLxa0RH@5W$TN7&$zyrPG(d4-_wRW zuaDVv9Ah{7!z3HFCg5?8d5(Km4WG{Ca6sO4i^057nxvRHfFuFC>GTR}ftw=!SNak7p^zPF+^qi11yA#23Z9 zLCKh$I$>d*{L2A+hyMh`y2Uo|WGYM?6ghSr>Z^agRa)ZhxnmS#AH%w13^1SPBTDM`f-t4?S1Ww97tY(29ZWy3pph1Wn z>&jyvuD$)ai2zY%ujp_8<$0H_)^bc}3*TRrnAUPY?%7x)d_?qy0G!i5BX_0u zp^u=wanELl=HfhS*C^g&yisMLD4sDJy5KJqajhnnG04||V`g+h{X;S>BCRwQa8$6W zyyyg=xnk+%y{lp}-R0KC=C(vMpNX#Ia^J{71JZ>qJs#6A|e*~8YL2Qw24hv`B*4G0L?A@X?Y~^?E0A{WIUoPXs=PS0QE=#?3^d6w3 z^Ka$123EA7>me}vHg1-ih0sov+L+KBXBMV}@QJ_fNm3gH6-=@b>=He+jVe}bN}K~1 zjYk?%(Bu`G*@Ge6lDhuu&3->I)N^_};@LB`rD^^#9u%-kJb)Ih62|7!e^6{171%37 zXZ`=UFiX3s5sp_w$i1r|VF> zizxHCEqCWuu_nZ$0|9}U$Ug6YTLq2oTF*tRo0#1d!r~-T48F~)(!g@kf=3D(oa(0+ z&R>Aa1+zUF5v(W%uNib54h+tO&Wp$0J?&QM~7x+TFbi1pJsvctlqpn&Njm( z78a-r6eeKDcD{lG3l`oR49l#xZ&Vfy$EzvKVzE(@TJ^-AE2jFg)m1w)be}j zRd$y}s(wHLwX$a!X7Rw^#43l&3JuBaMnyygLxEfY^^?Sm%|w|@d%|P%M*4@~y7Pe1 z_6NK7@<1m|l9gkT*4=G`xVGogRGaP*q6LxL|du_JEh{ M5Y_bQp#AUw03%(Ei~s-t literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/fragment_senders_feishu_app.xml b/app/src/main/res/layout/fragment_senders_feishu_app.xml new file mode 100644 index 00000000..6630122a --- /dev/null +++ b/app/src/main/res/layout/fragment_senders_feishu_app.xml @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index ce8547fc..dd482196 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -485,6 +485,7 @@ Telegram Bot SMS FeiShu Bot + FeiShu App PushPlus Gotify 0 @@ -892,20 +893,24 @@ Markdown IP address/broadcast address, eg. 192.168.1.255 Malformed IP address, eg. 192.168.168.168 - ^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])$ + ^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])$ Network card mac, eg. AA:BB:CC:DD:EE:FF The network card mac format is incorrect, eg. AA:BB:CC:DD:EE:FF - ^((([a-fA-F0-9]{2}:){5})|(([a-fA-F0-9]{2}-){5}))[a-fA-F0-9]{2}$ + ^((([a-fA-F0-9]{2}:){5})|(([a-fA-F0-9]{2}-){5}))[a-fA-F0-9]{2}$ IP MAC There is no history record, WOL will be added automatically after successful sending WOL is generally sent over port 7 or port 9 Port number value range: 1~65535 - ^([0-9]|[1-9]\\d|[1-9]\\d{2}|[1-9]\\d{3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$ + ^([0-9]|[1-9]\\d|[1-9]\\d{2}|[1-9]\\d{3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$ Select Dir Web Client Restarting HttpServer Download and unzip to:\n%s Root Directory:\n%s Select WebClient Directory + AppId/AppSecret/UserId cannot be empty + App ID + App Secret + User ID diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e967bf4b..de570417 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -266,7 +266,7 @@ 设置Server酱·Turbo版 设置Telegram机器人 设置SMS - 设置飞书机器人 + 设置飞书群机器人 设置PushPlus 设置Gotify 19999999999 @@ -485,7 +485,8 @@ Server酱·Turbo版 Telegram机器人 手机短信 - 飞书机器人 + 飞书群机器人 + 飞书企业应用 PushPlus Gotify 0 @@ -771,7 +772,7 @@ HttpServer已停止 服务端设置 建议启用签名设置、点击“随机”自动生成并复制到剪贴板 - 复制 + 复制 随机生成 签名密钥 已生成密钥,并复制到剪贴板 @@ -893,20 +894,24 @@ Markdown类型 可选,主机IP/广播地址,例如:192.168.1.255 IP地址格式错误,例如:192.168.168.168 - ^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])$ + ^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])$ 必填,网卡mac,例如:AA:BB:CC:DD:EE:FF 网卡mac格式错误,例如:AA:BB:CC:DD:EE:FF - ^((([a-fA-F0-9]{2}:){5})|(([a-fA-F0-9]{2}-){5}))[a-fA-F0-9]{2}$ + ^((([a-fA-F0-9]{2}:){5})|(([a-fA-F0-9]{2}-){5}))[a-fA-F0-9]{2}$ 主机IP/广播地址 网卡MAC 暂无历史记录,WOL发送成功后自动加入 可选,WOL一般透过端口7或端口9进行发送 端口号取值范围:1~65535 - ^([0-9]|[1-9]\\d|[1-9]\\d{2}|[1-9]\\d{3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$ + ^([0-9]|[1-9]\\d|[1-9]\\d{2}|[1-9]\\d{3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$ 选择目录 Web客户端 正在重启HttpServer 请先下载Web客户端并解压到:\n%s 根目录:\n%s 选择Web客户端目录 + AppId/AppSecret/UserId都不能为空 + App ID + App Secret + User ID