update root&xp检测提示;从外部群添加好友;修改好友信息;建群达到上限提示

This commit is contained in:
gallonyin
2022-11-10 11:22:53 +08:00
parent bd79538a9a
commit 01063dc6d3
10 changed files with 736 additions and 85 deletions

View File

@@ -19,6 +19,7 @@ object Constant {
val transformation = "AES/CBC/PKCS7Padding" val transformation = "AES/CBC/PKCS7Padding"
var encryptType = SPUtils.getInstance().getInt("encryptType", 1) var encryptType = SPUtils.getInstance().getInt("encryptType", 1)
var autoReply = SPUtils.getInstance().getInt("autoReply", 1) var autoReply = SPUtils.getInstance().getInt("autoReply", 1)
var groupStrict = false
var host: String var host: String
get() = SPUtils.getInstance().getString("host", DEFAULT_HOST) get() = SPUtils.getInstance().getString("host", DEFAULT_HOST)
set(value) { set(value) {

View File

@@ -21,6 +21,8 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yameida.worktool.utils.HostTestHelper import org.yameida.worktool.utils.HostTestHelper
import org.yameida.worktool.utils.PermissionHelper import org.yameida.worktool.utils.PermissionHelper
import org.yameida.worktool.utils.PermissionPageManagement import org.yameida.worktool.utils.PermissionPageManagement
import org.yameida.worktool.utils.envcheck.CheckHook
import org.yameida.worktool.utils.envcheck.CheckRoot
class ListenActivity : AppCompatActivity() { class ListenActivity : AppCompatActivity() {
@@ -87,7 +89,15 @@ class ListenActivity : AppCompatActivity() {
true true
} }
val version = "${AppUtils.getAppVersionName()} Android ${DeviceUtils.getSDKVersionName()} ${DeviceUtils.getManufacturer()} ${DeviceUtils.getModel()}" val version = "${AppUtils.getAppVersionName()} Android ${DeviceUtils.getSDKVersionName()} ${DeviceUtils.getManufacturer()} ${DeviceUtils.getModel()}"
val deviceRooted = CheckRoot.isDeviceRooted()
val hook = CheckHook.isHook(applicationContext)
if (hook) {
tv_version.text = "当前设备存在侵入代码,请勿在本设备使用本程序!!!"
} else if (deviceRooted) {
tv_version.text = "$version\n本设备已Root存在一定风险"
} else {
tv_version.text = version tv_version.text = version
}
val workVersionName = AppUtils.getAppInfo(Constant.PACKAGE_NAMES)?.versionName val workVersionName = AppUtils.getAppInfo(Constant.PACKAGE_NAMES)?.versionName
when (workVersionName) { when (workVersionName) {
null -> { null -> {
@@ -107,6 +117,8 @@ class ListenActivity : AppCompatActivity() {
} }
SPUtils.getInstance().put("appVersion", version) SPUtils.getInstance().put("appVersion", version)
SPUtils.getInstance().put("workVersion", workVersionName) SPUtils.getInstance().put("workVersion", workVersionName)
SPUtils.getInstance().put("deviceRooted", deviceRooted)
SPUtils.getInstance().put("hook", hook)
} }
private fun initAccessibility() { private fun initAccessibility() {
@@ -215,7 +227,7 @@ class ListenActivity : AppCompatActivity() {
if (text.matches("ws{1,2}://[^/]+.*".toRegex())) { if (text.matches("ws{1,2}://[^/]+.*".toRegex())) {
Constant.host = text Constant.host = text
tv_host.text = text tv_host.text = text
HostTestHelper.test() HostTestHelper.testWs()
commentDialog.dismiss() commentDialog.dismiss()
} else { } else {
ToastUtils.showLong("格式异常!") ToastUtils.showLong("格式异常!")

View File

@@ -32,6 +32,8 @@ public class WeworkMessageBean {
* 按手机号添加好友 ADD_FRIEND_BY_PHONE * 按手机号添加好友 ADD_FRIEND_BY_PHONE
* 展示群信息 SHOW_GROUP_INFO * 展示群信息 SHOW_GROUP_INFO
* 推送文件(网络图片视频和文件等) PUSH_FILE * 推送文件(网络图片视频和文件等) PUSH_FILE
* 解散群聊 DISMISS_GROUP
* 从外部群添加好友 ADD_FRIEND_BY_GROUP
* <p> * <p>
* 非操作类型 300 * 非操作类型 300
* 机器人普通日志记录 ROBOT_LOG * 机器人普通日志记录 ROBOT_LOG
@@ -66,6 +68,7 @@ public class WeworkMessageBean {
public static final int DELETED_ANTI_HARASSMENT_RULE = 217; public static final int DELETED_ANTI_HARASSMENT_RULE = 217;
public static final int PUSH_FILE = 218; public static final int PUSH_FILE = 218;
public static final int DISMISS_GROUP = 219; public static final int DISMISS_GROUP = 219;
public static final int ADD_FRIEND_BY_GROUP = 220;
public static final int ROBOT_LOG = 301; public static final int ROBOT_LOG = 301;
public static final int ROBOT_ERROR_LOG = 302; public static final int ROBOT_ERROR_LOG = 302;
@@ -287,6 +290,20 @@ public class WeworkMessageBean {
public int hashCode() { public int hashCode() {
return Objects.hash(phone, name, markName, markCorp, markExtra, tagList, newFriend, leavingMsg); return Objects.hash(phone, name, markName, markCorp, markExtra, tagList, newFriend, leavingMsg);
} }
@Override
public String toString() {
return "Friend{" +
"phone='" + phone + '\'' +
", name='" + name + '\'' +
", markName='" + markName + '\'' +
", markCorp='" + markCorp + '\'' +
", markExtra='" + markExtra + '\'' +
", tagList=" + tagList +
", newFriend=" + newFriend +
", leavingMsg='" + leavingMsg + '\'' +
'}';
}
} }
@Override @Override

View File

@@ -117,7 +117,7 @@ fun getRoot(ignoreCheck: Boolean): AccessibilityNodeInfo {
} }
} }
} }
sleep(1000) sleep(Constant.CHANGE_PAGE_INTERVAL)
} }
} }

View File

@@ -136,16 +136,19 @@ object MyLooper {
WeworkMessageBean.PUSH_OFFICE -> { WeworkMessageBean.PUSH_OFFICE -> {
WeworkController.pushOffice(message) WeworkController.pushOffice(message)
} }
WeworkMessageBean.PASS_ALL_FRIEND_REQUEST -> {
}
WeworkMessageBean.ADD_FRIEND_BY_PHONE -> {
WeworkController.addFriendByPhone(message)
}
WeworkMessageBean.PUSH_FILE -> { WeworkMessageBean.PUSH_FILE -> {
WeworkController.pushFile(message) WeworkController.pushFile(message)
} }
WeworkMessageBean.DISMISS_GROUP -> { WeworkMessageBean.DISMISS_GROUP -> {
WeworkController.dismissGroup(message) WeworkController.dismissGroup(message)
} }
WeworkMessageBean.PASS_ALL_FRIEND_REQUEST -> { WeworkMessageBean.ADD_FRIEND_BY_GROUP -> {
} WeworkController.addFriendByGroup(message)
WeworkMessageBean.ADD_FRIEND_BY_PHONE -> {
WeworkController.addFriendByPhone(message)
} }
WeworkMessageBean.SHOW_GROUP_INFO -> { WeworkMessageBean.SHOW_GROUP_INFO -> {
WeworkController.showGroupInfo(message) WeworkController.showGroupInfo(message)

View File

@@ -187,6 +187,22 @@ object WeworkController {
) )
} }
/**
* 从外部群添加好友
* @see WeworkMessageBean.ADD_FRIEND_BY_GROUP
* @param message#groupName 外部群
* @param message#friend 待添加用户
*/
@RequestMapping
fun addFriendByGroup(message: WeworkMessageBean): Boolean {
LogUtils.d("addFriendByGroup(): ${message.groupName} ${message.friend}")
return WeworkOperationImpl.addFriendByGroup(
message,
message.groupName,
message.friend
)
}
/** /**
* 推送微盘图片 * 推送微盘图片
* @see WeworkMessageBean.PUSH_MICRO_DISK_IMAGE * @see WeworkMessageBean.PUSH_MICRO_DISK_IMAGE
@@ -287,7 +303,7 @@ object WeworkController {
/** /**
* 按手机号添加好友 * 按手机号添加好友
* @see WeworkMessageBean.ADD_FRIEND_BY_PHONE * @see WeworkMessageBean.ADD_FRIEND_BY_PHONE
* @param message#friend 待添加用户列表 * @param message#friend 待添加用户
*/ */
@RequestMapping @RequestMapping
fun addFriendByPhone(message: WeworkMessageBean): Boolean { fun addFriendByPhone(message: WeworkMessageBean): Boolean {

View File

@@ -219,13 +219,19 @@ object WeworkOperationImpl {
uploadCommandResult(message, ExecCallbackBean.ERROR_CREATE_GROUP, "创建群失败", startTime) uploadCommandResult(message, ExecCallbackBean.ERROR_CREATE_GROUP, "创建群失败", startTime)
return false return false
} }
if (createGroupLimit()) {
uploadCommandResult(message, ExecCallbackBean.ERROR_CREATE_GROUP_LIMIT, "建群达到上限", startTime)
return false
} else {
LogUtils.v("未发现建群达到上限")
}
} }
if (!groupRename(groupName)) { if (!groupRename(groupName)) {
uploadCommandResult(message, ExecCallbackBean.ERROR_GROUP_RENAME, "创建群成功 群改名失败", startTime) uploadCommandResult(message, ExecCallbackBean.ERROR_GROUP_RENAME, "创建群成功 群改名失败", startTime)
return false return false
} }
if (!groupAddMember(selectList)) { if (!groupAddMember(selectList)) {
uploadCommandResult(message, ExecCallbackBean.ERROR_GROUP_ADD_MEMBER, "创建群成功 群改名成功 群拉人失败 ${selectList?.joinToString()}", startTime) uploadCommandResult(message, ExecCallbackBean.ERROR_GROUP_ADD_MEMBER, "创建群成功 群改名成功 群拉人失败: ${selectList?.joinToString()}", startTime)
return false return false
} }
if (!groupChangeAnnouncement(groupAnnouncement)) { if (!groupChangeAnnouncement(groupAnnouncement)) {
@@ -278,11 +284,11 @@ object WeworkOperationImpl {
return false return false
} }
if (!groupAddMember(selectList, showMessageHistory == true)) { if (!groupAddMember(selectList, showMessageHistory == true)) {
uploadCommandResult(message, ExecCallbackBean.ERROR_GROUP_ADD_MEMBER, "进入房间成功 群改名成功 群拉人失败 ${selectList?.joinToString()}", startTime) uploadCommandResult(message, ExecCallbackBean.ERROR_GROUP_ADD_MEMBER, "进入房间成功 群改名成功 群拉人失败: ${selectList?.joinToString()}", startTime)
return false return false
} }
if (!groupRemoveMember(removeList)) { if (!groupRemoveMember(removeList)) {
uploadCommandResult(message, ExecCallbackBean.ERROR_GROUP_REMOVE_MEMBER, "进入房间成功 群改名成功 群拉人成功 群踢人失败 ${removeList?.joinToString()}", startTime) uploadCommandResult(message, ExecCallbackBean.ERROR_GROUP_REMOVE_MEMBER, "进入房间成功 群改名成功 群拉人成功 群踢人失败: ${removeList?.joinToString()}", startTime)
return false return false
} }
if (!groupChangeAnnouncement(newGroupAnnouncement)) { if (!groupChangeAnnouncement(newGroupAnnouncement)) {
@@ -657,46 +663,21 @@ object WeworkOperationImpl {
} }
/** /**
* 手机号添加好友 * 手机号添加好友或修改好友信息
* @see WeworkMessageBean.ADD_FRIEND_BY_PHONE * @see WeworkMessageBean.ADD_FRIEND_BY_PHONE
* @param friend 待添加用户列表 * @param friend 待添加用户
*/ */
fun addFriendByPhone( fun addFriendByPhone(
message: WeworkMessageBean, message: WeworkMessageBean,
friend: WeworkMessageBean.Friend friend: WeworkMessageBean.Friend
): Boolean { ): Boolean {
val startTime = System.currentTimeMillis() val startTime = System.currentTimeMillis()
goHome() //如果已经是好友的可以传name修改好友信息
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.RecyclerView, Views.ListView, Views.ViewGroup) if (friend.phone == null && friend.name != null) {
if (list != null) { if (getFriendInfo(friend.name)) {
val frontNode = AccessibilityUtil.findFrontNode(list) if (AccessibilityUtil.findOneByText(getRoot(), "标签", "电话", "描述", "设置备注和描述", exact = true) != null) {
val textViewList = AccessibilityUtil.findAllOnceByClazz(frontNode, Views.TextView) var markTv =
if (textViewList.size >= 2) { AccessibilityUtil.findOnceByText(getRoot(), "设置备注和描述", exact = true)
val searchButton: AccessibilityNodeInfo = textViewList[textViewList.size - 2]
val multiButton: AccessibilityNodeInfo = textViewList[textViewList.size - 1]
AccessibilityUtil.performClick(multiButton)
sleep(Constant.POP_WINDOW_INTERVAL)
val list = AccessibilityUtil.findAllByClazz(getRoot(), Views.ListView).lastOrNull()
if (list != null) {
val button = AccessibilityUtil.findOneByText(list, "添加客户", "添加居民", "加微信", "加学员", exact = true)
if (button != null) {
AccessibilityUtil.performClick(button)
sleep(Constant.POP_WINDOW_INTERVAL)
AccessibilityUtil.findTextAndClick(getRoot(), "搜索手机号添加")
AccessibilityUtil.findTextInput(getRoot(), friend.phone.trim())
if (AccessibilityUtil.findTextAndClick(getRoot(), "网络查找手机")) {
val bothUsedTv = AccessibilityUtil.findOneByText(getRoot(), "对方同时使用", "标签", "电话")
val bothUsedText = bothUsedTv?.text
if (bothUsedText != null && bothUsedText.contains("对方同时使用")) {
AccessibilityUtil.performClick(
AccessibilityUtil.findOnceByClazz(
AccessibilityUtil.findBackNode(bothUsedTv),
Views.ImageView
)
)
}
if (AccessibilityUtil.findOneByText(getRoot(), "标签", "电话") != null) {
var markTv = AccessibilityUtil.findOnceByText(getRoot(), "设置备注和描述", exact = true)
if (markTv == null) { if (markTv == null) {
markTv = AccessibilityUtil.findOnceByText(getRoot(), "企业", exact = true) markTv = AccessibilityUtil.findOnceByText(getRoot(), "企业", exact = true)
} }
@@ -729,28 +710,60 @@ object WeworkOperationImpl {
setFriendTags(friend.tagList) setFriendTags(friend.tagList)
} }
} }
//添加联系人 } else {
val imageView = LogUtils.e("未找到标签")
AccessibilityUtil.findOneByClazz(getRoot(), Views.ImageView) uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "未找到标签", startTime)
if (imageView != null) { return false
val textViewList = AccessibilityUtil.findAllOnceByClazz( }
imageView.parent, LogUtils.d("修改好友信息成功: ${friend.name}")
Views.TextView uploadCommandResult(message, ExecCallbackBean.SUCCESS, "", startTime)
sleep(3000)
return true
} else {
LogUtils.e("未找到用户: ${friend.name}")
uploadCommandResult(message, ExecCallbackBean.ERROR_TARGET, "未找到用户: ${friend.name}", startTime)
return false
}
}
//手机号添加好友
goHome()
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.RecyclerView, Views.ListView, Views.ViewGroup)
if (list != null) {
val frontNode = AccessibilityUtil.findFrontNode(list)
val textViewList = AccessibilityUtil.findAllOnceByClazz(frontNode, Views.TextView)
if (textViewList.size >= 2) {
val searchButton: AccessibilityNodeInfo = textViewList[textViewList.size - 2]
val multiButton: AccessibilityNodeInfo = textViewList[textViewList.size - 1]
AccessibilityUtil.performClick(multiButton)
sleep(Constant.POP_WINDOW_INTERVAL)
val list = AccessibilityUtil.findAllByClazz(getRoot(), Views.ListView).lastOrNull()
if (list != null) {
val button = AccessibilityUtil.findOneByText(list, "添加客户", "添加居民", "加微信", "加学员", exact = true)
if (button != null) {
AccessibilityUtil.performClick(button)
sleep(Constant.POP_WINDOW_INTERVAL)
AccessibilityUtil.findTextAndClick(getRoot(), "搜索手机号添加")
AccessibilityUtil.findTextInput(getRoot(), friend.phone.trim())
if (AccessibilityUtil.findTextAndClick(getRoot(), "网络查找手机")) {
sleep(Constant.POP_WINDOW_INTERVAL)
val bothUsedTv = AccessibilityUtil.findOneByTextRegex(getRoot(), "(对方同时使用.*?)|(标签)|(电话)|(描述)|(设置备注和描述)", timeout = 2000)
val bothUsedText = bothUsedTv?.text
if (bothUsedText != null && bothUsedText.contains("对方同时使用")) {
AccessibilityUtil.performClick(
AccessibilityUtil.findOnceByClazz(
AccessibilityUtil.findBackNode(bothUsedTv),
Views.ImageView
)
) )
val filter =
textViewList.filter { it.text != null && it.text.toString() != "微信" }
if (filter.isNotEmpty()) {
val tvNick = filter[0]
LogUtils.d("好友昵称或备注名: " + tvNick.text)
}
} }
if (modifyFriendInfo(friend)) {
if (AccessibilityUtil.findTextAndClick(getRoot(), "添加为联系人")) { if (AccessibilityUtil.findTextAndClick(getRoot(), "添加为联系人")) {
LogUtils.d("准备发送好友邀请: " + friend.phone) LogUtils.d("准备发送好友邀请: ${friend.phone}")
if (!friend.leavingMsg.isNullOrEmpty()) { if (!friend.leavingMsg.isNullOrEmpty()) {
AccessibilityUtil.findTextInput(getRoot(), friend.leavingMsg) AccessibilityUtil.findTextInput(getRoot(), friend.leavingMsg)
} }
if (AccessibilityUtil.findTextAndClick(getRoot(), "发送添加邀请", "发送申请")) { if (AccessibilityUtil.findTextAndClick(getRoot(), "发送添加邀请", "发送申请")) {
LogUtils.d("发送添加邀请成功: " + friend.phone) LogUtils.d("发送添加邀请成功: ${friend.phone}")
uploadCommandResult(message, ExecCallbackBean.SUCCESS, "", startTime) uploadCommandResult(message, ExecCallbackBean.SUCCESS, "", startTime)
return true return true
} else { } else {
@@ -770,8 +783,8 @@ object WeworkOperationImpl {
} }
} }
} else { } else {
LogUtils.e("未找到标签") LogUtils.e("修改用户信息失败: ${friend.phone}")
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "未找到标签", startTime) uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "修改用户信息失败: ${friend.phone}", startTime)
return false return false
} }
} else { } else {
@@ -801,6 +814,119 @@ object WeworkOperationImpl {
} }
} }
/**
* 从外部群添加好友
* @see WeworkMessageBean.ADD_FRIEND_BY_PHONE
* @param groupName 外部群
* @param friend 待添加用户
*/
fun addFriendByGroup(
message: WeworkMessageBean,
groupName: String,
friend: WeworkMessageBean.Friend
): Boolean {
val startTime = System.currentTimeMillis()
if (WeworkRoomUtil.intoRoom(groupName) && WeworkRoomUtil.intoGroupManager()) {
if (AccessibilityUtil.findTextAndClick(getRoot(), "查看全部群成员")) {
val title = friend.name
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
if (list != null) {
val frontNode = AccessibilityUtil.findFrontNode(list)
val textViewList = AccessibilityUtil.findAllOnceByClazz(frontNode, Views.TextView)
.filter { it.text == null }
if (textViewList.size >= 2) {
val searchButton: AccessibilityNodeInfo = textViewList[textViewList.size - 1]
AccessibilityUtil.performClick(searchButton)
val needTrim = title.contains(Constant.regTrimTitle)
val trimTitle = title.replace(Constant.regTrimTitle, "")
AccessibilityUtil.findTextInput(getRoot(), trimTitle)
sleep(Constant.CHANGE_PAGE_INTERVAL)
//消息页搜索结果列表
val selectListView = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
val reverseRegexTitle = RegexHelper.reverseRegexTitle(trimTitle)
val regex1 = "^$reverseRegexTitle" + if (needTrim) ".*?" else "(-.*)?(…)?(\\(.*?\\))?$"
val regex2 = ".*?\\($reverseRegexTitle\\)$"
val regex = "($regex1)|($regex2)"
val matchSelect = AccessibilityUtil.findOneByTextRegex(
selectListView,
regex,
timeout = 2000,
root = false
)
if (selectListView != null && matchSelect != null) {
for (i in 0 until selectListView.childCount) {
val item = selectListView.getChild(i)
val searchResult = AccessibilityUtil.findOnceByTextRegex(item, regex)
//过滤异常好友
if (searchResult != null && searchResult.parent.childCount < 3) {
item.refresh()
val imageView =
AccessibilityUtil.findOneByClazz(item, Views.ImageView, root = false)
AccessibilityUtil.performClick(imageView)
break
}
}
if (modifyFriendInfo(friend)) {
if (AccessibilityUtil.findTextAndClick(getRoot(), "添加为联系人")) {
LogUtils.d("准备发送好友邀请: ${friend.name}")
if (!friend.leavingMsg.isNullOrEmpty()) {
AccessibilityUtil.findTextInput(getRoot(), friend.leavingMsg)
}
if (AccessibilityUtil.findTextAndClick(getRoot(), "发送添加邀请", "发送申请")) {
LogUtils.d("发送添加邀请成功: ${friend.name}")
uploadCommandResult(message, ExecCallbackBean.SUCCESS, "", startTime)
return true
} else {
if (AccessibilityUtil.findOnceByText(getRoot(), "发消息", exact = true) != null) {
LogUtils.d("已经添加成功: ${friend.name}")
uploadCommandResult(message, ExecCallbackBean.SUCCESS, "", startTime)
return true
}
LogUtils.e("未找到发送邀请按钮")
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "未找到发送邀请按钮", startTime)
return false
}
} else {
if (AccessibilityUtil.findOnceByText(getRoot(), "发消息", exact = true) != null) {
LogUtils.e("已经添加联系人,请勿重复添加: ${friend.name}")
uploadCommandResult(message, ExecCallbackBean.ERROR_REPEAT, "已经添加联系人,请勿重复添加 ${friend.name}", startTime)
return false
} else {
LogUtils.e("未找到添加为联系人")
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "未找到添加为联系人", startTime)
return false
}
}
} else {
LogUtils.e("修改用户信息失败: ${friend.name}")
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "修改用户信息失败: ${friend.name}", startTime)
return false
}
} else {
LogUtils.e("未搜索到结果: ${friend.name}")
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "未搜索到结果: ${friend.name}", startTime)
return false
}
} else {
LogUtils.e("未发现搜索按钮")
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "未发现搜索按钮", startTime)
return false
}
} else {
LogUtils.e("未发现通讯录列表")
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "未发现通讯录列表", startTime)
return false
}
} else {
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "未找到查看全部群成员按钮 $groupName", startTime)
return false
}
} else {
uploadCommandResult(message, ExecCallbackBean.ERROR_INTO_ROOM, "进入房间失败 $groupName", startTime)
return false
}
}
/** /**
* 展示群信息 * 展示群信息
* @see WeworkMessageBean.SHOW_GROUP_INFO * @see WeworkMessageBean.SHOW_GROUP_INFO
@@ -884,7 +1010,7 @@ object WeworkOperationImpl {
if (matchSelect != null) { if (matchSelect != null) {
LogUtils.d("找到搜索结果: $select") LogUtils.d("找到搜索结果: $select")
} else { } else {
LogUtils.e("未搜索到结果") LogUtils.e("未搜索到结果: $select")
} }
sleep(Constant.POP_WINDOW_INTERVAL) sleep(Constant.POP_WINDOW_INTERVAL)
} }
@@ -902,18 +1028,18 @@ object WeworkOperationImpl {
AccessibilityUtil.performClick(sendButton) AccessibilityUtil.performClick(sendButton)
return true return true
} }
LogUtils.e("未发现发送按钮: ") LogUtils.e("未发现发送按钮")
return false return false
} else { } else {
LogUtils.e("未发现确认按钮: ") LogUtils.e("未发现确认按钮")
return false return false
} }
} else { } else {
LogUtils.e("未发现搜索和多选按钮: ") LogUtils.e("未发现搜索和多选按钮")
return false return false
} }
} }
LogUtils.e("未知错误: ") LogUtils.e("未知错误")
return false return false
} }
@@ -936,6 +1062,15 @@ object WeworkOperationImpl {
return false return false
} }
/**
* 检查是否达到当日建群上限
*/
private fun createGroupLimit(): Boolean {
val hasLimit =
AccessibilityUtil.findOneByText(getRoot(), "新建群聊功能暂时被限制", "未验证企业", timeout = 2000)
return hasLimit != null
}
/** /**
* 修改群名称 * 修改群名称
*/ */
@@ -1047,8 +1182,8 @@ object WeworkOperationImpl {
if (matchSelect != null) { if (matchSelect != null) {
LogUtils.d("找到搜索结果: $select") LogUtils.d("找到搜索结果: $select")
} else { } else {
LogUtils.e("未搜索到结果") LogUtils.e("未搜索到结果: $select")
return false if (Constant.groupStrict) return false
} }
} }
if (showMessageHistory) { if (showMessageHistory) {
@@ -1059,7 +1194,7 @@ object WeworkOperationImpl {
if (confirmButton != null) { if (confirmButton != null) {
AccessibilityUtil.performClick(confirmButton) AccessibilityUtil.performClick(confirmButton)
} else { } else {
LogUtils.e("未发现确认按钮: ") LogUtils.e("未发现确认按钮")
return false return false
} }
} else { } else {
@@ -1138,8 +1273,8 @@ object WeworkOperationImpl {
if (matchSelect != null) { if (matchSelect != null) {
LogUtils.d("找到搜索结果: $select") LogUtils.d("找到搜索结果: $select")
} else { } else {
LogUtils.e("未搜索到结果") LogUtils.e("未搜索到结果: $select")
return false if (Constant.groupStrict) return false
} }
} }
val confirmButton = val confirmButton =
@@ -1147,7 +1282,7 @@ object WeworkOperationImpl {
if (confirmButton != null) { if (confirmButton != null) {
AccessibilityUtil.performClick(confirmButton) AccessibilityUtil.performClick(confirmButton)
} else { } else {
LogUtils.e("未发现移出按钮: ") LogUtils.e("未发现移出按钮")
return false return false
} }
} else { } else {
@@ -1193,10 +1328,10 @@ object WeworkOperationImpl {
} }
sleep(3000) sleep(3000)
} else { } else {
LogUtils.e("无法进行群公告发布: ") LogUtils.e("无法进行群公告发布")
} }
} else { } else {
LogUtils.e("无法进行群公告发布和编辑: ") LogUtils.e("无法进行群公告发布和编辑")
return false return false
} }
} else { } else {
@@ -1225,10 +1360,10 @@ object WeworkOperationImpl {
if (AccessibilityUtil.findTextAndClick(getRoot(), "确定")) { if (AccessibilityUtil.findTextAndClick(getRoot(), "确定")) {
return true return true
} else { } else {
LogUtils.e("无法进行群备注发布: ") LogUtils.e("无法进行群备注发布")
} }
} else { } else {
LogUtils.e("无法进行群备注修改: ") LogUtils.e("无法进行群备注修改")
} }
} else { } else {
LogUtils.e("未找到群公告按钮") LogUtils.e("未找到群公告按钮")
@@ -1352,6 +1487,141 @@ object WeworkOperationImpl {
return false return false
} }
/**
* 从通讯录查询好友信息
*/
private fun getFriendInfo(title: String): Boolean {
goHomeTab("通讯录")
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
if (list != null) {
val frontNode = AccessibilityUtil.findFrontNode(list)
val textViewList = AccessibilityUtil.findAllOnceByClazz(frontNode, Views.TextView)
if (textViewList.size >= 2) {
val searchButton: AccessibilityNodeInfo = textViewList[textViewList.size - 2]
val multiButton: AccessibilityNodeInfo = textViewList[textViewList.size - 1]
AccessibilityUtil.performClick(searchButton)
val needTrim = title.contains(Constant.regTrimTitle)
val trimTitle = title.replace(Constant.regTrimTitle, "")
AccessibilityUtil.findTextInput(getRoot(), trimTitle)
sleep(Constant.CHANGE_PAGE_INTERVAL)
//消息页搜索结果列表
val selectListView = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
val reverseRegexTitle = RegexHelper.reverseRegexTitle(trimTitle)
val regex1 = "^$reverseRegexTitle" + if (needTrim) ".*?" else "(-.*)?(…)?(\\(.*?\\))?$"
val regex2 = ".*?\\($reverseRegexTitle\\)$"
val regex = "($regex1)|($regex2)"
val matchSelect = AccessibilityUtil.findOneByTextRegex(
selectListView,
regex,
timeout = 2000,
root = false
)
if (selectListView != null && matchSelect != null) {
for (i in 0 until selectListView.childCount) {
val item = selectListView.getChild(i)
val searchResult = AccessibilityUtil.findOnceByTextRegex(item, regex)
//过滤异常好友
if (searchResult != null && searchResult.parent.childCount < 3) {
item.refresh()
val imageView =
AccessibilityUtil.findOneByClazz(item, Views.ImageView, root = false)
AccessibilityUtil.performClick(imageView)
break
}
}
}
if (matchSelect != null) {
LogUtils.d("找到搜索结果: $title")
return true
} else {
LogUtils.e("未搜索到结果: $title")
}
}
}
return false
}
/**
* 修改好友信息
*/
private fun modifyFriendInfo(friend: WeworkMessageBean.Friend): Boolean {
if (AccessibilityUtil.findOneByText(getRoot(), "标签", "电话", "描述", "设置备注和描述", exact = true) != null) {
var markTv = AccessibilityUtil.findOnceByText(getRoot(), "设置备注和描述", exact = true)
if (markTv == null) {
markTv = AccessibilityUtil.findOnceByText(getRoot(), "企业", exact = true)
}
if (markTv == null) {
markTv = AccessibilityUtil.findOnceByText(getRoot(), "描述", exact = true)
}
//设置备注
if (markTv != null && (friend.markName != null
|| friend.markCorp != null || friend.markExtra != null)
) {
AccessibilityUtil.performClick(markTv)
val etList =
AccessibilityUtil.findAllByClazz(getRoot(), Views.EditText, minSize = 2)
if (etList.size >= 5) {
//微信用户 备注/企业/电话/电话/描述
if (friend.markName != null) {
AccessibilityUtil.editTextInput(etList[0], friend.markName)
}
if (friend.markCorp != null) {
AccessibilityUtil.editTextInput(etList[1], friend.markCorp)
}
if (friend.markExtra != null) {
AccessibilityUtil.editTextInput(etList[4], friend.markExtra)
}
} else if (etList.size == 2) {
//同企业内部用户 备注/描述
if (friend.markName != null) {
AccessibilityUtil.editTextInput(etList[0], friend.markName)
}
if (friend.markExtra != null) {
AccessibilityUtil.editTextInput(etList[1], friend.markExtra)
}
} else if (etList.size == 3) {
//外部企业用户 备注/电话/描述
if (friend.markName != null) {
AccessibilityUtil.editTextInput(etList[0], friend.markName)
}
if (friend.markExtra != null) {
AccessibilityUtil.editTextInput(etList[2], friend.markExtra)
}
}
AccessibilityUtil.findTextAndClick(getRoot(), "保存")
}
//设置标签
if (!friend.tagList.isNullOrEmpty()) {
if (AccessibilityUtil.findTextAndClick(getRoot(), "标签")) {
setFriendTags(friend.tagList)
}
}
//添加联系人
val imageView =
AccessibilityUtil.findOneByClazz(getRoot(), Views.ImageView)
if (imageView != null) {
val textViewList = AccessibilityUtil.findAllOnceByClazz(
imageView.parent,
Views.TextView
)
val filter =
textViewList.filter { it.text != null && it.text.toString() != "微信" }
if (filter.isNotEmpty()) {
val tvNick = filter[0]
LogUtils.d("好友昵称或备注名: ${tvNick.text}")
}
}
return true
} else {
if (AccessibilityUtil.findOnceByText(getRoot(), "无权查看") != null) {
LogUtils.e("无权查看该用户")
return false
}
LogUtils.e("未找到标签")
return false
}
}
/** /**
* 设置好友标签 * 设置好友标签
*/ */
@@ -1462,7 +1732,6 @@ object WeworkOperationImpl {
) )
return true return true
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace()
LogUtils.e(e) LogUtils.e(e)
} }
} }

View File

@@ -87,7 +87,9 @@ class WeworkService : AccessibilityService() {
val robotId = SPUtils.getInstance().getString(Constant.LISTEN_CHANNEL_ID, "") val robotId = SPUtils.getInstance().getString(Constant.LISTEN_CHANNEL_ID, "")
val appVersion = SPUtils.getInstance().getString("appVersion", "") val appVersion = SPUtils.getInstance().getString("appVersion", "")
val workVersion = SPUtils.getInstance().getString("workVersion", "") val workVersion = SPUtils.getInstance().getString("workVersion", "")
log("链接建立: $robotId appVersion: $appVersion workVersion: $workVersion") val deviceRooted = SPUtils.getInstance().getBoolean("deviceRooted", false)
val hook = SPUtils.getInstance().getBoolean("hook", false)
log("链接建立: $robotId appVersion: $appVersion workVersion: $workVersion deviceRooted: $deviceRooted hook: $hook")
LogUtils.i("设置自动跳转企业微信") LogUtils.i("设置自动跳转企业微信")
sendBroadcast(true) sendBroadcast(true)
} }

View File

@@ -0,0 +1,110 @@
package org.yameida.worktool.utils.envcheck;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.util.Log;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class CheckHook {
public static boolean isHook(Context context) {
return isHookByPackageName(context) || isHookByStack(context) || isHookByJar();
}
/**
* 包名检测
*
* @param context
* @return
*/
public static boolean isHookByPackageName(Context context) {
boolean isHook = false;
PackageManager packageManager = context.getPackageManager();
List<ApplicationInfo> applicationInfoList = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
if (null == applicationInfoList) {
return isHook;
}
for (ApplicationInfo applicationInfo : applicationInfoList) {
if (applicationInfo.packageName.equals("de.robv.android.xposed.installer")) {
Log.wtf("HookDetection", "Xposed found on the system.");
isHook = true;
}
if (applicationInfo.packageName.equals("com.saurik.substrate")) {
isHook = true;
Log.wtf("HookDetection", "Substrate found on the system.");
}
}
return isHook;
}
public static boolean isHookByStack(Context context) {
boolean isHook = false;
try {
throw new Exception("blah");
} catch (Exception e) {
int zygoteInitCallCount = 0;
for (StackTraceElement stackTraceElement : e.getStackTrace()) {
if (stackTraceElement.getClassName().equals("com.android.internal.os.ZygoteInit")) {
zygoteInitCallCount++;
if (zygoteInitCallCount == 2) {
Log.wtf("HookDetection", "Substrate is active on the device.");
isHook = true;
}
}
if (stackTraceElement.getClassName().equals("com.saurik.substrate.MS$2") &&
stackTraceElement.getMethodName().equals("invoked")) {
Log.wtf("HookDetection", "A method on the stack trace has been hooked using Substrate.");
isHook = true;
}
if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") &&
stackTraceElement.getMethodName().equals("main")) {
Log.wtf("HookDetection", "Xposed is active on the device.");
isHook = true;
}
if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") &&
stackTraceElement.getMethodName().equals("handleHookedMethod")) {
Log.wtf("HookDetection", "A method on the stack trace has been hooked using Xposed.");
isHook = true;
}
}
}
return isHook;
}
public static boolean isHookByJar() {
boolean isHook = false;
try {
Set<String> libraries = new HashSet();
String mapsFilename = "/proc/" + android.os.Process.myPid() + "/maps";
BufferedReader reader = new BufferedReader(new FileReader(mapsFilename));
String line;
while ((line = reader.readLine()) != null) {
if (line.endsWith(".so") || line.endsWith(".jar")) {
int n = line.lastIndexOf(" ");
libraries.add(line.substring(n + 1));
}
}
for (String library : libraries) {
if (library.contains("com.saurik.substrate")) {
Log.wtf("HookDetection", "Substrate shared object found: " + library);
isHook = true;
}
if (library.contains("XposedBridge.jar")) {
Log.wtf("HookDetection", "Xposed JAR found: " + library);
isHook = true;
}
}
reader.close();
} catch (Exception e) {
Log.wtf("HookDetection", e.toString());
}
return isHook;
}
}

View File

@@ -0,0 +1,221 @@
package org.yameida.worktool.utils.envcheck;
import android.util.Log;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
public class CheckRoot {
private static String LOG_TAG = CheckRoot.class.getName();
public static boolean isDeviceRooted() {
if (checkDeviceDebuggable()) {
return true;
}
if (checkSuperuserApk()) {
return true;
}
if (checkRootPathSU()) {
return true;
}
if (checkRootWhichSU()) {
return true;
}
if (checkAccessRootData()) {
return true;
}
return false;
}
public static boolean checkDeviceDebuggable() {
String buildTags = android.os.Build.TAGS;
if (buildTags != null && buildTags.contains("test-keys")) {
Log.i(LOG_TAG, "buildTags=" + buildTags);
return true;
}
return false;
}
public static boolean checkSuperuserApk() {
try {
File file = new File("/system/app/Superuser.apk");
if (file.exists()) {
Log.i(LOG_TAG, "/system/app/Superuser.apk exist");
return true;
}
} catch (Exception e) {
}
return false;
}
public static boolean checkRootPathSU() {
File f = null;
final String kSuSearchPaths[] = {"/system/bin/", "/system/xbin/", "/system/sbin/", "/sbin/", "/vendor/bin/"};
try {
for (int i = 0; i < kSuSearchPaths.length; i++) {
f = new File(kSuSearchPaths[i] + "su");
if (f != null && f.exists()) {
Log.i(LOG_TAG, "find su in : " + kSuSearchPaths[i]);
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public static boolean checkRootWhichSU() {
String[] strCmd = new String[]{"/system/xbin/which", "su"};
ArrayList<String> execResult = executeCommand(strCmd);
if (execResult != null) {
Log.i(LOG_TAG, "execResult=" + execResult.toString());
return true;
} else {
Log.i(LOG_TAG, "execResult=null");
return false;
}
}
public static ArrayList<String> executeCommand(String[] shellCmd) {
String line = null;
ArrayList<String> fullResponse = new ArrayList<String>();
Process localProcess = null;
try {
Log.i(LOG_TAG, "to shell exec which for find su :");
localProcess = Runtime.getRuntime().exec(shellCmd);
} catch (Exception e) {
return null;
}
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(localProcess.getOutputStream()));
BufferedReader in = new BufferedReader(new InputStreamReader(localProcess.getInputStream()));
try {
while ((line = in.readLine()) != null) {
Log.i(LOG_TAG, "> Line received: " + line);
fullResponse.add(line);
}
} catch (Exception e) {
e.printStackTrace();
}
Log.i(LOG_TAG, "> Full response was: " + fullResponse);
return fullResponse;
}
public static synchronized boolean checkGetRootAuth() {
Process process = null;
DataOutputStream os = null;
try {
Log.i(LOG_TAG, "to exec su");
process = Runtime.getRuntime().exec("su");
os = new DataOutputStream(process.getOutputStream());
os.writeBytes("exit\n");
os.flush();
int exitValue = process.waitFor();
Log.i(LOG_TAG, "exitValue=" + exitValue);
if (exitValue == 0) {
return true;
} else {
return false;
}
} catch (Exception e) {
Log.i(LOG_TAG, "Unexpected error - Here is what I know: "
+ e.getMessage());
return false;
} finally {
try {
if (os != null) {
os.close();
}
process.destroy();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static synchronized boolean checkBusybox() {
try {
Log.i(LOG_TAG, "to exec busybox df");
String[] strCmd = new String[]{"busybox", "df"};
ArrayList<String> execResult = executeCommand(strCmd);
if (execResult != null) {
Log.i(LOG_TAG, "execResult=" + execResult.toString());
return true;
} else {
Log.i(LOG_TAG, "execResult=null");
return false;
}
} catch (Exception e) {
Log.i(LOG_TAG, "Unexpected error - Here is what I know: "
+ e.getMessage());
return false;
}
}
public static synchronized boolean checkAccessRootData() {
try {
Log.i(LOG_TAG, "to write /data");
String fileContent = "test_ok";
Boolean writeFlag = writeFile("/data/su_test", fileContent);
if (writeFlag) {
Log.i(LOG_TAG, "write ok");
} else {
Log.i(LOG_TAG, "write failed");
}
Log.i(LOG_TAG, "to read /data");
String strRead = readFile("/data/su_test");
Log.i(LOG_TAG, "strRead=" + strRead);
if (fileContent.equals(strRead)) {
return true;
} else {
return false;
}
} catch (Exception e) {
Log.i(LOG_TAG, "Unexpected error - Here is what I know: "
+ e.getMessage());
return false;
}
}
public static Boolean writeFile(String fileName, String message) {
try {
FileOutputStream fout = new FileOutputStream(fileName);
byte[] bytes = message.getBytes();
fout.write(bytes);
fout.close();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static String readFile(String fileName) {
File file = new File(fileName);
try {
FileInputStream fis = new FileInputStream(file);
byte[] bytes = new byte[1024];
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int len;
while ((len = fis.read(bytes)) > 0) {
bos.write(bytes, 0, len);
}
String result = new String(bos.toByteArray());
Log.i(LOG_TAG, result);
return result;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}