lol 自动接受对局

查看 115|回复 11
作者:rxlrxl012   
之前一直在用 Seraphine, 停更后最近也开始异常报错,由于一般也只需要自动接受对局这个功能,于是就写了这个.只有自动接受对局,个人感觉还是可以.所以拿出来分享.
[Python] 纯文本查看 复制代码import subprocess
import re
import base64
import json
import time
import urllib.request
import urllib.error
import ssl
import sys
import os
import threading
import tkinter as tk
from tkinter import ttk, messagebox
def is_admin():
    """
    检查程序是否以管理员权限运行
    """
    try:
        return os.getuid() == 0
    except AttributeError:
        # Windows系统
        import ctypes
        return ctypes.windll.shell32.IsUserAnAdmin() != 0
def get_client_info():
    """
    通过WMIC命令获取LeagueClient的端口和token信息
    """
    try:
        # 执行WMIC命令获取LeagueClientUx.exe的命令行参数
        cmd = 'WMIC PROCESS WHERE "name=\'LeagueClientUx.exe\'" GET commandline'
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
        
        if result.returncode != 0:
            print("执行WMIC命令失败")
            return None, None
            
        # 从输出中提取端口和token
        commandline_output = result.stdout
        
        # 使用正则表达式提取port和token
        port_match = re.search(r'"--app-port=(\d+)"', commandline_output)
        token_match = re.search(r'"--remoting-auth-token=([^"]+)"', commandline_output)
        
        if port_match and token_match:
            port = port_match.group(1)
            token = token_match.group(1)
            return port, token
        else:
            print("未找到端口或token信息")
            return None, None
            
    except Exception as e:
        print(f"获取客户端信息时出错: {e}")
        return None, None
def create_auth_header(token):
    """
    创建认证头
    """
    # 将token编码为base64
    auth_string = f"riot:{token}"
    encoded_auth = base64.b64encode(auth_string.encode('utf-8')).decode('utf-8')
    return {
        "Authorization": f"Basic {encoded_auth}",
        "Accept": "application/json",
        "Content-Type": "application/json"
    }
def check_game_status(port, token):
    """
    检查游戏状态
    """
    try:
        url = f"https://127.0.0.1:{port}/lol-gameflow/v1/session"
        headers = create_auth_header(token)
        
        # 创建不验证SSL的请求上下文
        context = ssl.create_default_context()
        context.check_hostname = False
        context.verify_mode = ssl.CERT_NONE
        
        request = urllib.request.Request(url, headers=headers)
        
        response = urllib.request.urlopen(request, context=context, timeout=5)
        data = json.loads(response.read().decode())
        return data.get('phase', 'None')
        
    except urllib.error.URLError as e:
        # print(f"URL错误: {e}")
        return 'None'
    except Exception as e:
        # print(f"检查游戏状态时出错: {e}")
        return 'None'
def check_ready_check_status(port, token):
    """
    检查是否处于准备确认状态
    """
    try:
        url = f"https://127.0.0.1:{port}/lol-matchmaking/v1/ready-check"
        headers = create_auth_header(token)
        
        # 创建不验证SSL的请求上下文
        context = ssl.create_default_context()
        context.check_hostname = False
        context.verify_mode = ssl.CERT_NONE
        
        request = urllib.request.Request(url, headers=headers)
        
        response = urllib.request.urlopen(request, context=context, timeout=5)
        data = json.loads(response.read().decode())
        return data.get('state', 'None')
        
    except urllib.error.URLError as e:
        # print(f"URL错误: {e}")
        return 'None'
    except Exception as e:
        # print(f"检查准备状态时出错: {e}")
        return 'None'
def accept_match(port, token):
    """
    接受匹配
    """
    try:
        url = f"https://127.0.0.1:{port}/lol-matchmaking/v1/ready-check/accept"
        headers = create_auth_header(token)
        
        # 创建不验证SSL的请求上下文
        context = ssl.create_default_context()
        context.check_hostname = False
        context.verify_mode = ssl.CERT_NONE
        
        # 创建POST请求
        request = urllib.request.Request(url, headers=headers, method='POST')
        
        response = urllib.request.urlopen(request, context=context, timeout=5)
        
        if response.status == 204:
            print("成功接受匹配!")
            return True
        else:
            print(f"接受匹配失败,状态码: {response.status}")
            return False
            
    except urllib.error.HTTPError as e:
        if e.code == 204:
            print("成功接受匹配!")
            return True
        else:
            print(f"接受匹配失败,HTTP错误码: {e.code}")
            return False
    except Exception as e:
        print(f"接受匹配时出错: {e}")
        return False
class LolAutoAcceptGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("LOL 自动接受匹配")
        self.root.geometry("500x450")
        self.root.resizable(False, False)
        
        # 初始化变量
        self.port = None
        self.token = None
        self.is_running = False
        self.delay = 5  # 默认延迟5秒
        self.last_accepted = 0  # 上次接受匹配的时间
        self.animation_id = None
        self.client_check_job = None  # 客户端检查任务
        
        # 状态映射
        self.status_mapping = {
            'None': '未运行',
            'Lobby': '大厅中',
            'Matchmaking': '正在匹配',
            'ReadyCheck': '等待接受',
            'ChampSelect': '英雄选择中',
            'InProgress': '游戏中',
            'Reconnect': '重新连接',
            'WaitingForStats': '等待统计',
            'PreEndOfGame': '游戏结束前',
            'EndOfGame': '游戏结束'
        }
        
        # 准备状态映射
        self.ready_check_mapping = {
            'None': '未知',
            'InProgress': '等待接受',
            'EveryoneReady': '所有人已准备',
            'StrangerCancelled': '陌生人取消',
            'PartyCancelled': '队伍取消',
            'Error': '错误'
        }
        
        self.setup_ui()
        self.check_admin()
        
        # 程序启动后自动开始检测
        self.root.after(1000, self.auto_start_detection)
        
    def setup_ui(self):
        # 设置样式
        self.style = ttk.Style()
        self.style.theme_use('clam')
        
        # 自定义样式
        self.style.configure('Title.TLabel', font=('Arial', 16, 'bold'), foreground='#333333')
        self.style.configure('Status.TLabel', font=('Arial', 12, 'bold'), foreground='#333333')
        self.style.configure('Section.TLabelframe', font=('Arial', 10, 'bold'))
        self.style.configure('Custom.TEntry', font=('Arial', 10))
        self.style.configure('Switch.TLabel', font=('Arial', 10, 'bold'))
        
        # 主框架
        main_frame = ttk.Frame(self.root, padding="20")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 标题
        title_label = ttk.Label(main_frame, text="LOL 自动接受匹配", style='Title.TLabel')
        title_label.pack(pady=(0, 20))
        
        # 状态显示框架
        status_frame = ttk.LabelFrame(main_frame, text="当前状态", padding="15", style='Section.TLabelframe')
        status_frame.pack(fill=tk.X, pady=(0, 15))
        
        self.status_label = ttk.Label(status_frame, text="未运行", style='Status.TLabel')
        self.status_label.pack()
        
        # 延迟设置框架
        delay_frame = ttk.LabelFrame(main_frame, text="接受延迟设置", padding="15", style='Section.TLabelframe')
        delay_frame.pack(fill=tk.X, pady=(0, 15))
        
        delay_inner_frame = ttk.Frame(delay_frame)
        delay_inner_frame.pack(fill=tk.X)
        
        ttk.Label(delay_inner_frame, text="延迟(秒):", font=('Arial', 10)).pack(side=tk.LEFT)
        
        self.delay_var = tk.StringVar(value="5")
        self.delay_entry = ttk.Entry(delay_inner_frame, textvariable=self.delay_var, width=10,
                                    font=('Arial', 10))
        self.delay_entry.pack(side=tk.LEFT, padx=(10, 10))
        
        ttk.Label(delay_inner_frame, text="(检测到匹配后延迟多少秒接受)", font=('Arial', 9)).pack(side=tk.LEFT)
        
        # 开关控制框架
        switch_frame = ttk.LabelFrame(main_frame, text="自动接受控制", padding="15", style='Section.TLabelframe')
        switch_frame.pack(fill=tk.X, pady=(0, 15))
        
        switch_inner_frame = ttk.Frame(switch_frame)
        switch_inner_frame.pack(fill=tk.X)
        
        ttk.Label(switch_inner_frame, text="关闭", font=('Arial', 10)).pack(side=tk.LEFT)
        
        # 开关滑块 (0=关闭, 1=开启)
        self.switch_var = tk.IntVar(value=1)  # 默认设置为开启
        self.switch_scale = tk.Scale(switch_inner_frame, from_=0, to=1, orient=tk.HORIZONTAL,
                                    variable=self.switch_var, length=100,
                                    highlightthickness=0, showvalue=0,
                                    troughcolor='#cccccc', bg='#f0f0f0',
                                    activebackground='#0078d7', relief='flat')
        self.switch_scale.pack(side=tk.LEFT, padx=10)
        
        ttk.Label(switch_inner_frame, text="开启", font=('Arial', 10)).pack(side=tk.LEFT)
        
        self.switch_status_label = ttk.Label(switch_inner_frame, text="已开启",
                                            font=('Arial', 10, 'bold'), foreground='#008000')
        self.switch_status_label.pack(side=tk.LEFT, padx=(10, 0))
        
        # 绑定开关事件
        self.switch_scale.configure(command=self.toggle_auto_accept)
        
        # 日志显示框架
        log_frame = ttk.LabelFrame(main_frame, text="运行日志", padding="10", style='Section.TLabelframe')
        log_frame.pack(fill=tk.BOTH, expand=True)
        
        # 创建日志文本框和滚动条
        log_text_frame = ttk.Frame(log_frame)
        log_text_frame.pack(fill=tk.BOTH, expand=True)
        
        self.log_text = tk.Text(log_text_frame, height=10, state=tk.DISABLED,
                               font=("Consolas", 9))
        log_scrollbar = ttk.Scrollbar(log_text_frame, orient=tk.VERTICAL, command=self.log_text.yview)
        self.log_text.configure(yscrollcommand=log_scrollbar.set)
        
        self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        log_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
    def auto_start_detection(self):
        """程序启动后自动开始检测"""
        self.switch_var.set(1)
        self.switch_status_label.config(text="已开启", foreground='#008000')
        self.start_listening()
        
    def toggle_auto_accept(self, value):
        """切换自动接受功能"""
        if int(value) == 1:  # 开启
            self.switch_status_label.config(text="已开启", foreground='#008000')  # 绿色
            self.start_listening()
        else:  # 关闭
            self.switch_status_label.config(text="已关闭", foreground='#cc0000')  # 红色
            self.stop_listening()
        
    def check_admin(self):
        if not is_admin():
            messagebox.showerror("权限错误", "请以管理员权限运行此程序!\n右键点击程序,选择'以管理员身份运行'。")
            self.root.destroy()
            return False
        return True
        
    def log_message(self, message):
        self.log_text.config(state=tk.NORMAL)
        self.log_text.insert(tk.END, f"[{time.strftime('%H:%M:%S')}] {message}\n")
        self.log_text.see(tk.END)
        self.log_text.config(state=tk.DISABLED)
        
    def update_status(self, gameflow_status, ready_check_status=None):
        # 根据游戏流程状态和准备状态显示更准确的信息
        if gameflow_status == 'ReadyCheck' and ready_check_status:
            display_status = self.ready_check_mapping.get(ready_check_status, ready_check_status)
        else:
            display_status = self.status_mapping.get(gameflow_status, gameflow_status)
            
        self.status_label.config(text=display_status)
        
        # 如果状态是"等待接受",添加动画效果
        if display_status == '等待接受':
            self.start_animation()
        else:
            self.stop_animation()
        
    def start_animation(self):
        """开始状态标签的动画效果"""
        self.stop_animation()  # 先停止之前的动画
        
        def animate():
            current_text = self.status_label.cget("text")
            if current_text.endswith("..."):
                self.status_label.config(text=current_text[:-3])
            else:
                self.status_label.config(text=current_text + ".")
            self.animation_id = self.root.after(500, animate)
            
        self.animation_id = self.root.after(500, animate)
        
    def stop_animation(self):
        """停止动画效果"""
        if self.animation_id:
            self.root.after_cancel(self.animation_id)
            self.animation_id = None
        
    def get_client_info_wrapper(self):
        self.log_message("正在获取League of Legends客户端信息...")
        self.port, self.token = get_client_info()
        
        if not self.port or not self.token:
            self.log_message("请确保League of Legends客户端正在运行!")
            return False
            
        self.log_message(f"成功获取客户端信息:")
        self.log_message(f"端口: {self.port}")
        self.log_message(f"Token: {self.token}")
        
        return True
        
    def start_listening(self):
        # 如果已经在运行,则不重复启动
        if self.is_running:
            return
            
        try:
            self.delay = int(self.delay_var.get())
        except ValueError:
            messagebox.showwarning("输入错误", "延迟时间必须是数字!")
            self.switch_var.set(0)  # 将开关设置为关闭
            self.switch_status_label.config(text="已关闭", foreground='#cc0000')
            return
            
        if self.delay  10:  # 至少间隔10秒
                        self.log_message(f"检测到匹配,将在 {self.delay} 秒后接受...")
                        # 延迟接受
                        time.sleep(self.delay)
                        if self.is_running:  # 再次检查是否仍在运行
                            success = accept_match(self.port, self.token)
                            if success:
                                self.log_message("成功接受匹配!")
                                self.last_accepted = time.time()
                            else:
                                self.log_message("接受匹配失败!")
               
                # 每秒检查一次
                time.sleep(1)
               
            except Exception as e:
                # 检查是否是因为连接断开导致的错误
                if self.is_running:  # 只有在运行状态下才记录错误
                    self.log_message(f"运行时出错: {e}")
                    # 等待5秒后继续
                    time.sleep(5)
                else:
                    # 如果已经停止,则退出循环
                    break
def main():
    root = tk.Tk()
    app = LolAutoAcceptGUI(root)
    root.mainloop()
if __name__ == "__main__":
    main()
提供一个成品,小伙伴自取.
https://wwuf.lanzoum.com/iw4co33pmyzi
密码:52pj

状态, 客户端

不苟言笑   

开始对局,然后挂机是吧,无人值守?
银河男神   

非常有用的功能,我常年只玩儿一个英雄,所以配装和召唤师技能都是固定的,请问能不能实现自动接受对局+自动选定指定英雄,然后确定,坐等游戏开始呢?
Bankai   

感觉还是有点用
love5599   

挺不错的 省的错过对局
mirakyux   

之前也用python写过一个, 不过最近嫌打包出来太大且占资源多就用tauri重写了
GMWQ   

大佬强的啊,感谢分享
ddcatgg   

感谢分享!原来 lol 的客户端竟然运行着一个本地http服务吗?
wuaipojiejkl   

楼主厉害啊
cxl   

没有python用不了
您需要登录后才可以回帖 登录 | 立即注册

返回顶部