[TOC]
趣消除App自动化 - 成语消消乐-全自动化
目标
趣消除App自动化 - 成语消消乐-半自动化
这篇文章实现了成语消消乐
的半自动化:
- 用户点击开始一局游戏
- 代码自动答题
- 对代码没有找到的成语,用户自己点击成语赢得游戏
这篇文章的目的是全自动化:
- 代码自动开始一局游戏
- 代码自动答题
- 对没有全部找到的,放弃这局:等待对方赢得游戏;
- 开始下一局游戏
写在前面:
- 自己的文章从不介绍背景知识,直接上代码;因为定位实战,非教程
- 看了些评论,多是
不明觉厉
,希望你可以评主题相关的讨论或感谢 - 这篇文章
威力巨大
[呵呵],所以不要做恶;不要做恶;不要做恶;
不要做恶:
比如游戏有12个成语要找,但代码只答对了11个,还有1个成语4个字,共有4*3*2*1=24
种排列组合,请读者不要发24个请求来找到这最后一个成语,这是楼主认为的'做恶',也是对自动化:对没有全部找到的,放弃这局:等待对方赢得游戏
做出的取舍;你可以用游戏里的认输
和提示
写这篇文章与代码的目的:
- 虚荣:有读者阅读、评论
- 金钱:赢得游戏有几分钱
- 时间:游戏里插了很广告,跳过广告;自动化节约自己时间
- 能力:要写能用的代码,一定要学点什么,比如学习了websocket库
- 爱惜:自己的手机用了3年了,移植到电脑上来执行,可以让手机再战一年啊
- 成就:代码和文章等作品;不同维度地'虐人'的快感[鄙视]
好了,希望你找到了学习的兴趣与动力,上代码
测试环境
App: 趣消除App
iOS版本、扶我起来学数学App
iOS版本
工具: python、Charles、python第三方库websocket
背景知识:python、抓包、websocket
解决
分析
成语消消乐
有2个接口:
- https://king.hddgood.com/king_api/v1/game/join_game[http]
- wss://king.hddgood.com/websock_m/websock_message?uid={}&gameid={}&token={}[websocket]
-
game/join_game
接口会返回websock_m/websock_message
接口需要的gameid
;gameid
每局都不同 -
uid
对每个账号是固定 -
token
对一次登入是固定,每局游戏都一样; - 游戏的消息来回传递都在
websock_m/websock_message
接口websocket协议里完成
POST /king_api/v1/game/join_game HTTP/1.1
Host: king.hddgood.com
A-Token-Header: PTtWUFdWUkBFHEVZCVcNdUtVWwdc=
Cookie: UM_distinctid=16b27e625da1ef-038c4847dc733-336d7451-4a640-16b27e625dd490; cn_1276022107_dplus=%7B%22distinct_id%22%3A%20%2216b27e625da1ef-038c4847dc733-336d7451-4a640-16b27e625dd490%22%2C%22%24_sessionid%22%3A%20104%2C%22%24_sessionTime%22%3A%201561087099%2C%22%24dp%22%3A%200%2C%22%24_sessionPVTime%22%3A%201561087099%2C%22initial_view_time%22%3A%20%221559738991%22%2C%22initial_referrer%22%3A%20%22%24direct%22%2C%22initial_referrer_domain%22%3A%20%22%24direct%22%2C%22%24recent_outside_referrer%22%3A%20%22%24direct%22%7D; CNZZDATA1276022107=326225286-1559738991-%7C1561086230
uid=1457362&rank=11&type=G
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Connection: close
{"success":true,"msg":"操作成功","code":"200","codemsg":"操作成功","result":{"gameid":"G11-810737","dup":0,"starter":472251}}
工作原理
写2个文件:chengyu-auto.py
[代码文件]、chengyu.text
[数据文件]
-
chengyu-auto.py
从asking
消息里解析出ask_string
到chengyu.text
文件里查找是否包含相应的成语 - 自动提交成语答案
-
chengyu.text
文件刚开始是空的;在每局游戏结束时,游戏都会发送game_result
消息给我们,里面有这局游戏的答案成语,把这些成语写到文件中 - 玩的局数越多,
chengyu.text
文件包含的成语越多,查找到答案的可能性越大
代码
需要安装第三方python库:websockets
chengyu-auto.py
:
#!/usr/bin/env python3
# coding=utf-8
'''
# 趣消除App-成语消消乐全自动化;
# App版本:1.1.2
# App地址:https://itunes.apple.com/cn/app/id1449545954
提现非常迅速
'''
import re
import time
import datetime
import random
import json
import sys
import logging
import collections
import pathlib
import requests
Red = '\033[0;31m'
Green = '\033[0;32m'
Yellow = '\033[0;33m'
Blue = '\033[0;34m'
Purple = '\033[0;35m'
Cyan = '\033[0;36m'
White = '\033[0;37m'
colors = {
0:Red,
1:Purple,
2:Yellow,
3:Blue,
4:White,
}
# 这些变量的值可以通过像Charles抓包软件获得
# 账号变量
# ------------------------------------------------
# A_Token_Header的一些结论:
# 1.每个账号不同;
# 2.同一个账号每次登录时也是不一样的
# 3.同一个账号,退出时,只要不登录,上次的A-Token-Header的值还有效,只有再登录时,上次的token值才失败
A_Token_Header_zxg = 'PTtWUFdWUkBFHEVZCVcNdUtVWwdc'
# Cookie的一些结论:
# 1.同一个账号,退出或再登录,都不用修改,一直有效
# 2.值为空也可以
Cookie_zxg = ''
# UUID的一些结论:
# 1.固定不变
UUID_zxg = '1457362'
# ------------------------------------------------
api_ = 'https://king.hddgood.com/king_api/v1/'
class QuXiaoChuUser():
headers = {
'Host': 'king.hddgood.com',
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'zh-cn',
'Origin': 'https://king.hddgood.com',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_1_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16D57/; quxiaochu/ios v1.1.2',
'Referer': 'https://king.hddgood.com/'
}
data = {
'uid': '',
'channel': '',
'version': '1.1.2',
'os': 'ios',
'web_ver': '20190261'
}
SLEEP = 0.5
def __init__(self, uid, token_header, cookie):
self.uid = uid
self.headers = dict(QuXiaoChuUser.headers)
self.headers['A-Token-Header'] = token_header
self.token_header = token_header
self.headers['Cookie'] = cookie
def game_chengyu_join_game(self, rank):
'''
成语消消乐-获取游戏id
https://king.hddgood.com/king_api/v1/game/join_game
{"success":true,"msg":"操作成功","code":"200","codemsg":"操作成功","result":{"gameid":"G15-3232777","dup":0,"starter":531492}}
'''
print("成语消消乐-获取游戏id {}".format(self.uid))
data = self._uid_data()
# 1:书童;2:儒生;15:殿阁大学士
data['rank'] = str(rank)
data['type'] = 'G'
api = self._genapi('game/join_game')
result = self._post(api, self.headers, data)
return json.loads(result)
def _uid_data(self):
return {'uid': self.uid}
@staticmethod
def _genapi(path):
return 'https://king.hddgood.com/king_api/v1/' + path
@staticmethod
def _post(api, headers, data, p=logging.warning):
time.sleep(QuXiaoChuUser.SLEEP)
res = requests.post(api, headers=headers, data=data, verify=False)
print(res.url)
result = res.text
print(result)
print('')
return result
class Chengyu(object):
def __init__(self):
path = pathlib.PurePath(__file__)
path = path.parent.joinpath('chengyu.text')
self.dictpath = str(path)
self.chengyu = set()
with open(self.dictpath, 'rt') as f:
for line in f.readlines():
self.chengyu.add(line.strip())
self.answers = list()
self.ask_string = ''
# {'和':[1,8], '我':[11]}
self.char_indexs_dict = dict()
# {1:'和', 8:'和', 11:'我'}
self.index_char_dict = dict()
self.count = 0
# 自动提交答案的网络发送次数
self.auto_send_answers = list()
self.ack_true_answers = list()
# 找到的的成语中各异字符为2个的答案数量:如 [真真假假]
self.answer_2chars_count = 0
# {'中流砥柱':[1,9,21,25]}
self.answer_indexs_dict = dict()
# {'中流砥柱':set('中流砥柱')}
self.answer_charset_dict = dict()
# 查找到的错误答案
self.error_answers = []
# ---------------------------------------------
def find_answers_v2(self, ask_string):
'''
在内存成语字典查找答案
'''
ask_set = set(ask_string)
for i, c in enumerate(ask_string):
self.char_indexs_dict.setdefault(c, []).append(i)
self.index_char_dict = dict( zip(range(len(ask_string)), ask_string))
max_count = (len(ask_string) / 4 ) * 1.5
for item in self.chengyu:
item_set = self.answer_charset_dict.setdefault(item, set(item))
if not (item_set - ask_set):
self.answers.append(item)
if len(item_set)<4:
self.answer_2chars_count += 1
if len(self.answers) - self.answer_2chars_count >= max_count :
self.count = len(self.answers)
return
self.count = len(self.answers)
async def auto_answer(self, flow):
if len(self.answers):
item = self.answers[0]
answer_index = []
counter = collections.Counter(item)
for char, count in counter.items():
if self.char_indexs_dict[char]:
if len(self.char_indexs_dict[char]) < count:
self.error_answers.append(item)
self.answers.remove(item)
return
else:
pass
for c in item:
if self.char_indexs_dict[c]:
index = self.char_indexs_dict[c][0]
answer_index.append( str(index) )
del self.char_indexs_dict[c][0]
else:
pass
if len(set(answer_index)) < 4:
print('算法有错误:{} 小于4'.format(answer_index))
send_message = {
'answer': item,
'answer_index': answer_index,
'type': 'answer'
}
mm = json.dumps(send_message)
# -----------------------
print(mm)
# -----------------------
self.answer_indexs_dict[item] = answer_index
# 向服务器发送消息
self.auto_send_answers.append(item)
self.answers.remove(item)
await flow.send(mm)
# time.sleep(0.5)
def add_new_worlds_to_memory(self, m):
'''
把答案增加到内存字典中
'''
if len(self.ack_true_answers) < len(m['all_answer']):
for answer in m['all_answer']:
self.chengyu.add(answer['phrase'])
print('\033[1;31m 共收录{}个成语 \033[0m'.format(len(self.chengyu)))
def add_new_worlds_to_file(self, m):
'''
把答案增加到文件中
'''
if len(self.ack_true_answers) < len(m['all_answer']):
with open(self.dictpath, 'wt') as f:
l = list(self.chengyu)
l.sort()
for item in l:
f.write(item)
f.write('\n')
def print_answers(self):
'''
图形化、色彩化显示答案
'''
self.print_color('共找到 {}/{} 个成语'.format(self.count, len(self.ask_string)//4))
self.print_color('错误成语 {}'.format(self.error_answers))
self.print_color('共自动 {} 次提交:{}'.format(len(self.auto_send_answers),self.auto_send_answers))
self.print_color('已确认 {} 个提交:{}'.format(len(self.ack_true_answers),self.ack_true_answers))
self.print_color('问题 {}'.format(self.ask_string))
for item in self.answers:
self.print_color(item)
# self.print_matrix(item)
if (not self.answers) and self.index_char_dict:
self.print_matrix()
def print_matrix(self, item = []):
chars_in_line = 6
length = len(self.ask_string)
lines = (length + chars_in_line - 1) // chars_in_line
PADDING = ' '*(lines * chars_in_line - length)
is_need_padding = len(PADDING) != 0
self.print_color('--'*chars_in_line)
global colors, White
for i, c in self.index_char_dict.items():
end = ''
if (i+1) % chars_in_line == 0 or (i+1) == length:
end = '\n'
color = White
if c in item:
color = colors[item.index(c)]
line, first = divmod(i, chars_in_line)
if is_need_padding and first == 0 and (line + 1 == lines):
c = PADDING + c
self.print_color(c, end=end, color=color)
self.print_color('--'*chars_in_line)
def print_color(self, message, end='\n', color=Red):
print('{}{}\033[0m'.format(color, message), end=end)
def reset_data_to_init(self):
self.ask_string = ''
self.answers.clear()
self.index_char_dict.clear()
self.count = 0
self.answer_2chars_count = 0
self.answer_indexs_dict.clear()
self.char_indexs_dict.clear()
self.error_answers.clear()
self.ack_true_answers.clear()
self.auto_send_answers.clear()
def chengyu_auto_answer(user: QuXiaoChuUser):
'''
成语消消乐自动答题
wss://king.hddgood.com/websock_m/websock_message?uid=472251&gameid=G15-3232777&token=JSdLVVRRV0ZCH0INUlYNchcDUlc=
'''
result = user.game_chengyu_join_game(g_rank)
if result['success']:
gameid = result['result']['gameid']
url = 'wss://king.hddgood.com/websock_m/websock_message?uid={}&gameid={}&token={}'
url = url.format(user.uid, gameid, user.token_header)
print(url)
import asyncio
import websockets
async def chengyu():
async with websockets.connect(url) as websocket:
print('连接成功')
global chengyu
live = True
count = 0
while live:
if count % 10 == 0:
keeplive = json.dumps({"type":"keepalive"})
await websocket.send(keeplive)
print('send keeplive')
# await asyncio.sleep(0.5)
count += 1
m = await websocket.recv()
print(f"\n{m}\n")
m = json.loads(m)
message_type = m['type']
if m.get('ask_string'):
chengyu.ask_string = m['ask_string']
# 计算答案
chengyu.find_answers_v2(chengyu.ask_string)
if message_type == 'answer':
chengyu.answer_indexs_dict[m['answer']] = m['answer_index']
# 删除已回答正确的答案
if m.get('ack') == 1:
answer = m['answer']
chengyu.ack_true_answers.append(answer)
answer_index = chengyu.answer_indexs_dict.get(answer,[])
for i in answer_index:
chengyu.index_char_dict[int(i)] = ' '
try:
chengyu.answers.remove(m['answer'])
except:
pass
# 自动答题
await chengyu.auto_answer(websocket)
# 显示答案
if len(chengyu.ask_string):
chengyu.print_answers()
if message_type == 'game_result':
live = False
# 把答案增加到内存字典中
chengyu.add_new_worlds_to_memory(m)
chengyu.add_new_worlds_to_file(m)
chengyu.reset_data_to_init()
# 其它解析
for item in m['scores']:
if str(item['uid']) == user.uid:
global g_rank
g_rank = item['rank']
print('\033[1;31m 获得金币: {} Rank: {}\033[0m'.format(m['coin'], g_rank))
print('\033[1;31m 游戏结束 \033[0m')
asyncio.get_event_loop().run_until_complete(chengyu())
def genUsers():
yield QuXiaoChuUser(UUID_zxg, A_Token_Header_zxg, Cookie_zxg)
g_rank = 15
chengyu = Chengyu()
if __name__ == "__main__":
for user in genUsers():
start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
for _ in range(20):
chengyu_auto_answer(user)
time.sleep(1)
print('开始时间 ', start_time)
print('结束时间 ', time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
chengyu.text
一劳永逸
一掷千金
一曝十寒
一石二鸟
一筹莫展
一落千丈
一衣带水
一语破的
...
注意:
chengyu.text
与chengyu-auto.py
放在同一目录下
chengyu.text
收集约1926个成语,98%能找到全部答案
参考
楼主的趣消除App
系列文章
网友评论