diff --git a/BACKEND_PROTOCOL.md b/BACKEND_PROTOCOL.md
index e4b8090..30a7a1f 100644
--- a/BACKEND_PROTOCOL.md
+++ b/BACKEND_PROTOCOL.md
@@ -166,3 +166,133 @@ WorkTool 使用 WebSocket 协议与服务端进行长连接通信。
* 8: 链接 (TEXT_TYPE_LINK)
* 9: 文件 (TEXT_TYPE_FILE)
* 15: 引用回复 (TEXT_TYPE_REPLY)
+
+## 附录:自动通过好友请求
+
+当 APP 开启"自动通过好友请求"开关后,有新好友添加成功时会主动上传好友信息到服务端。
+
+### 上传时机
+* 自动通过好友请求成功后
+* 手动通过好友请求成功后
+
+### 消息类型
+* **Type**: `502` (GET_FRIEND_INFO)
+
+### 数据结构
+```json
+{
+ "type": 502,
+ "friend": {
+ "name": "张三", // 好友昵称
+ "newFriend": true, // 标记为新好友
+ "wechatId": "zhangsan123", // 微信号
+ "phone": "13800138000", // 手机号
+ "corpName": "XX科技有限公司", // 企业名称
+ "department": "技术部", // 部门
+ "position": "工程师", // 职位
+ "email": "zhangsan@example.com", // 邮箱
+ "markName": "张工", // 备注名
+ "gender": 1, // 性别: 1男 2女 0未知
+ "leavingMsg": "请求添加好友时的留言" // 留言/验证消息
+ }
+}
+```
+
+### 服务端接收示例
+服务端只需监听 WebSocket 消息,当 `type=502` 且 `friend.newFriend=true` 时,表示有新好友添加成功。
+
+---
+
+## 附录:接收消息列表 (101)
+
+客户端监听到新消息时,会主动上传消息列表到服务端。
+
+### 消息类型
+* **Type**: `101` (TYPE_RECEIVE_MESSAGE_LIST)
+
+### 数据结构(群聊)
+```json
+{
+ "type": 101,
+ "roomType": 1,
+ "titleList": ["技术交流群"],
+ "messageList": [
+ {
+ "sender": 0,
+ "textType": 1,
+ "nameList": ["张三"],
+ "itemMessageList": [
+ {"feature": 2, "text": "你好,这是消息内容"}
+ ]
+ },
+ {
+ "sender": 1,
+ "textType": 1,
+ "itemMessageList": [
+ {"feature": 2, "text": "收到"}
+ ]
+ }
+ ]
+}
+```
+
+### 数据结构(单聊)
+```json
+{
+ "type": 101,
+ "roomType": 2,
+ "titleList": ["张三"],
+ "contact": {
+ "name": "张三",
+ "wechatId": "zhangsan123",
+ "phone": "13800138000",
+ "corpName": "XX科技有限公司",
+ "department": "技术部",
+ "position": "工程师",
+ "email": "zhangsan@example.com",
+ "markName": "张工"
+ },
+ "messageList": [
+ {
+ "sender": 0,
+ "textType": 1,
+ "itemMessageList": [
+ {"feature": 2, "text": "你好"}
+ ]
+ }
+ ]
+}
+```
+
+### 字段说明
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| type | int | 消息类型:101=接收的消息列表 |
+| roomType | int | 房间类型:1=外部群, 2=外部联系人, 3=内部群, 4=内部联系人 |
+| titleList | Array | 聊天对象名称(群名或好友名) |
+| contact | Object | 联系人信息(仅单聊时有效) |
+| messageList | Array | 消息列表 |
+
+### messageList 每条消息
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| sender | int | 发送者:0=其他人, 1=自己, 2=系统消息 |
+| textType | int | 消息类型:1=文本, 2=图片, 3=语音, 5=视频, 8=链接, 9=文件 |
+| nameList | Array | 发送者名称列表(群聊时有效) |
+| itemMessageList | Array | 消息内容列表 |
+
+### contact 联系人信息
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| name | String | 好友昵称 |
+| wechatId | String | 微信号 |
+| phone | String | 手机号 |
+| corpName | String | 企业名称 |
+| department | String | 部门 |
+| position | String | 职位 |
+| email | String | 邮箱 |
+| markName | String | 备注名 |
+| gender | int | 性别:1=男, 2=女, 0=未知 |
diff --git a/app/src/main/java/org/yameida/worktool/model/WeworkMessageBean.java b/app/src/main/java/org/yameida/worktool/model/WeworkMessageBean.java
index e0eef54..2f3e238 100644
--- a/app/src/main/java/org/yameida/worktool/model/WeworkMessageBean.java
+++ b/app/src/main/java/org/yameida/worktool/model/WeworkMessageBean.java
@@ -246,6 +246,9 @@ public class WeworkMessageBean {
//添加好友
public Friend friend;
+ //联系人信息(单聊时上传)
+ public Friend contact;
+
//网络文件
public String fileBase64;
public String fileUrl;
@@ -376,18 +379,32 @@ public class WeworkMessageBean {
public Boolean newFriend;
//留言
public String leavingMsg;
+ //微信号
+ public String wechatId;
+ //企业名称
+ public String corpName;
+ //部门
+ public String department;
+ //职位
+ public String position;
+ //邮箱
+ public String email;
+ //性别: 1男 2女 0未知
+ public Integer gender;
+ //头像URL
+ public String avatarUrl;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Friend friend = (Friend) o;
- return Objects.equals(phone, friend.phone) && Objects.equals(name, friend.name) && Objects.equals(markName, friend.markName) && Objects.equals(markCorp, friend.markCorp) && Objects.equals(markExtra, friend.markExtra) && Objects.equals(tagList, friend.tagList) && Objects.equals(newFriend, friend.newFriend) && Objects.equals(leavingMsg, friend.leavingMsg);
+ return Objects.equals(phone, friend.phone) && Objects.equals(name, friend.name) && Objects.equals(markName, friend.markName) && Objects.equals(markCorp, friend.markCorp) && Objects.equals(markExtra, friend.markExtra) && Objects.equals(tagList, friend.tagList) && Objects.equals(newFriend, friend.newFriend) && Objects.equals(leavingMsg, friend.leavingMsg) && Objects.equals(wechatId, friend.wechatId) && Objects.equals(corpName, friend.corpName) && Objects.equals(department, friend.department) && Objects.equals(position, friend.position) && Objects.equals(email, friend.email) && Objects.equals(gender, friend.gender) && Objects.equals(avatarUrl, friend.avatarUrl);
}
@Override
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, wechatId, corpName, department, position, email, gender, avatarUrl);
}
@Override
@@ -401,6 +418,13 @@ public class WeworkMessageBean {
", tagList=" + tagList +
", newFriend=" + newFriend +
", leavingMsg='" + leavingMsg + '\'' +
+ ", wechatId='" + wechatId + '\'' +
+ ", corpName='" + corpName + '\'' +
+ ", department='" + department + '\'' +
+ ", position='" + position + '\'' +
+ ", email='" + email + '\'' +
+ ", gender=" + gender +
+ ", avatarUrl='" + avatarUrl + '\'' +
'}';
}
}
diff --git a/app/src/main/java/org/yameida/worktool/service/WeworkLoopImpl.kt b/app/src/main/java/org/yameida/worktool/service/WeworkLoopImpl.kt
index 0df5a9a..3b6a4dd 100644
--- a/app/src/main/java/org/yameida/worktool/service/WeworkLoopImpl.kt
+++ b/app/src/main/java/org/yameida/worktool/service/WeworkLoopImpl.kt
@@ -280,16 +280,26 @@ object WeworkLoopImpl {
}
messageList.removeIf { it.textType == WeworkMessageBean.TEXT_TYPE_IMAGE }
}
- WeworkController.weworkService.webSocketManager.send(
- WeworkMessageBean(
- null, null,
- WeworkMessageBean.TYPE_RECEIVE_MESSAGE_LIST,
- roomType,
- titleList,
- messageList,
- null
- )
+ // 获取单聊联系人详细信息
+ var contactInfo: WeworkMessageBean.Friend? = null
+ if (roomType == WeworkMessageBean.ROOM_TYPE_EXTERNAL_CONTACT
+ || roomType == WeworkMessageBean.ROOM_TYPE_INTERNAL_CONTACT) {
+ val friendName = titleList.firstOrNull() ?: ""
+ if (friendName.isNotEmpty()) {
+ contactInfo = getChatContactInfo(friendName)
+ }
+ }
+
+ val messageBean = WeworkMessageBean(
+ null, null,
+ WeworkMessageBean.TYPE_RECEIVE_MESSAGE_LIST,
+ roomType,
+ titleList,
+ messageList,
+ null
)
+ messageBean.contact = contactInfo
+ WeworkController.weworkService.webSocketManager.send(messageBean)
//推测是否回复并在房间等待指令
if (needInfer) {
val lastMessage = messageList.lastOrNull()
@@ -415,7 +425,8 @@ object WeworkLoopImpl {
val filter = textViewList.filter { it.text != null && it.text.toString() != "微信" }
if (filter.isNotEmpty()) {
val tvNick = filter[0]
- LogUtils.d("好友请求: " + tvNick.text)
+ val friendName = tvNick.text.toString()
+ LogUtils.d("好友请求: $friendName")
//设置标签
if (AccessibilityUtil.findTextAndClick(getRoot(), "标签")) {
WeworkOperationImpl.setFriendTags(arrayListOf("worktool自动通过"))
@@ -429,14 +440,13 @@ object WeworkLoopImpl {
if (textNode?.text?.toString() == "添加请求已过期,添加失败") {
LogUtils.d("添加好友失败")
} else {
+ // 获取完整的好友信息
+ val friendInfo = getFriendDetailInfo(friendName)
val weworkMessageBean = WeworkMessageBean()
weworkMessageBean.type = WeworkMessageBean.GET_FRIEND_INFO
- weworkMessageBean.friend = WeworkMessageBean.Friend().apply {
- name = tvNick.text.toString()
- newFriend = true
- }
+ weworkMessageBean.friend = friendInfo
WeworkController.weworkService.webSocketManager.send(weworkMessageBean)
- nameList.add(tvNick.text.toString())
+ nameList.add(friendName)
}
//回到上一页
var retry = 5
@@ -451,6 +461,107 @@ object WeworkLoopImpl {
return nameList
}
+ /**
+ * 获取好友详细信息
+ * 通过好友后进入详情页抓取完整信息
+ */
+ private fun getFriendDetailInfo(friendName: String): WeworkMessageBean.Friend {
+ val friend = WeworkMessageBean.Friend().apply {
+ name = friendName
+ newFriend = true
+ }
+ try {
+ // 点击发消息进入聊天页面
+ val sendMsgNode = AccessibilityUtil.findOneByText(getRoot(), "发消息", exact = true)
+ if (sendMsgNode != null) {
+ AccessibilityUtil.performClick(sendMsgNode)
+ sleep(Constant.CHANGE_PAGE_INTERVAL)
+ }
+
+ // 进入好友详情页
+ if (WeworkRoomUtil.intoFriendDetail()) {
+ sleep(Constant.CHANGE_PAGE_INTERVAL)
+ // 解析详情页信息
+ val listView = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
+ if (listView != null) {
+ // 遍历列表获取各字段信息
+ for (i in 0 until listView.childCount) {
+ val item = listView.getChild(i) ?: continue
+ val textViews = AccessibilityUtil.findAllOnceByClazz(item, Views.TextView)
+ if (textViews.size >= 2) {
+ val label = textViews[0].text?.toString() ?: ""
+ val value = textViews[1].text?.toString() ?: ""
+ when {
+ label.contains("微信号") || label.contains("微信ID") -> friend.wechatId = value
+ label.contains("手机") -> friend.phone = value
+ label.contains("企业") -> friend.corpName = value
+ label.contains("部门") -> friend.department = value
+ label.contains("职位") || label.contains("职务") -> friend.position = value
+ label.contains("邮箱") -> friend.email = value
+ label.contains("备注") -> friend.markName = value
+ }
+ LogUtils.d("好友详情 - $label: $value")
+ }
+ }
+ }
+ // 尝试获取头像(如果有)
+ val avatarView = AccessibilityUtil.findOneByClazz(getRoot(), Views.ImageView)
+ if (avatarView != null) {
+ // 头像通常没有直接URL,这里留空或可通过其他方式获取
+ // friend.avatarUrl = ""
+ }
+ // 返回聊天页面
+ backPress()
+ sleep(Constant.POP_WINDOW_INTERVAL)
+ }
+ } catch (e: Exception) {
+ LogUtils.e("获取好友详情失败: ${e.message}")
+ }
+ LogUtils.d("好友信息: $friend")
+ return friend
+ }
+
+ /**
+ * 获取单聊联系人详细信息
+ */
+ private fun getChatContactInfo(friendName: String): WeworkMessageBean.Friend? {
+ val friend = WeworkMessageBean.Friend().apply {
+ name = friendName
+ }
+ try {
+ // 进入好友详情页
+ if (WeworkRoomUtil.intoFriendDetail()) {
+ sleep(Constant.CHANGE_PAGE_INTERVAL)
+ // 解析详情页信息
+ val listView = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
+ if (listView != null) {
+ for (i in 0 until listView.childCount) {
+ val item = listView.getChild(i) ?: continue
+ val textViews = AccessibilityUtil.findAllOnceByClazz(item, Views.TextView)
+ if (textViews.size >= 2) {
+ val label = textViews[0].text?.toString() ?: ""
+ val value = textViews[1].text?.toString() ?: ""
+ when {
+ label.contains("微信号") || label.contains("微信ID") -> friend.wechatId = value
+ label.contains("手机") -> friend.phone = value
+ label.contains("企业") -> friend.corpName = value
+ label.contains("部门") -> friend.department = value
+ label.contains("职位") || label.contains("职务") -> friend.position = value
+ label.contains("邮箱") -> friend.email = value
+ label.contains("备注") -> friend.markName = value
+ }
+ }
+ }
+ }
+ backPress()
+ sleep(Constant.POP_WINDOW_INTERVAL)
+ }
+ } catch (e: Exception) {
+ LogUtils.e("获取联系人详情失败: ${e.message}")
+ }
+ return friend
+ }
+
/**
* 读取聊天列表
*/
diff --git a/app/src/main/res/layout/activity_accessibility_guide.xml b/app/src/main/res/layout/activity_accessibility_guide.xml
index a1c17a2..c2aaeb1 100644
--- a/app/src/main/res/layout/activity_accessibility_guide.xml
+++ b/app/src/main/res/layout/activity_accessibility_guide.xml
@@ -23,7 +23,7 @@
android:layout_marginTop="18dp"
android:layout_marginStart="18dp"
android:layout_marginEnd="18dp"
- android:text="请开启无障碍服务,以使用Awin WorkTool主功能"
+ android:text="请开启无障碍服务,以使用AwinWorkTool主功能"
android:textColor="@color/color_333333"
android:textSize="14sp" />
@@ -150,7 +150,7 @@
android:layout_gravity="center_vertical"
android:textSize="17sp"
android:textColor="@color/qmui_config_color_black"
- android:text="使用Awin WorkTool"
+ android:text="使用AwinWorkTool"
/>
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index 0c66a70..229f569 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -73,7 +73,7 @@
android:layout_centerHorizontal="true"
android:layout_marginTop="4dp"
android:gravity="center"
- android:text="Awin WorkTool 极致的自动化体验"
+ android:text="AwinWorkTool 智能自动化体验"
android:textColor="#9affffff"
android:textSize="10sp"
tools:ignore="SmallSp" />
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 438294e..5da3342 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,8 +1,47 @@
+
+
#00BCD4
#0097A7
- #E0F7FA
+ #00BCD4
+ #00BCD4
- #96ffffff
+
+ #00BCD4
+ #0097A7
+ #26C6DA
+ #00E5FF
+
+
+ #FFFFFF
+ #F5F9FA
+ #FFFFFF
+
+
+ #212121
+ #424242
+ #757575
+ #FFFFFF
+ #C8C8C8
+
+
+ #00BCD4
+ #00838F
+ #00BCD4
+ #E0E0E0
+
+
+ #FAFAFA
+ #F5F5F5
+ #E0E0E0
+ #00000000
+ #E6E6E6
+ #D9D9D9
+ #000000
+
+
+ #4CAF50
+ #FF9800
+ #F44336
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4479448..1fcd906 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,7 +1,7 @@
- Awin WorkTool
+ AwinWorkTool
- Awin WorkTool
+ AwinWorkTool
如果需要使用或停止该应用,应用需打开 无障碍服务 进行权限设置