This commit is contained in:
2026-05-11 14:32:27 +08:00
parent c6fbcec2d6
commit 473e2c2d89
11 changed files with 165 additions and 137 deletions

View File

@@ -58,7 +58,7 @@ object Constant {
SPUtils.getInstance().put(weworkCorpName + "weworkMP", value)
}
var encryptType: Int = SPUtils.getInstance().getInt("encryptType", 0)
var autoReply: Int = SPUtils.getInstance().getInt("autoReply", 1)
var autoReply: Int = SPUtils.getInstance().getInt("autoReply", 0)
var groupStrict: Boolean
get() = SPUtils.getInstance().getBoolean("groupStrict", false)
set(value) = SPUtils.getInstance().put("groupStrict", value)

View File

@@ -1,56 +0,0 @@
package org.yameida.worktool
import com.blankj.utilcode.util.TimeUtils
import org.yameida.worktool.model.WeworkMessageBean
import org.yameida.worktool.service.MyLooper
import org.yameida.worktool.service.WeworkController
import org.yameida.worktool.service.WeworkLoopImpl
import org.yameida.worktool.service.getRoot
import org.yameida.worktool.utils.AccessibilityUtil
import java.util.*
/**
* 示例
*/
object Demo {
fun test(flag: Boolean) {
if (!flag) return
MyLooper.getInstance().removeCallbacksAndMessages(null)
//打印当前视图树
// AccessibilityUtil.printNodeClazzTree(getRoot())
}
fun test2(name: String) {
val time = TimeUtils.date2String(Date(), "MMddHHmm")
val groupName = "测试群$time"
val json = """
{
"socketType":2,
"list":[
{
"type":203,
"titleList":[
"$name"
],
"receivedContent":"你好~我是机器人,你可以@我和我聊天你也可以通过API文档来让我发送消息或完成建群等任务。接口文档https://www.apifox.cn/apidoc/project-1035094/api-23520034"
},
{
"type": 206,
"groupName": "$groupName",
"selectList": [
"$name",
"甲仑"
],
"groupAnnouncement": "(自动拉群+自动群公告) WorkTool欢迎大家~WorkTool管家是机器人有问题可以在QQ群反馈~@我可以聊天~"
}
]
}
""".trimIndent()
MyLooper.onMessage(null, json)
}
}

View File

@@ -22,6 +22,7 @@ import org.yameida.worktool.utils.*
import org.yameida.worktool.utils.capture.MediaProjectionHolder
import org.yameida.worktool.utils.envcheck.CheckHook
import org.yameida.worktool.utils.envcheck.CheckRoot
import kotlin.random.Random
class ListenActivity : AppCompatActivity() {
@@ -59,6 +60,7 @@ class ListenActivity : AppCompatActivity() {
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(openWsReceiver)
FloatWindowHelper.hideWindow()
}
override fun onResume() {
@@ -75,16 +77,11 @@ class ListenActivity : AppCompatActivity() {
iv_settings.setOnClickListener {
SettingsActivity.enterActivity(this)
}
et_channel.setText(Constant.robotId)
bt_save.setOnClickListener {
val channel = et_channel.text.toString().trim()
Constant.robotId = channel
ToastUtils.showLong("保存成功")
sendBroadcast(Intent(Constant.WEWORK_NOTIFY).apply {
putExtra("type", "modify_channel")
})
HttpUtil.getMyConfig(toast = false)
MobclickAgent.onProfileSignIn(channel)
tv_channel.text = Constant.robotId.ifBlank { "未生成" }
bt_reset_channel.setOnClickListener {
val channel = generateDefaultRobotId()
tv_channel.text = channel
saveChannel(channel, toast = "重置成功")
}
tv_host.text = Constant.host
tv_host.setOnClickListener {
@@ -185,13 +182,35 @@ class ListenActivity : AppCompatActivity() {
}
private fun initData() {
Constant.robotId = "2038521871751249921"
Constant.host = "ws://8.166.130.74:18680"
// 链接号为空时自动生成一次并持久化,避免每次启动覆盖用户手动保存的链接号
if (Constant.robotId.isBlank()) {
Constant.robotId = generateDefaultRobotId()
tv_channel.text = Constant.robotId
}
// HttpUtil.checkUpdate()
HttpUtil.getMyConfig(toast = false)
CacheUtil.autoDelete()
}
private fun generateDefaultRobotId(): String {
val suffix = Random.nextInt(1000, 9999)
return "${System.currentTimeMillis()}$suffix"
}
private fun saveChannel(channel: String, toast: String) {
if (channel.isBlank()) {
ToastUtils.showLong("链接号不能为空")
return
}
Constant.robotId = channel
ToastUtils.showLong(toast)
sendBroadcast(Intent(Constant.WEWORK_NOTIFY).apply {
putExtra("type", "modify_channel")
})
HttpUtil.getMyConfig(toast = false)
MobclickAgent.onProfileSignIn(channel)
}
private fun initNotification() {
if (!Constant.enableMediaProject) {
return
@@ -206,7 +225,7 @@ class ListenActivity : AppCompatActivity() {
}, Context.BIND_AUTO_CREATE)
//开启屏幕录制权限
if (MediaProjectionHolder.mMediaProjection == null) {
bt_save.postDelayed({
window.decorView.postDelayed({
fastStartActivity(this, GetScreenShotActivity::class.java)
}, 1000)
}
@@ -282,8 +301,8 @@ class ListenActivity : AppCompatActivity() {
.setNegativeButton("", null)
.setPositiveButton("", null)
val show = positiveButton.show()
bt_save.postDelayed({ show.dismiss() }, 5000)
bt_save.postDelayed({
window.decorView.postDelayed({ show.dismiss() }, 5000)
window.decorView.postDelayed({
packageManager.getLaunchIntentForPackage(Constant.PACKAGE_NAMES)?.apply {
this.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(this)

View File

@@ -1,7 +1,6 @@
package org.yameida.worktool.service
import com.blankj.utilcode.util.*
import org.yameida.worktool.Demo
import org.yameida.worktool.annotation.RequestMapping
import org.yameida.worktool.model.ExecCallbackBean
import org.yameida.worktool.model.WeworkMessageBean
@@ -159,7 +158,6 @@ object WeworkController {
@RequestMapping
fun test(message: WeworkMessageBean? = null) {
LogUtils.d(message)
Demo.test(true)
}
/**
@@ -640,4 +638,4 @@ object WeworkController {
return WeworkGetImpl.getCorpList(message)
}
}
}

View File

@@ -7,7 +7,6 @@ import androidx.core.text.isDigitsOnly
import com.blankj.utilcode.util.*
import com.hjq.toast.ToastUtils
import org.yameida.worktool.Constant
import org.yameida.worktool.Demo
import org.yameida.worktool.MyApplication
import org.yameida.worktool.activity.GetScreenShotActivity
import org.yameida.worktool.model.WeworkMessageBean
@@ -176,8 +175,7 @@ object WeworkLoopImpl {
val nameList = passFriendRequest()
if (nameList.isEmpty())
break
//todo 可自定义执行任务
// Demo.test2(nameList[0])
// todo 可自定义执行任务
}
}
return true
@@ -1020,4 +1018,4 @@ object WeworkLoopImpl {
return false
}
}
}

View File

@@ -13,11 +13,9 @@ import okhttp3.Response
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import org.yameida.worktool.Constant
import org.yameida.worktool.Demo
import org.yameida.worktool.observer.MultiFileObserver
import org.yameida.worktool.utils.*
import java.lang.Exception
import kotlin.concurrent.thread
/**
* 企业微信辅助服务
@@ -47,8 +45,6 @@ class WeworkService : AccessibilityService() {
MyLooper.init()
//初始化图片接收
initObserver()
//开发者可以在这里添加测试代码 启动时调用一次
thread { Demo.test(AppUtils.isAppDebug()) }
//监听是否修改链接号并重新长连接
registerReceiver(object : BroadcastReceiver() {
@@ -116,6 +112,7 @@ class WeworkService : AccessibilityService() {
//隐藏软键盘模式
softKeyboardController.showMode = SHOW_MODE_AUTO
webSocketManager.close(1000, "service Destroy")
FloatWindowHelper.hideWindow()
}
inner class EchoWebSocketListener : WebSocketListener() {
@@ -172,4 +169,4 @@ class WeworkService : AccessibilityService() {
})
}
}
}
}

View File

@@ -31,6 +31,7 @@ import kotlin.concurrent.thread
object FloatWindowHelper {
var isPause = false
private var bound = false
fun showWindow() {
LogUtils.d("FloatWindowHelper.showWindow()")
@@ -39,7 +40,23 @@ object FloatWindowHelper {
val app = Utils.getApp()
val intent = Intent(app, DefaultFloatService::class.java)
app.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
if (!bound) {
bound = app.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
}
}
fun hideWindow() {
LogUtils.d("FloatWindowHelper.hideWindow()")
val app = Utils.getApp()
FloatWindowManager.hide(DefaultFloatService::class.java)
if (bound) {
try {
app.unbindService(serviceConnection)
} catch (ignore: Exception) {
}
bound = false
}
app.stopService(Intent(app, DefaultFloatService::class.java))
}
/**
@@ -143,7 +160,8 @@ object FloatWindowHelper {
override fun onServiceDisconnected(name: ComponentName?) {
LogUtils.i("DefaultFloatService 服务断开")
bound = false
}
}
}
}

View File

@@ -22,32 +22,37 @@ import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
public class WebSocketManager {
public static final String HEARTBEAT = "{\"type\":" + WeworkMessageBean.HEART_BEAT + "}";
private static final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
private static final OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.pingInterval(25, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
public static Map<String, WebSocketManager> webSocketManager = new ConcurrentHashMap<>();
private static final int reconnectInt = 5000; //毫秒
private static final long heartBeatRate = 5; //秒
private Map<String, Long> messageIdMap = new ConcurrentHashMap<>();
private ScheduledFuture task;
private WebSocket socket;
private String url;
private WebSocketListener listener;
private boolean connecting = false;
private volatile boolean connecting = false;
private volatile boolean manuallyClosed = false;
private volatile boolean opened = false;
private long lastConnectedTime = 0L;
public WebSocketManager(String url, WebSocketListener listener) {
Log.e(url, "新建链接");
this.url = url;
this.listener = listener;
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
this.socket = client.newWebSocket(request, listener);
socket.send("{\"td\":" + System.currentTimeMillis() + "}");
this.socket = client.newWebSocket(new Request.Builder().url(url).build(), innerListener);
webSocketManager.put(url, this);
task = heartCheckStart();
}
@@ -83,7 +88,7 @@ public class WebSocketManager {
public void send(WeworkMessageListBean msg, boolean log) {
String json = GsonUtils.toJson(msg);
boolean success = socket.send(json);
boolean success = socket != null && socket.send(json);
if (log && success)
LogUtils.d(url, json, "通讯消息发送成功!");
if (!success)
@@ -91,14 +96,19 @@ public class WebSocketManager {
}
public void send(String msg) {
boolean success = socket.send(msg);
boolean success = socket != null && socket.send(msg);
LogUtils.e(url, msg, (success ? "通讯消息发送成功!" : "通讯消息发送失败!"));
}
public void close(int code, String reason) {
task.cancel(true);
manuallyClosed = true;
if (task != null) {
task.cancel(true);
}
Log.e("url", "task 取消");
this.socket.close(code, reason);
if (this.socket != null) {
this.socket.close(code, reason);
}
Log.e(url, "链接关闭");
}
@@ -112,44 +122,25 @@ public class WebSocketManager {
}
public void reConnect() {
if (manuallyClosed || connecting) {
return;
}
connecting = true;
opened = false;
Log.e(url, "重连");
boolean isConnect = false;
int interval = reconnectInt;
while (true) {
try {
isConnect = connect();
if (isConnect) {
connecting = false;
break;
}
} catch (Exception e) {
e.printStackTrace();
}
try {
Thread.sleep(interval);
if (interval < 600000) {
interval *= 2;
}
} catch (InterruptedException e) {
e.printStackTrace();
try {
if (socket != null) {
socket.cancel();
}
} catch (Exception ignore) {
}
}
private boolean connect() {
WebSocket s = new OkHttpClient().newWebSocket(new Request.Builder().url(url).build(), listener);
if (s.send(WebSocketManager.HEARTBEAT)) {
this.socket = s;
s.send("{\"td\":" + System.currentTimeMillis() + "}");
return true;
}
return false;
socket = client.newWebSocket(new Request.Builder().url(url).build(), innerListener);
}
private ScheduledFuture heartCheckStart() {
lastConnectedTime = System.currentTimeMillis();
Runnable r = () -> {
if (manuallyClosed) return;
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
Log.d(url, "心跳检测" + df.format(new Date()));// new Date()为获取当前系统时间
if (!connecting && (socket == null || !socket.send(HEARTBEAT))) {
@@ -159,7 +150,7 @@ public class WebSocketManager {
reConnect();
//重连后刷新连接时间
lastConnectedTime = System.currentTimeMillis();
} else if (System.currentTimeMillis() % 1000 == 0) {
} else if (opened && System.currentTimeMillis() % 1000 == 0) {
socket.send("{\"td\":" + System.currentTimeMillis() + "}");
}
if (!Constant.INSTANCE.getEnableMediaProject()) {
@@ -176,4 +167,46 @@ public class WebSocketManager {
public static WebSocketManager getWebSocketManager(String id) {
return webSocketManager.get(id);
}
private final WebSocketListener innerListener = new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
opened = true;
connecting = false;
lastConnectedTime = System.currentTimeMillis();
listener.onOpen(webSocket, response);
}
@Override
public void onMessage(WebSocket webSocket, String text) {
listener.onMessage(webSocket, text);
}
@Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
listener.onMessage(webSocket, bytes);
}
@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
opened = false;
connecting = false;
listener.onClosing(webSocket, code, reason);
}
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
opened = false;
connecting = false;
listener.onClosed(webSocket, code, reason);
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
opened = false;
connecting = false;
WeworkController.INSTANCE.setEnableLoopRunning(false);
listener.onFailure(webSocket, t, response);
}
};
}

View File

@@ -326,11 +326,14 @@
android:textColor="@color/color_333333"
android:textSize="@dimen/setting_start_font_size" />
<EditText
android:id="@+id/et_channel"
<TextView
android:id="@+id/tv_channel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入申请的机器人ID"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:text="未生成"
android:textIsSelectable="true"
android:textColor="@color/color_999999"
android:textSize="@dimen/setting_end_font_size" />
@@ -344,17 +347,17 @@
android:orientation="vertical">
<Button
android:id="@+id/bt_save"
android:id="@+id/bt_reset_channel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="@drawable/comment_red_btn"
android:paddingStart="50dp"
android:paddingEnd="50dp"
android:textSize="18sp"
android:textStyle="bold"
android:paddingStart="42dp"
android:paddingEnd="42dp"
android:text="重置"
android:textColor="@color/white"
android:text="保存" />
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>