├── res └── images │ └── sgs │ ├── edge.png │ ├── chrome.png │ ├── fanhui.png │ ├── quxiao.png │ ├── pifu_icon.png │ ├── denglu_youxi.png │ ├── jinru_youxi.png │ ├── wujiang_icon.png │ ├── gonghui_leigu.png │ ├── jiangyin_chouqu.png │ ├── kaishi_tiaozhan.png │ ├── linqu_jiangli.png │ ├── minjiang_tang.png │ └── sanguoxiu_icon.png ├── sanguosha.bat ├── README.md ├── mous.pyw └── san_guo_sha.py /res/images/sgs/edge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinStarry/sanguosha_py/HEAD/res/images/sgs/edge.png -------------------------------------------------------------------------------- /sanguosha.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | start cmd /k "cd/d D:\Code\python_work\core\ && python san_guo_sha.py" 3 | -------------------------------------------------------------------------------- /res/images/sgs/chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinStarry/sanguosha_py/HEAD/res/images/sgs/chrome.png -------------------------------------------------------------------------------- /res/images/sgs/fanhui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinStarry/sanguosha_py/HEAD/res/images/sgs/fanhui.png -------------------------------------------------------------------------------- /res/images/sgs/quxiao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinStarry/sanguosha_py/HEAD/res/images/sgs/quxiao.png -------------------------------------------------------------------------------- /res/images/sgs/pifu_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinStarry/sanguosha_py/HEAD/res/images/sgs/pifu_icon.png -------------------------------------------------------------------------------- /res/images/sgs/denglu_youxi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinStarry/sanguosha_py/HEAD/res/images/sgs/denglu_youxi.png -------------------------------------------------------------------------------- /res/images/sgs/jinru_youxi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinStarry/sanguosha_py/HEAD/res/images/sgs/jinru_youxi.png -------------------------------------------------------------------------------- /res/images/sgs/wujiang_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinStarry/sanguosha_py/HEAD/res/images/sgs/wujiang_icon.png -------------------------------------------------------------------------------- /res/images/sgs/gonghui_leigu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinStarry/sanguosha_py/HEAD/res/images/sgs/gonghui_leigu.png -------------------------------------------------------------------------------- /res/images/sgs/jiangyin_chouqu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinStarry/sanguosha_py/HEAD/res/images/sgs/jiangyin_chouqu.png -------------------------------------------------------------------------------- /res/images/sgs/kaishi_tiaozhan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinStarry/sanguosha_py/HEAD/res/images/sgs/kaishi_tiaozhan.png -------------------------------------------------------------------------------- /res/images/sgs/linqu_jiangli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinStarry/sanguosha_py/HEAD/res/images/sgs/linqu_jiangli.png -------------------------------------------------------------------------------- /res/images/sgs/minjiang_tang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinStarry/sanguosha_py/HEAD/res/images/sgs/minjiang_tang.png -------------------------------------------------------------------------------- /res/images/sgs/sanguoxiu_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinStarry/sanguosha_py/HEAD/res/images/sgs/sanguoxiu_icon.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sanguosha_py 2 | 3 | # 说明篇 4 | 脚本主要基于python的pyautogui库,而这个库又依赖pillow,pillow在Windows上似乎又需要Microsoft VS工具支持。 5 | 6 | mouse.pyw 这个文件是在写代码的过程中,需要频繁的确定坐标而写的代码,用于开发的时候确定坐标,我的电脑是1920*1080,使用的是chrome浏览器,没有书签模式下(ctrl+shift+b),运行。 7 | 8 | sanguosha.bat 这个文件是放在桌面上双击运行的,双击之后回车,然后就可以喝咖啡看着自动跑了。 9 | 10 | ------------------------------------------------ 11 | # 废话篇 12 | 版本在使用过程中修改了很多次,最后修改大概是2020.11月。 13 | 14 | 修改的初衷是为了使代码更简洁,结果事与愿违。 15 | 16 | 最初没有选择参数的功能,是后面加的,定时功能也是后面加的,计算每个函数运行的时间只是为了方便观察。 17 | 18 | 网上有些js脚本,水平有限,直接在控制台跑别人的js脚本没有效果,这才生出想自己写个脚本的念头。 19 | 20 | 开始是打算用selenium来操作,但是发现,使用selenium操控chrome会被弹出登录界面,无法正常加载,后面试图通过chrome开发者模式启动,人工进入游戏,然后使用selenium接管chrome,结果依旧会被弹出登录界面。 21 | 22 | 直到遇到了pyautogui这个库,就像是打开了新世界的大门。 23 | 24 | 本脚本配置麻烦,给自己留个念想了。 25 | 26 | ------------------------------------------------------- 27 | # 代码篇 28 | datetime threading time 这几个库都是为了定时做的,不需要定时模块可以直接删掉。 29 | 30 | pyautogui.FAILSAFE = True 这个是为了防止失控,把鼠标放到电脑左上角,脚本会抛出异常然后终止。 31 | 32 | confidence=0.7,这个参数是相似度,如果匹配不到,可以调低一点,0-1。1表示100%匹配。 -------------------------------------------------------------------------------- /mous.pyw: -------------------------------------------------------------------------------- 1 | import time 2 | from pynput import mouse 3 | 4 | from tkinter.tix import * 5 | 6 | 7 | class Position(object): 8 | def __init__(self): 9 | self.string_a = '' 10 | self.string_b = '' 11 | self.x1 = 0 12 | self.x2 = 0 13 | self.y1 = 0 14 | self.y2 = 0 15 | 16 | def on_click(self, x, y, button, pressed): 17 | if button == mouse.Button.right: 18 | if pressed: 19 | self.x1 = x 20 | self.y1 = y 21 | else: 22 | self.x2 = x 23 | self.y2 = y 24 | width = self.x2 - self.x1 25 | height = self.y2 - self.y1 26 | self.string_a = f'{self.x1}, {self.y1}, {width}, {height}' 27 | return False 28 | else: 29 | if pressed: 30 | self.string_b = f'{x}, {y}' 31 | else: 32 | return False 33 | 34 | def listen(self): 35 | with mouse.Listener(on_click=self.on_click) as listener: 36 | listener.join() 37 | 38 | def start(self): 39 | self.listen() 40 | 41 | 42 | def click(): 43 | m = Position() 44 | m.listen() 45 | c_time = time.strftime('%H:%M:%S', time.localtime( 46 | time.time())) 47 | text.insert(END, c_time) 48 | if m.string_a != '': 49 | text.insert(END, m.string_a) 50 | if m.string_b != '': 51 | text.insert(END, m.string_b) 52 | text.insert(END, '\n') 53 | text.see(END) 54 | text.update() 55 | 56 | 57 | root = Tk() 58 | root.geometry('300x200') 59 | root.title('My GUI') 60 | btn = Button(root, text='点击开始', bg='white', fg='blue', command=click) 61 | btn.grid() 62 | text = Listbox(root, width=40, height=10) 63 | text.insert(END, 'mouse left for a position, right for area.') 64 | text.grid() 65 | root.mainloop() 66 | 67 | 68 | -------------------------------------------------------------------------------- /san_guo_sha.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import threading 3 | import time 4 | import pyautogui 5 | 6 | pyautogui.PAUSE = 1 7 | pyautogui.FAILSAFE = True 8 | DELAY_TIME = 2 9 | IMG_BASE_PATH = './res/images/sgs/' 10 | 11 | 12 | def calculate_time(): 13 | def wrapper(function): 14 | def execute(*args, **kwargs): 15 | beg = time.time() 16 | result = function(*args, **kwargs) 17 | end = time.time() 18 | seconds = end - beg 19 | m, s = divmod(seconds, 60) 20 | h, m = divmod(m, 60) 21 | print(f'run function {function.__name__} took time:', '%d:%02d:%02d' % (h, m, s)) 22 | return result 23 | 24 | return execute 25 | 26 | return wrapper 27 | 28 | 29 | def flag_match_img(img_src, try_time=3, confidence=0.7): 30 | for i in range(try_time): 31 | pos = pyautogui.locateOnScreen(img_src, confidence=confidence) 32 | if pos: 33 | return pos 34 | elif i == try_time - 1: 35 | return None 36 | time.sleep(1) 37 | 38 | 39 | @calculate_time() 40 | def zhulu_tianxia(): 41 | time.sleep(DELAY_TIME) 42 | print('\nsubprogram zhu_lu is start...') 43 | # 冒险 -> 进入逐鹿 44 | pyautogui.click(1263, 527) 45 | time.sleep(1) 46 | pyautogui.click(790, 539) 47 | pyautogui.click(1027, 175, interval=1, clicks=2) # 可能弹出动画. 48 | pos1 = flag_match_img(IMG_BASE_PATH + 'kaishi_tiaozhan.png', try_time=6) 49 | pos2 = flag_match_img(IMG_BASE_PATH + 'fanhui.png', try_time=1) 50 | if pos1 and pos2: 51 | for i in range(5): 52 | # 英雄模式 -> 101-125关卡 -> 105关卡 -> 挑战 -> 确定阵容, 挑战 -> 等待进入挑战界面 53 | pyautogui.click(1027, 175) 54 | pyautogui.click(387, 430) 55 | pyautogui.click(681, 433) 56 | pyautogui.click(1619, 645) 57 | pyautogui.click(1419, 851) 58 | time.sleep(5) 59 | count_time = 50 60 | for j in range(count_time): 61 | pyautogui.click(959, 892) # 继续对话框 62 | pos3 = flag_match_img(IMG_BASE_PATH + 'kaishi_tiaozhan.png', try_time=3) 63 | if pos3: 64 | break 65 | else: 66 | if j == count_time - 1: 67 | print('out of the max_time, now can not address it by current program.') 68 | exit() 69 | pass 70 | print('zhulu challenge completed once.') 71 | pyautogui.click(pyautogui.center(pos2), clicks=2, interval=2) 72 | else: 73 | print('out of expected, may something error in here.') 74 | print('subprogram zhu_lu is over...') 75 | 76 | 77 | def login_sgs(): 78 | time.sleep(DELAY_TIME) 79 | print('\nsubprogram login_sgs is starting...') 80 | pyautogui.hotkey('ctrl', 't') 81 | pyautogui.hotkey('ctrl', 'l') 82 | pyautogui.typewrite("https://web.sanguosha.com/login/index.html") 83 | pyautogui.press('enter', presses=2) 84 | pos1 = flag_match_img(IMG_BASE_PATH + 'denglu_youxi.png', try_time=3) 85 | if pos1: 86 | pyautogui.click(pyautogui.center(pos1)) 87 | pos2 = flag_match_img(IMG_BASE_PATH + 'jinru_youxi.png', try_time=6) 88 | if pos2: 89 | pyautogui.click(pyautogui.center(pos2)) 90 | else: 91 | exit() 92 | time.sleep(2) 93 | pyautogui.click(1000, 570) 94 | print('loading game, please be patient') 95 | time.sleep(15) 96 | pyautogui.click(1733, 124, clicks=4, interval=3) # click as much as possible, to make sure show quit activity. 97 | pos3 = flag_match_img(IMG_BASE_PATH + 'quxiao.png', confidence=0.7) 98 | if pos3: 99 | pyautogui.click(pyautogui.center(pos3)) 100 | else: 101 | pyautogui.click(1415, 320) 102 | pyautogui.click(1600, 250) 103 | pyautogui.click(1733, 124, clicks=3, interval=2) 104 | pos3 = flag_match_img(IMG_BASE_PATH + 'quxiao.png', confidence=0.7, try_time=2) 105 | if pos3: 106 | pyautogui.click(pyautogui.center(pos3)) 107 | else: 108 | print('out of the program excepted, this condition should be exist, make sure to be safe, quit it !!!') 109 | exit() 110 | print('subprogram login_sgs is over...') 111 | 112 | 113 | def gonghui_leigu(): 114 | time.sleep(DELAY_TIME) 115 | print('\nsubprogram gonghui_leigu is start...') 116 | # 进入公会->擂鼓*3->为了防止已经擂鼓过出现元宝擂鼓界面(增加取消)->返回主界面 117 | pyautogui.click(1220, 968) 118 | pos = flag_match_img(IMG_BASE_PATH + 'gonghui_leigu.png', try_time=7) 119 | if pos: 120 | pyautogui.click(pyautogui.center(pos), interval=2, clicks=3) 121 | pos2 = flag_match_img(IMG_BASE_PATH + 'quxiao.png', try_time=1) 122 | if pos2: 123 | pyautogui.click(pyautogui.center(pos2)) 124 | for i in range(3): 125 | pos3 = flag_match_img(IMG_BASE_PATH + 'linqu_jiangli.png', confidence=0.7, try_time=2) 126 | if pos3: 127 | pyautogui.click(pyautogui.center(pos3)) 128 | pos4 = flag_match_img(IMG_BASE_PATH + 'fanhui.png', confidence=0.7, try_time=2) 129 | if pos4: 130 | pyautogui.click(pyautogui.center(pos4)) 131 | else: 132 | print('out of expected in here, some errors in here.') 133 | else: 134 | print('out of expected, may not in gonghui_leigu activity.') 135 | print('subprogram gonghui_leigu is over...') 136 | 137 | 138 | def wujiang_jiangyin(): 139 | time.sleep(DELAY_TIME) 140 | print('\nsubprogram wujiang_jiangyin is start...') 141 | # 点击武将->判断名将堂是否出现->点击将印打开抽取界面->开启一次->关闭抽取界面->返回主界面 142 | wujiang_icon = flag_match_img(IMG_BASE_PATH + 'wujiang_icon.png', confidence=0.75) 143 | if wujiang_icon: 144 | pyautogui.click(pyautogui.center(wujiang_icon)) 145 | else: 146 | print('not found wujiang icon in current activity, end of subprogram') 147 | return False 148 | pos1 = flag_match_img(IMG_BASE_PATH + 'minjiang_tang.png', try_time=7) 149 | pos2 = flag_match_img(IMG_BASE_PATH + 'fanhui.png', try_time=2) 150 | if pos1 and pos2: 151 | pyautogui.click(pyautogui.center(pos1)) 152 | max_time = 15 153 | while max_time: 154 | re_pos1 = flag_match_img(IMG_BASE_PATH + 'minjiang_tang.png', try_time=1) 155 | if re_pos1 is None: 156 | pyautogui.click(308, 499) 157 | pyautogui.click(1240, 849) 158 | pyautogui.click(1564, 248) 159 | break 160 | else: 161 | max_time = max_time - 1 162 | if max_time == 0: 163 | print('out of max try time. some errors in here, end of subprogram') 164 | return False 165 | pyautogui.click(pyautogui.center(pos2)) 166 | else: 167 | print('out of expected, may some errors in here.') 168 | print('subprogram wujiang_jiangin is over...') 169 | 170 | 171 | def sanguo_xiu(): 172 | time.sleep(DELAY_TIME) 173 | print('\nsubprogram sanguo_xiu is starting... ') 174 | # 三国秀->三国秀坊(用返回按键来判断是否进入三国秀界面)->开启->开启三国秀后先返回抽取界面,再返回主界面故两次. 175 | sanguoxiu_icon = flag_match_img(IMG_BASE_PATH + 'sanguoxiu_icon.png', confidence=0.75) 176 | if sanguoxiu_icon: 177 | pyautogui.click(pyautogui.center(sanguoxiu_icon)) 178 | else: 179 | print('not found sanguoxiu icon in current activity, end of subprogram') 180 | return False 181 | pos = flag_match_img(IMG_BASE_PATH + 'fanhui.png', try_time=6) 182 | if pos: 183 | pyautogui.click(1679, 810, interval=2) 184 | pyautogui.click(729, 627, interval=2) 185 | pyautogui.click(pyautogui.center(pos), clicks=2, interval=2) 186 | else: 187 | print('out of expected, the program may have some errors') 188 | print('subprogram sanguo_xiu is over... ') 189 | 190 | 191 | def jianglin(): 192 | time.sleep(DELAY_TIME) 193 | print('\nsubprogram jianglin is start...') 194 | max_time = 30 195 | while max_time: 196 | pyautogui.click(924, 867) 197 | pos = flag_match_img(IMG_BASE_PATH + 'fanhui.png', try_time=1) 198 | if pos: 199 | break 200 | elif max_time == 1: 201 | print('out of max try times, may some errors in here. end of subprogram.') 202 | return False 203 | max_time = max_time - 1 204 | pos = flag_match_img(IMG_BASE_PATH + 'fanhui.png', try_time=1) 205 | if pos: 206 | # 聚宝盆->领取奖励->关闭 207 | pyautogui.click(1449, 870) 208 | pyautogui.click(1170, 683) 209 | pyautogui.click(1323, 376) 210 | # 将灵出征部分,包括领取奖励和领取完毕后出征两部分. 211 | pyautogui.click(1559, 883), time.sleep(3) 212 | pyautogui.click(962, 813), time.sleep(3) 213 | pyautogui.click(1146, 787), time.sleep(1) 214 | pyautogui.click(710, 831, interval=3, clicks=2) 215 | pyautogui.click(856, 672) 216 | pyautogui.click(1564, 249) 217 | time.sleep(1) 218 | pyautogui.click(pyautogui.center(pos)) 219 | else: 220 | print(f'out of expected, may not into jianglin activity, some errors in here.') 221 | pyautogui.click(924, 867, clicks=2, interval=2) # 没有进入到将灵界面 222 | print('subprogram jianglin is over...') 223 | 224 | 225 | def open_browser(key='2'): 226 | print('start to open browser') 227 | pyautogui.hotkey('winleft', 'd') 228 | time.sleep(1) 229 | if key == '1': 230 | pos1 = flag_match_img(IMG_BASE_PATH + 'edge.png') 231 | if pos1: 232 | pyautogui.click(pyautogui.center(pos1), clicks=2) 233 | else: 234 | exit() 235 | elif key == '2': 236 | pos2 = flag_match_img(IMG_BASE_PATH + 'chrome.png') 237 | if pos2: 238 | pyautogui.click(pyautogui.center(pos2), clicks=2) 239 | else: 240 | exit() 241 | 242 | 243 | def start_game(): 244 | open_browser() 245 | login_sgs() 246 | sanguo_xiu() 247 | wujiang_jiangyin() 248 | jianglin() 249 | zhulu_tianxia() 250 | gonghui_leigu() 251 | # pyautogui.click(1892, 15) # 关闭浏览器 252 | 253 | 254 | def timer(): 255 | now_time = datetime.datetime.now() 256 | print(f'现在时间:{now_time}') 257 | next_time = now_time + datetime.timedelta(days=+1) 258 | next_year = next_time.date().year 259 | next_month = next_time.date().month 260 | next_day = next_time.date().day 261 | next_time = datetime.datetime.strptime(str(next_year) + "-" + str(next_month) + "-" + str(next_day) + " 00:01:00", 262 | "%Y-%m-%d %H:%M:%S") 263 | timer_start_time = (next_time - now_time).total_seconds() 264 | print(f'距离运行还有{timer_start_time}秒.') 265 | m_timer = threading.Timer(timer_start_time, start_game) 266 | m_timer.start() 267 | 268 | 269 | def with_start(): 270 | def m_execute(m_argv): 271 | for x in m_argv: 272 | if x == '1': 273 | zhulu_tianxia() 274 | if x == '2': 275 | jianglin() 276 | 277 | print('第一参数:1.启动Edge. 2启动Chrome. 3.在游戏主界面. t.定时00:01:00运行') 278 | print('第二参数(可多选):1.逐鹿天下 2.将灵') 279 | string = input('输入参数: ') 280 | argv1 = string[:1] 281 | argv2 = string[1:] 282 | if argv1 == '1': 283 | open_browser(key='1') 284 | login_sgs() 285 | if not argv2: 286 | sanguo_xiu() 287 | wujiang_jiangyin() 288 | jianglin() 289 | zhulu_tianxia() 290 | gonghui_leigu() 291 | else: 292 | m_execute(argv2) 293 | elif argv1 == '2': 294 | open_browser(key='2') 295 | login_sgs() 296 | m_execute(argv2) 297 | elif argv1 == '3': 298 | m_execute(argv2) 299 | elif argv1 == 't': 300 | timer() 301 | else: 302 | start_game() 303 | 304 | 305 | if __name__ == '__main__': 306 | with_start() 307 | --------------------------------------------------------------------------------