• CyberController手机外挂番外篇:源代码的二次修改


    前言

    本教程是 CyberController手机外挂 的番外篇,- CyberController的源码二次修改

    • 之前已经可以跑通demo了,但是有一些功能不符合自己的需求,需要进行进一步的修改,这部分因为涉及到源码修改,我没有办法仔细的讲解为什么那么修改,所以就需要读者有一定的Python基础了

    调试过程中的疑问

    为什么一段时间不使用CyberController,翻译就无法触发了?

    • 这个时候再重新在手机上进行一下语音输入,就又可以进行翻译了
    • 因为这个时候连接报错了
      • img
    • 应该是一段时间无使用,那么安卓系统就会好,这个软件和电脑端的TCP连接给杀掉,那么肯定无法进行翻译了。而当手机端使用语音输入的时候,相当于手机端主动和电脑端建立TCP连接,这个时候就可以再次正常进行翻译了
    • 解决方法其实很简单,只需要设置电池优化白名单

    为什么连接成功了,但却依然无法进行语音识别和翻译?

    • 现象
      • img
      • img
    • 可能单纯的是网络不好吧
      • img
      • 查看一下手机能否打开其他网址,如果打不开其他网址的话,就重启一下手机
        • img
      • 查看一下代理延迟是不是太高了
        • 重新选择一个延迟比较低的节点
        • img
        • 之后就可以正常语音识别了
        • img
      • 2022.11.18-20:48:29
      • 重启一下手机之后,发现正常了
        • img
    • 可能是因为开了电池优化白名单
      • 我发现每次只要一开这个电池优化排名单,马上就无法联网了

    多长时间TCP连接就会挂掉

    • 两分钟就会挂掉
    • Android中socket通信连接时长大概为120s,如果超过这个时间没有操作,就会断开连接

    连接正常与断开连接有什么区别?

    • 连接正常
    • img
    • 断开连接

    不停进行翻译,会断掉TCP连接吗?

    • 不会
      • 自己每30秒翻译一次,坚持了11分钟还没断掉
        • img
        • img

    如何解决120秒socket通讯连接断掉的问题?

    • 这个暂时未解决,下面的方案,是网络上给出的解决方法

    • 手机端每隔一段时间往服务器转发一条不被解析的命令,这样可以使这个socket通讯一直保持畅通(但是自己对于java的编程不太擅长,如果有同学可以完成这部分工作的话,那就非常棒了)

    • 通过PowerManager设置电源模式

      • Android SocketClient休眠断开的问题 - 简书

      • PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); 
        PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag"); 
        wl.acquire(); 
        ..screen will stay on during this section.. 
        wl.release();
        
        • 1
        • 2
        • 3
        • 4
        • 5
      • 因为app使用activity,故在oncreate中wl.acquire();,在ondestroy中wl.release();

      • 加权限:android.permission.WAKE_LOCK

    为什么可以进行翻译,但是无法进行语音输入?

    • 可以在手机端识别到对应的语音输入内容,但是无法发送到电脑端
    • 因为电脑端出错了
    • img
    • 在Controller.py文件中添加一行代码
    • from KeyboardManager import *
    • img
    • 之后就可以成功了

    NameError: name ‘key_down’ is not defined

    • 出现这个错误,一般是在手机和电脑第一次建立连接的时候,不用管它,继续运行即可

    • img

    • 之后会发现,就算有这个错误,也不影响运行

      • img
      • img
      • 如果非要找原因的话,我们可以定位到对应的代码处查看
        • img
        • 应该是这个key_down按键脚本找不到了
          • img
        • 但实际上,在KeyboardListener.py文件中,这个函数是存在的
          • img
        • 我们新建一个测试脚本,把下面的代码输入进去,看能否运行成功
          • img
          • 但实际上是可以运行成功的。那么可能的原因应该就是因为这个key_down()函数是放在exec()字符中的,之前PyCharm自动清理的时候,将from KeyboardManager import * 这行代码清理掉了(因为PyCharm认为这个包没有用到,所以是无用的)
            • img
          • 因此我们在原始代码中,补充上这行代码就好了
            • img

    修改

    修改翻译的触发快捷键

    • 这里是使用了三次Ctrl+c的快捷键作为触发,但是每次想要翻译一个单词,都需要按三次键,感觉太繁琐,因此我将其修改为了使用一个Ctrl+Q的快捷键作为触发,只需要修改下面的代码

    • 将KeyboardListener.py-onKeyEvent()函数中的if修改为下面的代码

    • img

    • img

    • import pyautogui 
      if key.event_type == "down" \ 
              and key.name.lower() == "q" \ 
              and self.isCtrlHolding(): 
          pyautogui.hotkey('ctrl', 'c') 
       
          print("need trans") 
          if self.callback: 
              self.callback()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • KeyboardListener.py的完整代码如下

    • import keyboard 
      import time 
      from screen_shot import ScreenCapture 
      import io 
      import pyautogui 
       
      class KeyboardListener: 
      	def __init__(self, tcpServer): 
      		self.tcpServer = tcpServer 
      		self.t = 0 
      		self.c = 0 
      		self.key_state_map={} 
      		self.screen_capture = None 
      		 
      	def listen_keyboard(self,callback): 
      		self.callback = callback 
      		keyboard.hook(self.onKeyEvent) 
      		keyboard.wait() 
       
      	def onImgCapture(self,pic):	 
      		imgByteArr = io.BytesIO() 
      		pic.save(imgByteArr, format='JPEG') 
      		bytes_data = imgByteArr.getvalue() 
      		self.tcpServer.send_img(bytes_data) 
       
      	def isCtrlHolding(self): 
      		return ('ctrl' in self.key_state_map and self.key_state_map['ctrl']=='down')\ 
      			or ('left ctrl' in self.key_state_map and self.key_state_map['left ctrl']=='down')\ 
      			or ('right ctrl' in self.key_state_map and self.key_state_map['right ctrl']=='down') 
       
      	def isAltHolding(self): 
      		return ('alt' in self.key_state_map and self.key_state_map['alt']=='down')\ 
      			or ('left alt' in self.key_state_map and self.key_state_map['left alt']=='down')\ 
      			or ('right alt' in self.key_state_map and self.key_state_map['right alt']=='down') 
       
      	def isKeyHolding(self,key): 
      		return (key in self.key_state_map and self.key_state_map[key]=='down') 
       
       
      	def onKeyEvent(self,key): 
      		#update key_state_map 
      		self.key_state_map[key.name.lower()]=key.event_type 
       
      		#is screenshoot? 
       
      		if  self.isKeyHolding("caps lock")\ 
      			and key.event_type=="down"\ 
      			and key.name.lower()=="a": 
      			self.screen_capture = ScreenCapture() 
      			self.screen_capture.are_capture(self.onImgCapture) 
       
      		#print(self.key_state_map) 
      		#is triple c? 
      		# if  key.event_type=="down" \ 
      		# 	and key.name.lower()=="c" \ 
      		# 	and self.isCtrlHolding(): 
      		# 
      		# 	if self.t == 0: 
      		# 		self.t=time.time() 
      		# 		self.c += 1 
      		# 		print("wait for nex c",self.c) 
      		# 		return 
      		# 
      		# 	if (time.time()-self.t<0.5): 
      		# 		self.t=time.time() 
      		# 		self.c += 1 
      		# 		print("wait for nex c:",self.c) 
      		# 
      		# 	else: 
      		# 		self.c = 0 
      		# 		self.t=0 
      		# 		print("wait for nex c",self.c) 
      		# 
      		# 	if self.c>=2: 
      		# 		self.c=0 
      		# 		print("need trans") 
      		# 		if self.callback: 
      		# 			self.callback() 
       
       
       
       
      		if  key.event_type=="down" \ 
      			and key.name.lower()=="q" \ 
      			and self.isCtrlHolding(): 
      			pyautogui.hotkey('ctrl', 'c') 
       
      			print("need trans") 
      			if self.callback: 
      				self.callback()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81
      • 82
      • 83
      • 84
      • 85
      • 86
      • 87
      • 88
      • 89
      • 90

    关掉不停的打印按键

    • 将这行代码注释掉即可
    • img

    关掉手机上麦克风图标

    • 自己并不想要有这个麦克风的图标
    • img

    电脑端翻译的代码是哪一段?

    • 这个onTrans()函数
    • img

    手动往手机端发送数据

    • 程序不能直接运行,必须得有一个时间等待,否则手机和电脑端的TCP连接还没有建立上,会发送数据失败

    • img

    • import json 
      import time 
       
      from ComputerMonitor import ComputerMonitor 
      from KeyboardListener import KeyboardListener 
      from TcpServer import TcpServer 
      from service import * 
       
       
      def on_message_received(data): 
      	command_message = json.loads(data) 
      	script = command_message["script"] 
      	params = command_message["params"] 
      	exec(script) 
       
      def on_screen_locked(): 
      	print("screen locked") 
      	data = json.dumps({"command":2,"message":""}) 
      	print(data) 
      	tcpServer.send_text(data) 
       
      computerMonitor = ComputerMonitor(on_screen_locked) 
       
      def on_tcp_connected(): 
      	if not computerMonitor.started: 
      		computerMonitor.start() 
       
       
      tcpServer = TcpServer() 
      tcpServer.set_receive_listener(on_message_received) 
      tcpServer.connected_listener = on_tcp_connected 
      tcpServer.start() 
       
      keyboardListener = KeyboardListener(tcpServer) 
       
      def onTrans(): 
      	print("need trans1111") 
      	content = getClipContent() 
      	text = json.dumps({"command":1,"message":content}) 
       
      	tcpServer.send_text(text) 
       
       
      def Trans_alive():		#用来进行TCP保活 
      	print("need trans1111") 
      	content = getClipContent() 
      	text = json.dumps({"command":1,"message":content}) 
       
      	tcpServer.send_text(text) 
       
      #keyboardListener.listen_keyboard(onTrans) 
      time.sleep(5) 
      onTrans()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53

    手动进行保活

    • 原理很简单,就是电脑端和手机端发空文本

    • import json 
      import time 
       
      from ComputerMonitor import ComputerMonitor 
      from KeyboardListener import KeyboardListener 
      from TcpServer import TcpServer 
      from service import * 
       
       
      def on_message_received(data): 
      	command_message = json.loads(data) 
      	script = command_message["script"] 
      	params = command_message["params"] 
      	exec(script) 
       
      def on_screen_locked(): 
      	print("screen locked") 
      	data = json.dumps({"command":2,"message":""}) 
      	print(data) 
      	tcpServer.send_text(data) 
       
      computerMonitor = ComputerMonitor(on_screen_locked) 
       
      def on_tcp_connected(): 
      	if not computerMonitor.started: 
      		computerMonitor.start() 
       
       
      tcpServer = TcpServer() 
      tcpServer.set_receive_listener(on_message_received) 
      tcpServer.connected_listener = on_tcp_connected 
      tcpServer.start() 
       
      keyboardListener = KeyboardListener(tcpServer) 
       
      def onTrans(): 
      	print("need trans1111") 
      	content = getClipContent() 
      	text = json.dumps({"command":1,"message":content}) 
       
      	tcpServer.send_text(text) 
       
       
      def Trans_alive():		#用来进行TCP保活 
      	print("need trans1111") 
      	content = '' 
      	text = json.dumps({"command":1,"message":content}) 
       
      	tcpServer.send_text(text) 
       
      #keyboardListener.listen_keyboard(onTrans) 
      time.sleep(5) 
      Trans_alive()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53

    自动进行保活

    • 在Controller.py进入键盘监听循环之前添加下面一段代码

      • def Trans_alive():		#用来进行TCP保活 
        	content = '' 
        	# text = json.dumps({"command":1,"message":content}) 
        	text = json.dumps({"command":11,"message":content})		#这里之所以用11,而不是1,是因为原作者的command1对应的命令是翻译,会在手机端触发对应的翻译任务看,而11就只相当于是一条空命令了,既可以完成保活,又不会干扰手机端的命令执行,一举两得 
        	print("text:", text) 
        	tcpServer.send_text(text) 
         
        def run(): 
        	print('用来保活的,不用管我') 
        	t = threading.Timer(3, run) 
        	t.start() 
         
        	Trans_alive() 
         
         
        t = threading.Timer(3,run) 
        t.start()
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
      • img

    • 完整Controller.py代码

      • import json 
        import time 
         
        from ComputerMonitor import ComputerMonitor 
        from KeyboardListener import KeyboardListener 
        from TcpServer import TcpServer 
        from service import * 
        import threading 
         
        def on_message_received(data): 
        	command_message = json.loads(data) 
        	script = command_message["script"] 
        	params = command_message["params"] 
        	exec(script) 
         
        def on_screen_locked(): 
        	print("screen locked") 
        	data = json.dumps({"command":2,"message":""}) 
        	print(data) 
        	tcpServer.send_text(data) 
         
        computerMonitor = ComputerMonitor(on_screen_locked) 
         
        def on_tcp_connected(): 
        	if not computerMonitor.started: 
        		computerMonitor.start() 
         
         
        tcpServer = TcpServer() 
        tcpServer.set_receive_listener(on_message_received) 
        tcpServer.connected_listener = on_tcp_connected 
        tcpServer.start() 
         
        keyboardListener = KeyboardListener(tcpServer) 
         
        def onTrans(): 
        	print("need trans1111") 
        	content = getClipContent() 
        	text = json.dumps({"command":1,"message":content}) 
         
        	tcpServer.send_text(text) 
         
         
        def Trans_alive():		#用来进行TCP保活 
        	content = '' 
        	# text = json.dumps({"command":1,"message":content}) 
        	text = json.dumps({"command":11,"message":content})		#这里之所以用11,而不是1,是因为原作者的command1对应的命令是翻译,会在手机端触发对应的翻译任务看,而11就只相当于是一条空命令了,既可以完成保活,又不会干扰手机端的命令执行,一举两得 
        	print("text:", text) 
        	tcpServer.send_text(text) 
         
        def run(): 
        	print('用来保活的,不用管我') 
        	t = threading.Timer(3, run) 
        	t.start() 
         
        	Trans_alive() 
         
         
        t = threading.Timer(3,run) 
        t.start() 
         
        keyboardListener.listen_keyboard(onTrans)
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43
        • 44
        • 45
        • 46
        • 47
        • 48
        • 49
        • 50
        • 51
        • 52
        • 53
        • 54
        • 55
        • 56
        • 57
        • 58
        • 59
        • 60
        • 61
        • 62
    • 效果

      • 这样只要电脑和手机连着同一局域网的WiFi,无论过多久都不会把这个TCP连接来杀掉

    • 额外说明

      • 使用command为1进行发送的结果
        • 可以看到发送的字符串虽然没空,但终究是要进行翻译任务的,所以会把麦克风给掩盖掉,我感觉效果并不好了
        • img
      • 使用command为11进行发送的结果
        • 这个结果就很nice,也很干净了,压根儿查看不出来后台进行了数据发送与接收用来进行TCP socket保活
        • img
  • 相关阅读:
    56资源网系统源码搭建知识付费-含源码
    springBoot 源码五:springboot启动源码补充和配置优先级
    回溯法实现全排列(leetcode46)
    shell脚本基础(1):shell快速入门:shell概述与第一个shell脚本 hello shell
    paddle 复现ResNet模型(只关注模型)
    WebVR
    uniapp小程序:使用uni.getLocation通过腾讯地图获取相关地址信息详情(超详细)
    【kubernetes】kubernetes中的Controller
    【目标检测】SPP-Net论文理解(超详细版本)
    信息学奥赛一本通:1104:计算书费
  • 原文地址:https://blog.csdn.net/u014723479/article/details/128059496