Teekeks / pyTwitchAPI

A Python 3.7 compatible implementation of the Twitch API, EventSub, PubSub and Chat
https://pytwitchapi.dev
MIT License
256 stars 38 forks source link

(EventSub/Websocket) TypeError when handling EventSub listen_channel_prediction_end() #315

Closed Gregorein closed 4 months ago

Gregorein commented 4 months ago

When mocking twitch websocket I've encountered the following error:

Task exception was never retrieved
future: <Task finished name='Task-117' coro=<EventSubWebsocket._handle_notification() done, defined at /mnt/c/Users/greg/Code/aileen/.venv/lib/python3.10/site-packages/twitchAPI/eventsub/websocket.py:418> exception=TypeError('twitchAPI.object.eventsub.TopPredictors() argument after ** must be a mapping, not list')>
Traceback (most recent call last):
  File "/mnt/c/Users/greg/Code/aileen/.venv/lib/python3.10/site-packages/twitchAPI/eventsub/websocket.py", line 429, in _handle_notification
    t = self._callback_loop.create_task(callback['callback'](callback['event'](**_payload)))
  File "/mnt/c/Users/greg/Code/aileen/.venv/lib/python3.10/site-packages/twitchAPI/object/base.py", line 121, in __init__
    self.__setattr__(name, TwitchObject._val_by_instance(cls, kwargs.get(name)))
  File "/mnt/c/Users/greg/Code/aileen/.venv/lib/python3.10/site-packages/twitchAPI/object/base.py", line 54, in _val_by_instance
    return instance(**val)
  File "/mnt/c/Users/greg/Code/aileen/.venv/lib/python3.10/site-packages/twitchAPI/object/base.py", line 121, in __init__
    self.__setattr__(name, TwitchObject._val_by_instance(cls, kwargs.get(name)))
  File "/mnt/c/Users/greg/Code/aileen/.venv/lib/python3.10/site-packages/twitchAPI/object/base.py", line 44, in _val_by_instance
    return [TwitchObject._val_by_instance(c, x) for x in val]
  File "/mnt/c/Users/greg/Code/aileen/.venv/lib/python3.10/site-packages/twitchAPI/object/base.py", line 44, in <listcomp>
    return [TwitchObject._val_by_instance(c, x) for x in val]
  File "/mnt/c/Users/greg/Code/aileen/.venv/lib/python3.10/site-packages/twitchAPI/object/base.py", line 54, in _val_by_instance
    return instance(**val)
  File "/mnt/c/Users/greg/Code/aileen/.venv/lib/python3.10/site-packages/twitchAPI/object/base.py", line 121, in __init__
    self.__setattr__(name, TwitchObject._val_by_instance(cls, kwargs.get(name)))
  File "/mnt/c/Users/greg/Code/aileen/.venv/lib/python3.10/site-packages/twitchAPI/object/base.py", line 54, in _val_by_instance
    return instance(**val)
TypeError: twitchAPI.object.eventsub.TopPredictors() argument after ** must be a mapping, not list

The error occurs when listen_channel_prediction_end() is hit with a matching event

test_predictionEnd_id = await eventSub.listen_channel_prediction_end(user.id, onPredictionEnd)

This is the testrunner log:

Executing: twitch event trigger -T websocket channel.prediction.end -u 44a31bc2-738c-0c9e-4d8e-36bc90f7b414
✔ Forwarded for use in mock EventSub WebSocket server
{
  "subscription": {
    "id": "44a31bc2-738c-0c9e-4d8e-36bc90f7b414",
    "status": "enabled",
    "type": "channel.prediction.end",
    "version": "1",
    "condition": {
      "broadcaster_user_id": "23541189"
    },
    "transport": {
      "method": "websocket",
      "session_id": "WebSocket-Server-Will-Set"
    },
    "created_at": "2024-06-27T09:50:51.077638339Z",
    "cost": 0
  },
  "event": {
    "broadcaster_user_id": "23541189",
    "broadcaster_user_login": "testBroadcaster",
    "broadcaster_user_name": "testBroadcaster",
    "ended_at": "2024-06-27T10:00:51.077638339Z",
    "id": "7404edfe-ae7c-7300-df3e-5ac9f1393a48",
    "outcomes": [
      {
        "channel_points": 16247,
        "color": "blue",
        "id": "53b5ba0b-a3b9-3355-7c12-bb5805db54cc",
        "title": "yes",
        "top_predictors": [
          {
            "channel_points_used": 9783,
            "channel_points_won": 19566,
            "user_id": "85893336",
            "user_login": "testLogin",
            "user_name": "testLogin"
          },
          {
            "channel_points_used": 197,
            "channel_points_won": 394,
            "user_id": "96314864",
            "user_login": "testLogin",
            "user_name": "testLogin"
          },
          {
            "channel_points_used": 3361,
            "channel_points_won": 6722,
            "user_id": "17321125",
            "user_login": "testLogin",
            "user_name": "testLogin"
          },
          {
            "channel_points_used": 2733,
            "channel_points_won": 5466,
            "user_id": "44136584",
            "user_login": "testLogin",
            "user_name": "testLogin"
          },
          {
            "channel_points_used": 173,
            "channel_points_won": 346,
            "user_id": "8137250",
            "user_login": "testLogin",
            "user_name": "testLogin"
          }
        ],
        "users": 5
      },
      {
        "channel_points": 53116,
        "color": "pink",
        "id": "e72a7acc-b4d7-18ab-be54-828deb6ca797",
        "title": "no",
        "top_predictors": [
          {
            "channel_points_used": 9757,
            "channel_points_won": 0,
            "user_id": "15305621",
            "user_login": "testLogin",
            "user_name": "testLogin"
          },
          {
            "channel_points_used": 5952,
            "channel_points_won": 0,
            "user_id": "98245283",
            "user_login": "testLogin",
            "user_name": "testLogin"
          },
          {
            "channel_points_used": 2300,
            "channel_points_won": 0,
            "user_id": "13470591",
            "user_login": "testLogin",
            "user_name": "testLogin"
          },
          {
            "channel_points_used": 4727,
            "channel_points_won": 0,
            "user_id": "50751763",
            "user_login": "testLogin",
            "user_name": "testLogin"
          },
          {
            "channel_points_used": 9933,
            "channel_points_won": 0,
            "user_id": "71609232",
            "user_login": "testLogin",
            "user_name": "testLogin"
          },
          {
            "channel_points_used": 9352,
            "channel_points_won": 0,
            "user_id": "78071556",
            "user_login": "testLogin",
            "user_name": "testLogin"
          },
          {
            "channel_points_used": 3230,
            "channel_points_won": 0,
            "user_id": "73841872",
            "user_login": "testLogin",
            "user_name": "testLogin"
          },
          {
            "channel_points_used": 7865,
            "channel_points_won": 0,
            "user_id": "22723678",
            "user_login": "testLogin",
            "user_name": "testLogin"
          }
        ],
        "users": 8
      }
    ],
    "started_at": "2024-06-27T09:50:51.077638339Z",
    "status": "resolved",
    "title": "Will the developer finish this program?",
    "winning_outcome_id": "53b5ba0b-a3b9-3355-7c12-bb5805db54cc"
  }
}

(subscription json was formatted for easier debug)

I'm tinkering with the library code, but I'm not sure where to fix it yet, help welcome :)

Gregorein commented 4 months ago

Possibly related to https://github.com/Teekeks/pyTwitchAPI/issues/312

Gregorein commented 4 months ago

following example from the other issue, I've implemented a temp fix #312

async def _handle_notification(self, data: dict):
  self._reset_timeout()
  _payload = data.get('payload', {})
  sub_id = _payload.get('subscription', {}).get('id')
  callback = self._callbacks.get(sub_id)
  if callback is None:
    self.logger.error(f'received event for unknown subscription with ID {sub_id}')
  else:             
    # hacky fix for predictions
    if _payload["subscription"]["type"] in ["channel.prediction.end", "channel.prediction.lock"]:
      for each in _payload["event"]["outcomes"]:
        del each["top_predictors"]

    t = self._callback_loop.create_task(callback['callback'](callback['event'](**_payload)))
    t.add_done_callback(self._task_callback)
Teekeks commented 4 months ago

Oh, I just realized that I tested this in my dev version and not the release version. I fixed this in 3344ff67a084dfd62b748507ec57168f71f8d139 which is part of the next release. Will do a bugfix release out of this rn.

Teekeks commented 4 months ago

Fix for this is now available on pypi in v4.2.1.