Python: TypeError: нехешируемый тип: «Изображение»

Я пытаюсь создать бота для MMO под названием «Graal Online Classic» с помощью Pyautogui. Он будет читать «ЛС» (личное сообщение) игрока, который отправляет ему сообщение, а затем отвечает соответствующим ответом. Он также сделает снимок экрана с именем игрока, сохранит его в словаре (а также сохранит, где этот конкретный игрок находится в иерархии ответов бота) и использует это, чтобы определить, отправлял ли игрок сообщение им раньше.

import time
import pyautogui

#option = None

def main():
    #find_notification()
    name_coords, reply_coords, text_coords = set_message_coords()
    option = read_message(text_coords)
    player_id(name_coords, option)
    answer = response()
    reply(reply_coords, answer)


def find_notification(): #looks for a waiting PM and clicks it
    while True:
        image = pyautogui.locateCenterOnScreen('test.png', grayscale = False, confidence = .9)
        print(image)
        if image is not None:
            print('Found a waiting message')
            pyautogui.click(image)
            break


def set_message_coords(): # Creates coords for message box and screenshots name
    try:
        imagex, imagey = pyautogui.locateCenterOnScreen('upper_right_message_corner.png', grayscale = True, confidence = .8)

    except:
        print('ERROR I SHOULD BE FINDING "upper_right_message_corner.PNG" EXITING PROGRAM') 
        exit()
    name_coords = (imagex - 424), imagey, 378, 50 # The coords of where the players name would be
    print('Found an open message')
    print(imagex, imagey)

    reply_coords = (imagex - 251), (imagey + 255 ) # Coords of the reply button
    text_coords = (imagex - 461), (imagey + 45), 430, 45 # Coords of where a players possible response is
    return name_coords, reply_coords, text_coords # Returns all coord values to be used by other functions


def player_id(name_coords, option): # Indentifies person who messaged and depending on if this person has messaged before changes response
    players = {} # Used to store players names and where in the response is

    name_image = pyautogui.screenshot('name_test.png', region = name_coords) # Names are screenshots 

    if name_image not in players:
        print("User was not previously found, adding to dictionary.")
        players[name_image] = None
    else:
        print("User is previous user.")
        players[name_image] = players[name_image] + option
        return players[name_image]


def reply(reply_coords, response): #Replies to PM
    pyautogui.click(reply_coords)
    pyautogui.typewrite(response)
    pyautogui.press('enter')


def read_message(text_coords): # Reads PM for numbers and sets option the number
    if pyautogui.locateCenterOnScreen('1.png',region = text_coords,  confidence = .9, grayscale = True):
        option = '1'
    elif pyautogui.locateCenterOnScreen('2.png',region = text_coords,  confidence = .9, grayscale = True):
        option = '2'
    elif pyautogui.locateCenterOnScreen('3.png',region = text_coords,  confidence = .9, grayscale = True):
        option = '3'
    elif pyautogui.locateCenterOnScreen('4.png',region = text_coords,  confidence = .9, grayscale = True):
        option = '4'
    elif pyautogui.locateCenterOnScreen('5.png',region = text_coords,  confidence = .9, grayscale = True):
        option = '5'
    elif pyautogui.locateCenterOnScreen('6.png',region = text_coords,  confidence = .9, grayscale = True):
        option = '6'
    elif pyautogui.locateCenterOnScreen('7.png',region = text_coords,  confidence = .9, grayscale = True):
        option = '7'
    elif pyautogui.locateCenterOnScreen('8.png',region = text_coords,  confidence = .9, grayscale = True):
        option = '8'
    elif pyautogui.locateCenterOnScreen('9.png',region = text_coords,  confidence = .9, grayscale = True):
        option = '9'
    elif pyautogui.locateCenterOnScreen('0.png',region = text_coords,  confidence = .9, grayscale = True):
        option = '0'
    else:
        print('ERROR CANT FIND DIGIT ANSWER')
        option = None
        #reply(reply_coords,'ERROR PLEASE ENTER A NUMBER RESPONSE!')
        #main()
    print(option)
    return option


def response(option): # All the possible responses the bot can give a player
    if option == None:
        return "Hello! I am bot made to answer your questions! PM the number for more options! 1: Bounties. 2: Bug Hunting. 3: Looting. 4: Farming."
    elif option == '1':
        return "The bounty quest allows you to hunt mobs for cash! Location: Castle, steward's room(to the right in the throne room) [PM 1 for specifics, PM 2 for TIPS, PM 3 for possible bounties]"
    elif option == '12':
        return '1. The bounty box will "drop"(stop following you, and will not pick up anything) after every kill/capture you get, and will require you to call it, or run over it to pick it up again. [PM 1 for more info, PM 9 to go back one level, PM 0 to reset choices]'
    elif option == '13':
        return '100 green blobs, 20 Lizardons, 75 Pyrats(as homage to the PQ I assume), 75 Rebel soldiers(regular green baddy), 60 dark blobs, 60 rats, 75 snakes, 75 bats, 75 bandits, 80 spiders, 50 archers, or 50 crabs. [PM 9 to go back one level, PM 0 to reset choices]'

main()

Выдает следующую ошибку.

Найдено открытое сообщение 692 371 ОШИБКА НЕ НАЙТИ ЦИФРОВОЙ ОТВЕТ Нет Отслеживание (последний последний вызов): Файл "C:\Users\Pablo\OneDrive\python_projects\chat_bot\main.py", строка 104, в main() Файл "C :\Users\Pablo\OneDrive\python_projects\chat_bot\main.py", строка 10, в основном файле player_id(name_coords, option) File "C:\Users\Pablo\OneDrive\python_projects\chat_bot\main.py", строка 47 , в player_id, если name_image не в плеерах: TypeError: unhashable type: 'Image' [Завершено в 1.9s]

Произошла ошибка в функции player_id()

У меня нет большого опыта работы со списками и словарями, поэтому я не знаю, как с этим справиться. Я просмотрел похожие темы с ошибкой «TypeError: unhashable type:», но я не очень понимаю ответы. Любая помощь будет принята с благодарностью, спасибо.


person Pablo Castro    schedule 28.05.2017    source источник
comment
все, что возвращается из pyautogui.screenshot (в данном случае name_image), не является хэшируемым объектом, поэтому его нельзя использовать в качестве ключа в словаре.   -  person ozgur    schedule 28.05.2017
comment
Есть ли способ использовать name_image в словаре? Или как способ определить, отправлял ли name_image сообщение моему боту ранее?   -  person Pablo Castro    schedule 28.05.2017
comment
@OzgurVatansever   -  person Pablo Castro    schedule 28.05.2017
comment
Даже если это сработает, как вы ожидаете добавить option к начальному значению None? И я не знаком с pyautogui, но что-то мне подсказывает, что делать скриншоты сообщений на экране не является безопасным/надежным способом идентификации пользователей. Разве у вас не должен быть доступ к каким-то базовым (текстовым) событиям/сообщениям?   -  person Andras Deak    schedule 29.05.2017
comment
@AndrasDeak Вот изображение PM, полученное в игре. i.imgur.com/JpzsxZ1.png Вы правы, использование скриншотов имен недопустимо. Это самый надежный способ идентификации пользователей, но на самом деле это единственный способ их идентификации. Там есть четкое имя, но некоторые тесты с pytesseract показали мне, что это не самый надежный способ ведения дел (поэтому в read_message() я ищу изображения вместо текста). О добавлении параметра «Нет», «Нет» не должно быть в параметре, когда мы доберемся до игроков [имя_изображения] + вариант, который я планировал перезаписать номером «Нет» до этого момента.   -  person Pablo Castro    schedule 29.05.2017
comment
None не будет в option, это верно. Но None будет в players[name_image]. И тогда у вас фактически будет None + '1' или что-то в этом роде. None + <anything>. Также: вам не нужно обрабатывать ввод option в случае нового пользователя? В этом случае вам нужно инициализировать option, и ваш None самопроизвольно исчезнет. Насчёт скринов: откуда программа знает, что ей попалась личка? Должен быть какой-то базовый механизм, к которому вы можете подключиться. Эти сообщения откуда-то приходят, верно?   -  person Andras Deak    schedule 29.05.2017


Ответы (1)


Вы не можете напрямую хэшировать объект PIL.Image.Image(). Вместо этого вы можете создать и сохранить объект bytes, а затем получить этот объект bytes следующим образом:

# Store the image
name_image = pyautogui.screenshot('name_test.png', region = name_coords) 
name_image_asbytes = name_image.tobytes()
players[name_image_asbytes] = None

# Retrieve data associated with the image
# In this case, I am using my_img as the image I want to get associated data from
data = players[my_img.tobytes()]

# Create image from bytes (in case you ever need to later on)
# You would also need to install the PIL library for this, but I suspect it is already installed because pyautogui needs PIL
# In this case, I am getting the image associated with the first player stored the "players" dictionary
from PIL.Image import Image
my_img = Image.frombytes(player.keys()[0])
# Do whatever you want with my_img
person Abc Bcd    schedule 03.02.2021