趣消除App自動(dòng)化 - 成語(yǔ)消消樂-全自動(dòng)化

[TOC]

趣消除App自動(dòng)化 - 成語(yǔ)消消樂-全自動(dòng)化

目標(biāo)

趣消除App自動(dòng)化 - 成語(yǔ)消消樂-半自動(dòng)化
這篇文章實(shí)現(xiàn)了成語(yǔ)消消樂的半自動(dòng)化:

  • 用戶點(diǎn)擊開始一局游戲
  • 代碼自動(dòng)答題
  • 對(duì)代碼沒有找到的成語(yǔ)数冬,用戶自己點(diǎn)擊成語(yǔ)贏得游戲

這篇文章的目的是全自動(dòng)化:

  • 代碼自動(dòng)開始一局游戲
  • 代碼自動(dòng)答題
  • 對(duì)沒有全部找到的,放棄這局:等待對(duì)方贏得游戲衩椒;
  • 開始下一局游戲

寫在前面:

  • 自己的文章從不介紹背景知識(shí)境钟,直接上代碼折汞;因?yàn)槎ㄎ粚?shí)戰(zhàn)锅尘,非教程
  • 看了些評(píng)論井辜,多是不明覺厲著摔,希望你可以評(píng)主題相關(guān)的討論或感謝
  • 這篇文章威力巨大[呵呵]缓窜,所以不要做惡;不要做惡;不要做惡禾锤;

不要做惡:
比如游戲有12個(gè)成語(yǔ)要找私股,但代碼只答對(duì)了11個(gè),還有1個(gè)成語(yǔ)4個(gè)字恩掷,共有4*3*2*1=24種排列組合倡鲸,請(qǐng)讀者不要發(fā)24個(gè)請(qǐng)求來找到這最后一個(gè)成語(yǔ),這是樓主認(rèn)為的'做惡'黄娘,也是對(duì)自動(dòng)化:對(duì)沒有全部找到的峭状,放棄這局:等待對(duì)方贏得游戲做出的取舍;你可以用游戲里的認(rèn)輸提示

寫這篇文章與代碼的目的:

  • 虛榮:有讀者閱讀逼争、評(píng)論
  • 金錢:贏得游戲有幾分錢
  • 時(shí)間:游戲里插了很廣告优床,跳過廣告;自動(dòng)化節(jié)約自己時(shí)間
  • 能力:要寫能用的代碼誓焦,一定要學(xué)點(diǎn)什么胆敞,比如學(xué)習(xí)了websocket庫(kù)
  • 愛惜:自己的手機(jī)用了3年了,移植到電腦上來執(zhí)行杂伟,可以讓手機(jī)再戰(zhàn)一年啊
  • 成就:代碼和文章等作品移层;不同維度地'虐人'的快感[鄙視]

好了,希望你找到了學(xué)習(xí)的興趣與動(dòng)力赫粥,上代碼

測(cè)試環(huán)境

App: 趣消除AppiOS版本幽钢、扶我起來學(xué)數(shù)學(xué)AppiOS版本
工具: python、Charles傅是、python第三方庫(kù)websocket
背景知識(shí):python匪燕、抓包、websocket

解決

分析

成語(yǔ)消消樂有2個(gè)接口:

  1. https://king.hddgood.com/king_api/v1/game/join_game[http]
  2. wss://king.hddgood.com/websock_m/websock_message?uid={}&gameid={}&token={}[websocket]
  • game/join_game接口會(huì)返回websock_m/websock_message接口需要的gameid喧笔;gameid每局都不同
  • uid對(duì)每個(gè)賬號(hào)是固定
  • token對(duì)一次登入是固定帽驯,每局游戲都一樣;
  • 游戲的消息來回傳遞都在websock_m/websock_message接口websocket協(xié)議里完成
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個(gè)文件:chengyu-auto.py[代碼文件]书闸、chengyu.text[數(shù)據(jù)文件]

  • chengyu-auto.pyasking消息里解析出ask_stringchengyu.text文件里查找是否包含相應(yīng)的成語(yǔ)
  • 自動(dòng)提交成語(yǔ)答案
  • chengyu.text文件剛開始是空的尼变;在每局游戲結(jié)束時(shí),游戲都會(huì)發(fā)送game_result消息給我們浆劲,里面有這局游戲的答案成語(yǔ)嫌术,把這些成語(yǔ)寫到文件中
  • 玩的局?jǐn)?shù)越多,chengyu.text文件包含的成語(yǔ)越多牌借,查找到答案的可能性越大

代碼

需要安裝第三方python庫(kù):websockets
chengyu-auto.py

#!/usr/bin/env python3
# coding=utf-8

'''
# 趣消除App-成語(yǔ)消消樂全自動(dòng)化度气;
# App版本:1.1.2
# App地址:https://itunes.apple.com/cn/app/id1449545954
提現(xiàn)非常迅速
'''

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抓包軟件獲得
# 賬號(hào)變量
# ------------------------------------------------
# A_Token_Header的一些結(jié)論:
# 1.每個(gè)賬號(hào)不同;
# 2.同一個(gè)賬號(hào)每次登錄時(shí)也是不一樣的
# 3.同一個(gè)賬號(hào)膨报,退出時(shí)磷籍,只要不登錄适荣,上次的A-Token-Header的值還有效,只有再登錄時(shí)院领,上次的token值才失敗
A_Token_Header_zxg = 'PTtWUFdWUkBFHEVZCVcNdUtVWwdc'


# Cookie的一些結(jié)論:
# 1.同一個(gè)賬號(hào)弛矛,退出或再登錄,都不用修改比然,一直有效
# 2.值為空也可以
Cookie_zxg = ''

# UUID的一些結(jié)論:
# 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):
        '''
        成語(yǔ)消消樂-獲取游戲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("成語(yǔ)消消樂-獲取游戲id {}".format(self.uid))

        data = self._uid_data()
        # 1:書童丈氓;2:儒生;15:殿閣大學(xué)士
        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

        # 自動(dòng)提交答案的網(wǎng)絡(luò)發(fā)送次數(shù)
        self.auto_send_answers = list()
        self.ack_true_answers = list()

        # 找到的的成語(yǔ)中各異字符為2個(gè)的答案數(shù)量:如 [真真假假] 
        self.answer_2chars_count = 0

        # {'中流砥柱':[1,9,21,25]}
        self.answer_indexs_dict = dict()

        # {'中流砥柱':set('中流砥柱')}
        self.answer_charset_dict = dict()

        # 查找到的錯(cuò)誤答案
        self.error_answers = []

    # ---------------------------------------------
    def find_answers_v2(self, ask_string):
        '''
            在內(nèi)存成語(yǔ)字典查找答案
        '''      
        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('算法有錯(cuò)誤:{} 小于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
            # 向服務(wù)器發(fā)送消息
            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):
        '''
            把答案增加到內(nèi)存字典中
        '''
        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 共收錄{}個(gè)成語(yǔ) \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('共找到 {}/{} 個(gè)成語(yǔ)'.format(self.count, len(self.ask_string)//4))
        self.print_color('錯(cuò)誤成語(yǔ) {}'.format(self.error_answers))
        self.print_color('共自動(dòng) {} 次提交:{}'.format(len(self.auto_send_answers),self.auto_send_answers))
        self.print_color('已確認(rèn) {} 個(gè)提交:{}'.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):
    '''
    成語(yǔ)消消樂自動(dòng)答題
    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']        
                        # 計(jì)算答案
                        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

                    # 自動(dòng)答題
                    await chengyu.auto_answer(websocket)

                    # 顯示答案
                    if len(chengyu.ask_string):
                        chengyu.print_answers()

                    
                    if message_type == 'game_result':
                        live = False
                        # 把答案增加到內(nèi)存字典中
                        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 游戲結(jié)束 \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('開始時(shí)間 ', start_time)
        print('結(jié)束時(shí)間 ', time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))      

chengyu.text

一勞永逸
一擲千金
一曝十寒
一石二鳥
一籌莫展
一落千丈
一衣帶水
一語(yǔ)破的
...

注意:
chengyu.textchengyu-auto.py放在同一目錄下
chengyu.text收集約1926個(gè)成語(yǔ)万俗,98%能找到全部答案

參考

樓主的趣消除App系列文章

  1. 趣消除App自動(dòng)化 - 簽到and作戰(zhàn)休息區(qū)
  2. 趣消除App自動(dòng)化 - 成語(yǔ)消消樂-半自動(dòng)化
  3. 趣消除App自動(dòng)化 - 成語(yǔ)消消樂-全自動(dòng)化
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市拟烫,隨后出現(xiàn)的幾起案子该编,更是在濱河造成了極大的恐慌,老刑警劉巖硕淑,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件课竣,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡置媳,警方通過查閱死者的電腦和手機(jī)于樟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拇囊,“玉大人迂曲,你說我怎么就攤上這事×认” “怎么了所意?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵痛倚,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)车猬,這世上最難降的妖魔是什么咽扇? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任钻蔑,我火速辦了婚禮拧抖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘识埋。我一直安慰自己凡伊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布窒舟。 她就那樣靜靜地躺著系忙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辜纲。 梳的紋絲不亂的頭發(fā)上笨觅,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天拦耐,我揣著相機(jī)與錄音耕腾,去河邊找鬼见剩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛扫俺,可吹牛的內(nèi)容都是我干的苍苞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼狼纬,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼羹呵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起疗琉,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤冈欢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后盈简,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凑耻,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年柠贤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了香浩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡臼勉,死狀恐怖邻吭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情宴霸,我是刑警寧澤囱晴,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站瓢谢,受9級(jí)特大地震影響畸写,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜恩闻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一艺糜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧幢尚,春花似錦破停、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至理茎,卻和暖如春黑界,著一層夾襖步出監(jiān)牢的瞬間管嬉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工朗鸠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蚯撩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓烛占,卻偏偏與公主長(zhǎng)得像胎挎,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子忆家,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348