"""抖音点赞操作。 两种方式: by_element — 用 xpath 找右侧红心按钮点击(相对定位,不依赖视频卡片序号) double_click — 双击屏幕中央(需明确选择此方式,抖音"双击点赞"手势) by_element 找不到点赞按钮时**跳过本次,不点屏幕**(避免直播/无按钮页误触)。 要适配新版本抖音:用 weditor 重新抓当前界面,调整下面定位逻辑。 """ from core.actions import BaseAction, register_action, should_trigger from core.logger import get_logger from . import ACTIONS _log = get_logger("action.like") @register_action(ACTIONS) class LikeAction(BaseAction): action_type = "like" name = "点赞" description = "看完视频后随机点赞(找红心按钮,找不到回退双击)" default_params = { "rate": 0.3, # 触发概率 0~1 "method": "by_element", # by_element=找红心 | double_click=双击屏幕 } def execute(self, d, params, worker): rate = float(params.get("rate", 0.3)) if not should_trigger(rate): return False method = params.get("method", "by_element") if method == "by_element": if self._like_by_element(d): worker.set_action("点赞(红心)") return True # 找不到点赞按钮:跳过本次,不点屏幕,避免误触(如直播/无点赞按钮的页面) worker.set_action("点赞(未找到,跳过)") return False # 仅在明确选择 double_click 方式时才双击屏幕 ok = self._double_click_center(d) if ok: worker.set_action("点赞(双击)") return ok @staticmethod def _like_by_element(d): """找右侧操作栏红心按钮点击,成功返回 True。 定位优先级:description 精确 → descriptionContains 模糊 → resourceId → xpath 每种方式找到即点即返回,找不到继续下一种。全程记日志。 """ # 方式1:description 精确匹配(最稳,不依赖结构) for desc in ("点赞", "未点赞", "like"): el = d(description=desc) if el.exists: el.click() _log.info(f"like 命中 description={desc}") return True # 方式2:descriptionContains 模糊匹配(适配不同版本文案) for kw in ("赞", "like"): el = d(descriptionContains=kw) if el.exists: el.click() _log.info(f"like 命中 descriptionContains={kw}") return True # 方式3:resourceId 列表(扩充候选,覆盖更多抖音版本) for rid in ("com.ss.android.ugc.aweme:id/aky", "com.ss.android.ugc.aweme:id/d-like-view-icon", "com.ss.android.ugc.aweme:id/h3r", "com.ss.android.ugc.aweme:id/dfz", "com.ss.android.ugc.aweme:id/c5w"): el = d(resourceId=rid) if el.exists: el.click() _log.info(f"like 命中 resourceId={rid}") return True # 方式4:xpath 相对定位——右侧操作栏 LinearLayout 的第一个 ImageView # 定位思路:ViewPager 下当前 FrameLayout 里的 LinearLayout, # 取其直接子 FrameLayout 里的第一个 ImageView(点赞在操作栏最上方) try: el = d.xpath( '//androidx.viewpager.widget.ViewPager' '/android.widget.FrameLayout' '/com.bytedance.highperformanceview.layout.MeasureOnceRelativeLayout2' '/com.bytedance.highperformanceview.layout.MeasureOnceRelativeLayout2' '/android.widget.LinearLayout/android.widget.FrameLayout[2]' '/android.widget.LinearLayout/android.widget.FrameLayout[1]' '/android.widget.FrameLayout[1]/android.widget.ImageView' ) if el.exists: el.click() _log.info("like 命中 xpath") return True except Exception as e: _log.warning(f"like xpath 异常: {e}") _log.info("like_by_element 全部失败,准备回退双击") return False @staticmethod def _double_click_center(d): """双击屏幕中央点赞。按屏幕尺寸算坐标,适配不同分辨率。""" try: info = d.info w, h = info["displayWidth"], info["displayHeight"] d.double_click(int(w * 0.5), int(h * 0.5)) return True except Exception: return False