Compare commits

...

3 Commits

Author SHA1 Message Date
caf6ce8deb Resolved conflicts and committed changes 2025-10-28 16:51:31 +08:00
b22efdec01 Update compiled Python files 2025-10-28 15:48:25 +08:00
4ff50ecdfc 修改翻译逻辑 2025-10-28 15:46:46 +08:00
22 changed files with 382 additions and 87 deletions

7
.idea/IOS__AI.iml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
</module>

2
.idea/misc.xml generated
View File

@@ -3,5 +3,5 @@
<component name="Black">
<option name="sdkName" value="Python 3.12 (AI-IOS)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="IOSAI" project-jdk-type="Python SDK" />
</project>

View File

@@ -14,7 +14,7 @@ anchorList: list[AnchorModel] = []
# 线程锁
anchorListLock = threading.Lock()
# 打招呼数据
prologueList: list[str] = []
prologueList = {}
# 评论数据
commentList = []

View File

@@ -337,27 +337,16 @@ def passAnchorData():
try:
LogManager.method_info("关注打招呼", "关注打招呼")
data: Dict[str, Any] = request.get_json()
# 设备列表
idList = data.get("deviceList", [])
# 主播列表
acList = data.get("anchorList", [])
Variables.commentList = data.get("comment")
isComment = data.get("isComment")
LogManager.info(f"[INFO] 获取数据: {idList} {acList}")
AiUtils.save_aclist_flat_append(acList)
# 是否需要回复
needReply = data.get("needReply", False)
# 是否需要进行翻译
needTranslate = data.get("needTranslate", True)
# 获取打招呼数据
ev.prologueList = data.get("prologueList", [])
@@ -369,7 +358,7 @@ def passAnchorData():
event = threading.Event()
# 启动脚本
thread = threading.Thread(target=manager.safe_greetNewFollowers,
args=(udid, needReply, needTranslate, isComment,event))
args=(udid, needReply, isComment, event,))
# 添加到线程管理
ThreadManager.add(udid, thread, event)
return ResultData(data="").toJson()
@@ -393,8 +382,7 @@ def followAndGreetUnion():
# 是否需要回复
needReply = data.get("needReply", True)
# 是否需要进行翻译
needTranslate = data.get("needTranslate", True)
# 获取打招呼数据
ev.prologueList = data.get("prologueList", [])
@@ -407,7 +395,7 @@ def followAndGreetUnion():
event = threading.Event()
# 启动脚本
thread = threading.Thread(target=manager.safe_followAndGreetUnion,
args=(udid, needReply, needTranslate, event))
args=(udid, needReply, event))
# 添加到线程管理
ThreadManager.add(udid, thread, event)
return ResultData(data="").toJson()

271
Utils/CountryEnum.py Normal file
View File

@@ -0,0 +1,271 @@
class CountryLanguageMapper:
# 初始化一个字典,映射国家到语言代码
country_to_language = {
"中国大陆": "zh-CN",
"台湾": "zh-TW",
"香港": "zh-TW",
"澳门": "zh-TW",
"美国": "en",
"英国": "en",
"澳大利亚": "en",
"日本": "ja",
"韩国": "ko",
"俄罗斯": "ru",
"法国": "fr",
"德国": "de",
"意大利": "it",
"西班牙": "es",
"墨西哥": "es",
"巴西": "pt",
"葡萄牙": "pt",
"印度": "hi",
"泰国": "th",
"越南": "vi",
"马来西亚": "ms",
"印度尼西亚": "id",
"阿联酋": "ar",
"沙特阿拉伯": "ar",
"埃及": "ar",
"以色列": "he",
"缅甸": "my",
"斯里兰卡": "ta",
"巴基斯坦": "ur",
"孟加拉国": "bn",
"波兰": "pl",
"荷兰": "nl",
"罗马尼亚": "ro",
"土耳其": "tr",
"老挝": "lo",
"乌克兰": "uk",
"芬兰": "fi",
"南非": "af",
"阿尔巴尼亚": "sq",
"安道尔": "ca",
"安提瓜和巴布达": "en",
"阿根廷": "es",
"亚美尼亚": "hy",
"奥地利": "de",
"阿塞拜疆": "az",
"巴哈马": "en",
"巴林": "ar",
"巴巴多斯": "en",
"白俄罗斯": "be",
"比利时": "fr",
"伯利兹": "en",
"贝宁": "fr",
"不丹": "dz",
"玻利维亚": "es",
"波斯尼亚和黑塞哥维那": "bs",
"博茨瓦纳": "en",
"文莱": "ms",
"保加利亚": "bg",
"布基纳法索": "fr",
"布隆迪": "fr",
"柬埔寨": "km",
"喀麦隆": "fr",
"加拿大": "en",
"佛得角": "pt",
"开曼群岛": "en",
"中非共和国": "fr",
"乍得": "fr",
"智利": "es",
"中国": "zh-CN",
"圣诞岛": "en",
"科科斯群岛": "en",
"哥伦比亚": "es",
"科摩罗": "ar",
"刚果": "fr",
"库克群岛": "en",
"哥斯达黎加": "es",
"科特迪瓦": "fr",
"克罗地亚": "hr",
"古巴": "es",
"库拉索": "nl",
"塞浦路斯": "el",
"捷克": "cs",
"丹麦": "da",
"吉布提": "fr",
"多米尼克": "en",
"多米尼加共和国": "es",
"厄瓜多尔": "es",
"萨尔瓦多": "es",
"赤道几内亚": "es",
"厄立特里亚": "ti",
"爱沙尼亚": "et",
"埃斯瓦蒂尼": "en",
"埃塞俄比亚": "am",
"福克兰群岛": "en",
"法罗群岛": "fo",
"斐济": "en",
"法属圭亚那": "fr",
"法属波利尼西亚": "fr",
"法属南部领地": "fr",
"加蓬": "fr",
"冈比亚": "en",
"格鲁吉亚": "ka",
"加纳": "en",
"直布罗陀": "en",
"希腊": "el",
"格陵兰": "kl",
"格林纳达": "en",
"瓜德罗普": "fr",
"关岛": "en",
"危地马拉": "es",
"根西岛": "en",
"几内亚": "fr",
"几内亚比绍": "pt",
"圭亚那": "en",
"海地": "fr",
"赫德岛和麦克唐纳群岛": "en",
"梵蒂冈": "it",
"洪都拉斯": "es",
"中国香港特别行政区": "zh-TW",
"匈牙利": "hu",
"冰岛": "is",
"伊朗": "fa",
"伊拉克": "ar",
"爱尔兰": "en",
"曼岛": "en",
"牙买加": "en",
"泽西岛": "en",
"约旦": "ar",
"哈萨克斯坦": "kk",
"肯尼亚": "en",
"基里巴斯": "en",
"朝鲜": "ko",
"科威特": "ar",
"吉尔吉斯斯坦": "ky",
"拉脱维亚": "lv",
"黎巴嫩": "ar",
"莱索托": "en",
"利比里亚": "en",
"利比亚": "ar",
"列支敦士登": "de",
"立陶宛": "lt",
"卢森堡": "fr",
"中国澳门特别行政区": "zh-TW",
"马达加斯加": "fr",
"马拉维": "en",
"马尔代夫": "dv",
"马里": "fr",
"马耳他": "mt",
"马绍尔群岛": "en",
"马提尼克": "fr",
"毛里塔尼亚": "ar",
"毛里求斯": "en",
"马约特": "fr",
"密克罗尼西亚": "en",
"摩尔多瓦": "ro",
"摩纳哥": "fr",
"蒙古": "mn",
"黑山": "sr",
"蒙特塞拉特": "en",
"摩洛哥": "ar",
"莫桑比克": "pt",
"纳米比亚": "en",
"瑙鲁": "en",
"尼泊尔": "ne",
"新喀里多尼亚": "fr",
"新西兰": "en",
"尼加拉瓜": "es",
"尼日尔": "fr",
"尼日利亚": "en",
"纽埃": "en",
"诺福克岛": "en",
"北马其顿": "mk",
"北马里亚纳群岛": "en",
"挪威": "no",
"阿曼": "ar",
"帕劳": "en",
"巴勒斯坦": "ar",
"巴拿马": "es",
"巴布亚新几内亚": "en",
"巴拉圭": "es",
"秘鲁": "es",
"菲律宾": "tl",
"皮特凯恩群岛": "en",
"波多黎各": "es",
"卡塔尔": "ar",
"留尼汪": "fr",
"卢旺达": "rw",
"圣巴泰勒米": "fr",
"圣赫勒拿": "en",
"圣基茨和尼维斯": "en",
"圣卢西亚": "en",
"法属圣马丁": "fr",
"圣皮埃尔和密克隆": "fr",
"圣文森特和格林纳丁斯": "en",
"萨摩亚": "sm",
"圣马力诺": "it",
"圣多美和普林西比": "pt",
"塞内加尔": "fr",
"塞尔维亚": "sr",
"塞舌尔": "fr",
"塞拉利昂": "en",
"新加坡": "zh-CN",
"荷属圣马丁": "nl",
"斯洛伐克": "sk",
"斯洛文尼亚": "sl",
"所罗门群岛": "en",
"索马里": "so",
"南乔治亚岛和南桑威奇群岛": "en",
"南苏丹": "en",
"苏丹": "ar",
"苏里南": "nl",
"斯瓦尔巴群岛和扬马延岛": "no",
"瑞典": "sv",
"瑞士": "de",
"叙利亚": "ar",
"台湾省": "zh-TW",
"塔吉克斯坦": "tg",
"坦桑尼亚": "sw",
"东帝汶": "tet",
"多哥": "fr",
"托克劳": "en",
"汤加": "to",
"特立尼达和多巴哥": "en",
"突尼斯": "ar",
"土库曼斯坦": "tk",
"特克斯和凯科斯群岛": "en",
"图瓦卢": "en",
"乌干达": "en",
"美国本土外小岛屿": "en",
"乌拉圭": "es",
"乌兹别克斯坦": "uz",
"瓦努阿图": "bi",
"委内瑞拉": "es",
"英属维尔京群岛": "en",
"美属维尔京群岛": "en",
"瓦利斯和富图纳": "fr",
"西撒哈拉": "ar",
"也门": "ar",
"赞比亚": "en",
"津巴布韦": "en",
"阿富汗": "fa",
"阿尔及利亚": "ar",
"美属萨摩亚": "en",
"安哥拉": "pt",
"安圭拉": "en",
"南极洲": "en",
"百慕大": "en",
"荷属加勒比区": "nl",
"布韦岛": "no",
"英属印度洋领地": "en",
}
@classmethod
def get_language_code(cls, country):
return cls.country_to_language.get(country)
# 使用示例
if __name__ == "__main__":
mapper = CountryLanguageMapper()
countries = ['英国', '美国', '日本', '未知国家']
for country in countries:
code = mapper.get_language_code(country)
if code:
print(f"{country} 对应的语言代码是 {code}")
else:
print(f"没有找到 {country} 对应的语言代码")

View File

@@ -10,6 +10,7 @@ from datetime import datetime
from Entity import Variables
from Utils.AiUtils import AiUtils
from Utils.ControlUtils import ControlUtils
from Utils.CountryEnum import CountryLanguageMapper
from Utils.IOSAIStorage import IOSAIStorage
from Utils.JsonUtils import JsonUtils
from Utils.LogManager import LogManager
@@ -409,11 +410,11 @@ class ScriptManager():
关注打招呼以及回复主播消息
"""
def safe_greetNewFollowers(self, udid, needReply, needTranslate, isComment, event):
def safe_greetNewFollowers(self, udid, needReply, isComment, event):
retries = 0
while not event.is_set():
try:
self.greetNewFollowers(udid, needReply, needTranslate, isComment,event)
self.greetNewFollowers(udid, needReply, isComment, event)
except Exception as e:
retries += 1
@@ -426,7 +427,7 @@ class ScriptManager():
LogManager.method_error("greetNewFollowers 重试次数耗尽,任务终止", "关注打招呼", udid)
# 关注打招呼
def greetNewFollowers(self, udid, needReply, needTranslate, isComment, event):
def greetNewFollowers(self, udid, needReply, isComment, event):
client = wda.USBClient(udid, ev.wdaFunctionPort)
session = client.session()
@@ -434,18 +435,14 @@ class ScriptManager():
print(f"是否要自动回复消息:{needReply}")
LogManager.method_info(f"是否要自动回复消息:{needReply}", "关注打招呼", udid)
LogManager.method_info(f"是否需要进行翻译:{needTranslate}", "关注打招呼", udid)
# 先关闭Tik Tok
ControlUtils.closeTikTok(session, udid)
if self.interruptible_sleep(event, 1):
return
event.wait(timeout=1)
# 重新打开Tik Tok
ControlUtils.openTikTok(session, udid)
if self.interruptible_sleep(event, 3): # [ADD]
return
event.wait(timeout=3)
LogManager.method_info(f"重启tiktok", "关注打招呼", udid)
# 设置查找深度
@@ -460,8 +457,7 @@ class ScriptManager():
LogManager.method_info(f"返回上一步", "关注打招呼", udid)
session.appium_settings({"snapshotMaxDepth": 15})
ControlUtils.clickBack(session)
if self.interruptible_sleep(event, 2): # [ADD]
return
event.wait(timeout=2)
LogManager.method_info(f"循环条件1:{not event.is_set()}", "关注打招呼", udid)
LogManager.method_info(f"循环条件2:{len(anchorList) > 0}", "关注打招呼", udid)
@@ -476,6 +472,7 @@ class ScriptManager():
# 获取一个主播,
LogManager.method_info(f"开始获取数据", "关注打招呼", udid)
# 获取一个主播,
result = AiUtils.peek_aclist_first()
LogManager.method_info(f"数据是:{result}", "关注打招呼", udid)
@@ -493,21 +490,17 @@ class ScriptManager():
if not anchor:
LogManager.method_info(f"数据库中的数据不足", "关注打招呼", udid)
# 你原来的写法:等待完成就 continue中途被打断就 return
if not self.interruptible_sleep(event, 30):
continue
return # [ADD] 被打断则退出
aid = anchor.get("anchorId", "")
anchorCountry = anchor.get("country", "")
LogManager.method_info(f"主播的数据,用户名:{aid},国家:{anchorCountry}", "关注打招呼", udid)
if self.interruptible_sleep(event, 1):
return
# 点击搜索按钮
# 点击搜索按钮
ControlUtils.clickSearch(session)
LogManager.method_info(f"点击搜索按钮", "关注打招呼", udid)
# 强制刷新session
@@ -519,9 +512,8 @@ class ScriptManager():
# 如果找到了输入框,就点击并且输入内容
if input.exists:
input.click()
# 稍作停顿(用你的可中断等待)
if self.interruptible_sleep(event, 0.5): # [ADD]
return
# 稍作停顿
event.wait(timeout=0.5)
else:
print(f"找不到输入框")
raise Exception("找不到输入框")
@@ -529,12 +521,12 @@ class ScriptManager():
input = session.xpath('//XCUIElementTypeSearchField')
if input.exists:
input.clear_text()
if self.interruptible_sleep(event, 1): # [ADD]
return
event.wait(timeout=1)
# 输入主播id
input.set_text(f"{aid or '暂无数据'}\n")
# 定位 "关注" 按钮 通过关注按钮的位置点击主播首页
session.appium_settings({"snapshotMaxDepth": 25})
try:
@@ -549,15 +541,12 @@ class ScriptManager():
session.appium_settings({"snapshotMaxDepth": 15})
continue
if self.interruptible_sleep(event, 2): # [ADD]
return
event.wait(timeout=2)
# 找到并点击第一个视频
cellClickResult, workCount = ControlUtils.clickFirstVideoFromDetailPage(session)
LogManager.method_info(f"点击第一个视频", "关注打招呼", udid)
if self.interruptible_sleep(event, 2): # [ADD]
return
LogManager.method_info(f"点击第一个视频", "关注打招呼", udid)
event.wait(timeout=2)
# 观看主播视频
def viewAnchorVideo(workCount):
@@ -575,12 +564,12 @@ class ScriptManager():
LogManager.method_info("停止脚本中", method="task")
if event.is_set():
break
if self.interruptible_sleep(event, 1): # [ADD]
return
event.wait(timeout=1)
LogManager.method_info("停止脚本成功", method="task")
img = client.screenshot()
if self.interruptible_sleep(event, 1): # [ADD]
return
event.wait(timeout=1)
# filePath = f"resources/{udid}/bgv.png"
base_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) # 当前脚本目录的上一级
filePath = os.path.join(base_dir, "resources", udid, "bgv.png")
@@ -591,9 +580,7 @@ class ScriptManager():
img.save(filePath)
LogManager.method_info("保存屏幕图像成功", "关注打招呼", udid)
if self.interruptible_sleep(event, 2): # [ADD]
return
event.wait(timeout=2)
# 查找add图标
r = ControlUtils.clickLike(session, udid)
@@ -604,8 +591,7 @@ class ScriptManager():
LogManager.method_info("停止脚本中", method="task")
if event.is_set():
break
if self.interruptible_sleep(event, 1): # [ADD]
return
event.wait(timeout=1)
LogManager.method_info("停止脚本成功", method="task")
# 使用OCR进行评论
@@ -626,20 +612,16 @@ class ScriptManager():
# 观看主播视频
LogManager.method_info("去查看主播视频", "关注打招呼", udid)
viewAnchorVideo(workCount)
if self.interruptible_sleep(event, 3): # [ADD]
return
event.wait(timeout=3)
LogManager.method_info("视频看完了,重置试图查询深度", "关注打招呼", udid)
session.appium_settings({"snapshotMaxDepth": 25})
if self.interruptible_sleep(event, 0.5): # [ADD]
return
event.wait(timeout=0.5)
# 向上滑动
ControlUtils.swipe_down(udid)
if self.interruptible_sleep(event, 2): # [ADD]
return
event.wait(timeout=2)
msgButton = AiUtils.getSendMesageButton(session)
if self.interruptible_sleep(event, 2): # [ADD]
return
event.wait(timeout=2)
if msgButton.exists:
# 进入聊天页面
@@ -653,8 +635,7 @@ class ScriptManager():
session.appium_settings({"snapshotMaxDepth": 15})
continue
if self.interruptible_sleep(event, 3): # [ADD]
return
event.wait(timeout=3)
# 查找聊天界面中的输入框节点
chatInput = session.xpath("//TextView")
if chatInput.exists:
@@ -663,12 +644,31 @@ class ScriptManager():
LogManager.method_info("找到输入框了, 准备发送一条打招呼消息", "关注打招呼", udid)
print("打招呼的数据", ev.prologueList)
LogManager.method_info(f"传递的打招呼的数据:{ev.prologueList}", "关注打招呼", udid)
# LogManager.method_info(f"传递的打招呼的数据:{ev.prologueList}", "关注打招呼", udid)
# 取出国家进行对应国家语言代码
anchorCountry_code = CountryLanguageMapper.get_language_code(anchorCountry)
print(anchorCountry_code)
print("存储的是:",ev.prologueList)
# 判断对应的语言代码是否在传入的字典中
if anchorCountry_code in ev.prologueList:
# 进行原本的进行传入
privateMessageList = ev.prologueList[anchorCountry_code]
needTranslate = False
else:
# 需要翻译
privateMessageList = ev.prologueList['yolo']
needTranslate = True # 使用yolo必须翻译
# 准备打招呼的文案
text = random.choice(ev.prologueList)
text = random.choice(privateMessageList)
LogManager.method_info(f"取出打招呼的数据,{text}, 判断是否需要翻译:{needTranslate}", "关注打招呼",
LogManager.method_info(f"取出打招呼的数据,{text}", "关注打招呼",
udid)
if needTranslate:
@@ -685,10 +685,10 @@ class ScriptManager():
if chatInput.exists:
chatInput.click()
chatInput.set_text(f"{msg or '暂无数据'}\n")
if self.interruptible_sleep(event, 2): # [ADD]
return
if self.interruptible_sleep(event, 1): # [ADD]
return
event.wait(timeout=2)
# 发送消息
# input.set_text(f"{aid or '暂无数据'}\n")
event.wait(timeout=1)
else:
print("无法发送信息")
LogManager.method_info(f"给主播{aid} 发送消息失败", "关注打招呼", udid)
@@ -696,6 +696,27 @@ class ScriptManager():
# 接着下一个主播
goBack(1)
# 点击关注按钮
# followButton = AiUtils.getFollowButton(session).get(timeout=5)
# if followButton is not None:
# # LogManager.method_info("找到关注按钮了", "关注打招呼", udid)
# # followButton.click()
# x, y, w, h = followButton.bounds
# cx = int(x + w / 2)
# cy = int(y + h / 2)
# # 随机偏移 ±5 px可自己改范围
# cx += random.randint(-5, 5)
# cy += random.randint(-5, 5)
#
# session.click(cx, cy)
#
# else:
# LogManager.method_info("没找到关注按钮", "关注打招呼", udid)
# time.sleep(1)
# goBack(4)
# session.appium_settings({"snapshotMaxDepth": 15})
# continue
session.appium_settings({"snapshotMaxDepth": 15})
goBack(3)
@@ -708,8 +729,7 @@ class ScriptManager():
# 设置查找深度
session.appium_settings({"snapshotMaxDepth": 15})
if self.interruptible_sleep(event, 2): # [ADD]
return
event.wait(timeout=2)
print("即将要回复消息")
LogManager.method_info("即将要回复消息", "关注打招呼", udid)
@@ -726,11 +746,9 @@ class ScriptManager():
homeButton.click()
else:
ControlUtils.closeTikTok(session, udid)
if self.interruptible_sleep(event, 2): # [ADD]
return
event.wait(timeout=2)
ControlUtils.openTikTok(session, udid)
if self.interruptible_sleep(event, 3): # [ADD]
return
event.wait(timeout=3)
print("重新创建wda会话 防止wda会话失效")
client = wda.USBClient(udid, ev.wdaFunctionPort)
@@ -742,12 +760,12 @@ class ScriptManager():
print("greetNewFollowers方法执行完毕")
def safe_followAndGreetUnion(self, udid, needReply, needTranslate, event):
def safe_followAndGreetUnion(self, udid, needReply, event):
retries = 0
while not event.is_set():
try:
self.followAndGreetUnion(udid, needReply, needTranslate, event)
self.followAndGreetUnion(udid, needReply, event)
except Exception as e:
retries += 1
@@ -760,7 +778,7 @@ class ScriptManager():
LogManager.method_error("greetNewFollowers 重试次数耗尽,任务终止", "关注打招呼", udid)
# 关注打招呼以及回复主播消息(联盟号)
def followAndGreetUnion(self, udid, needReply, needTranslate, event):
def followAndGreetUnion(self, udid, needReply, event):
client = wda.USBClient(udid, ev.wdaFunctionPort)
session = client.session()
@@ -849,6 +867,8 @@ class ScriptManager():
event.wait(timeout=0.5)
else:
print(f"找不到输入框")
raise Exception("找不到输入框")
input = session.xpath('//XCUIElementTypeSearchField')
if input.exists:
@@ -904,16 +924,25 @@ class ScriptManager():
print("找到输入框了, 准备发送一条打招呼消息")
LogManager.method_info("找到输入框了, 准备发送一条打招呼消息", "关注打招呼(联盟号)", udid)
print("打招呼的数据", ev.prologueList)
LogManager.method_info(f"传递的打招呼的数据:{ev.prologueList}", "关注打招呼(联盟号)", udid)
# 取出国家进行对应国家语言代码
anchorCountry_code = CountryLanguageMapper.get_language_code(anchorCountry)
print(anchorCountry_code)
print("存储的是:", ev.prologueList)
# 判断对应的语言代码是否在传入的字典中
if anchorCountry_code in ev.prologueList:
# 进行原本的进行传入
privateMessageList = ev.prologueList[anchorCountry_code]
needTranslate = False
else:
# 需要翻译
privateMessageList = ev.prologueList['yolo']
needTranslate = True # 使用yolo必须翻译
# 准备打招呼的文案
text = random.choice(ev.prologueList)
# text = '你好'
text = random.choice(privateMessageList)
LogManager.method_info(f"取出打招呼的数据,{text}, 判断是否需要翻译", "关注打招呼(联盟号)", udid)
isContainChniese = AiUtils.contains_chinese(text)
if needTranslate:
# 翻译成主播国家的语言