breadq 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
breadq-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,35 @@
1
+ Metadata-Version: 2.4
2
+ Name: breadq
3
+ Version: 0.1.0
4
+ Summary: Multiplayer client for QBReader
5
+ Home-page: https://github.com/packjackisback/breadq
6
+ Author: Jack
7
+ Author-email: packjackisback@gmail.com
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: websockets>=11.0
11
+ Dynamic: author
12
+ Dynamic: author-email
13
+ Dynamic: description
14
+ Dynamic: description-content-type
15
+ Dynamic: home-page
16
+ Dynamic: requires-dist
17
+ Dynamic: requires-python
18
+ Dynamic: summary
19
+
20
+ # breadq
21
+
22
+ A QBReader multiplayer library.
23
+
24
+ This is meant for making clients, bots are very easy to make and boring.
25
+ (Coming soon (tm) a tui qbreader client)
26
+
27
+
28
+ Please don't use this to cheat, it really just looks bad on you.
29
+
30
+ TODO:
31
+
32
+ Adding the rest of the outgoing events
33
+ Adding state
34
+ Adding ratelimiting
35
+ Documentation
breadq-0.1.0/README.md ADDED
@@ -0,0 +1,16 @@
1
+ # breadq
2
+
3
+ A QBReader multiplayer library.
4
+
5
+ This is meant for making clients, bots are very easy to make and boring.
6
+ (Coming soon (tm) a tui qbreader client)
7
+
8
+
9
+ Please don't use this to cheat, it really just looks bad on you.
10
+
11
+ TODO:
12
+
13
+ Adding the rest of the outgoing events
14
+ Adding state
15
+ Adding ratelimiting
16
+ Documentation
@@ -0,0 +1,3 @@
1
+ from .client import QBReaderClient
2
+
3
+ __all__ = ["QBReaderClient"]
@@ -0,0 +1,49 @@
1
+ import asyncio
2
+
3
+ def register(client):
4
+
5
+ async def pause(paused_time=0):
6
+ await client.emit("pause", pausedTime=paused_time)
7
+
8
+ async def buzz():
9
+ await client.emit("buzz")
10
+ await give_answer_live_update()
11
+
12
+ async def ban(userId, username):
13
+ await client.emit("ban", targetId=userId, targetUsername=username)
14
+
15
+ async def chat_live_update(message=""):
16
+ await client.emit("chat-live-update", message=message)
17
+
18
+ async def chat(message=""):
19
+ await chat-live-update("")
20
+ await client.emit("chat", message=message)
21
+
22
+ async def clear_stats():
23
+ await client.emit("clear-stats")
24
+
25
+ async def give_answer_live_update(message=""):
26
+ await client.emit("give-answer-live-update", givenAnswer=message)
27
+
28
+ async def give_answer(message=""):
29
+ await give_answer_live_update()
30
+ await client.emit("give-answer", givenAnswer=message)
31
+
32
+
33
+
34
+ client.pause = pause
35
+ client.buzz = buzz
36
+ client.ban = ban
37
+ client.chat_live_update = chat_live_update
38
+ client.chat = chat
39
+ client.clear_stats = clear_stats
40
+ client.give_answer_live_update = give_answer_live_update
41
+ client.give_answer = give_answer
42
+
43
+ async def do(action_name, **kwargs):
44
+ action = getattr(client, action_name, None)
45
+ if not action:
46
+ raise ValueError(f"Unknown action: {action_name}")
47
+ await action(**kwargs)
48
+
49
+ client.do = do
@@ -0,0 +1,115 @@
1
+ import asyncio
2
+ import json
3
+ import uuid
4
+ import websockets
5
+
6
+ from . import actions
7
+
8
+ # gotta enable that logging
9
+ import logging
10
+
11
+ class QBReaderClient:
12
+ def __init__(self, room, username, user_id=None, debug_mode=False):
13
+ if debug_mode:
14
+ logging.basicConfig(level=logging.DEBUG)
15
+ logging.getLogger("websockets").setLevel(logging.DEBUG)
16
+ print("Debug Mode ON")
17
+ print("Loading ", username)
18
+
19
+
20
+
21
+ self.room = room
22
+ self.username = username
23
+ self.user_id = user_id or str(uuid.uuid4())
24
+
25
+ self.ws = None
26
+ self.handlers = {}
27
+ self.send_queue = asyncio.Queue()
28
+ self.running = False
29
+ self.debug_mode = debug_mode
30
+
31
+ actions.register(self)
32
+ if debug_mode:
33
+ print(username, " Loaded: ", hasattr(self, "buzz"))
34
+
35
+ def on(self, message_type, handler):
36
+ self.handlers[message_type] = handler
37
+
38
+ async def emit(self, event_type: str, **data):
39
+ payload = {"type": event_type, **data}
40
+ await self.send(payload)
41
+
42
+ async def send(self, payload: dict):
43
+ if self.debug_mode:
44
+ print(f"[{self.user_id}] SEND -> {payload}")
45
+ await self.send_queue.put(payload)
46
+
47
+ async def start(self):
48
+ url = (
49
+ f"wss://www.qbreader.org/play/mp/test"
50
+ f"?roomName={self.room}"
51
+ f"&userId={self.user_id}"
52
+ f"&username={self.username}"
53
+ )
54
+
55
+
56
+ try:
57
+ async with websockets.connect(
58
+ url,
59
+ origin="https://www.qbreader.org",
60
+ ping_interval=None
61
+ ) as ws:
62
+ self.ws = ws
63
+ self.running = True
64
+
65
+ await asyncio.gather(
66
+ self._receiver_loop(),
67
+ self._sender_loop(),
68
+ self._heartbeat_loop(),
69
+ )
70
+
71
+ except websockets.exceptions.ConnectionClosed as e:
72
+ print(
73
+ f"[{self.user_id}] CLOSED "
74
+ f"code={e.code} reason={e.reason}"
75
+ )
76
+ raise
77
+
78
+ except Exception as e:
79
+ print(f"[{self.user_id}] START ERROR -> {e}")
80
+ raise
81
+
82
+
83
+
84
+ async def _receiver_loop(self):
85
+ try:
86
+ async for message in self.ws:
87
+ if self.debug_mode:
88
+ print(f"[{self.user_id}] RECV RAW -> {message}")
89
+
90
+ try:
91
+ data = json.loads(message)
92
+ except Exception as e:
93
+ print(f"[{self.user_id}] JSON ERROR -> {e}")
94
+ continue
95
+
96
+ msg_type = data.get("type")
97
+ handler = self.handlers.get(msg_type)
98
+
99
+ if handler:
100
+ await handler(data)
101
+
102
+ except Exception as e:
103
+ print(f"[{self.user_id}] RECEIVER ERROR -> {e}")
104
+ raise
105
+
106
+
107
+ async def _sender_loop(self):
108
+ while self.running:
109
+ payload = await self.send_queue.get()
110
+ await self.ws.send(json.dumps(payload))
111
+
112
+ async def _heartbeat_loop(self):
113
+ while self.running:
114
+ await asyncio.sleep(20)
115
+ await self.emit("ping")
@@ -0,0 +1,77 @@
1
+ import asyncio
2
+ import json
3
+ import uuid
4
+ import websockets
5
+
6
+
7
+ class QBReaderClient:
8
+ def __init__(self, room, username, user_id=None):
9
+ self.room = room
10
+ self.username = username
11
+ self.user_id = user_id or str(uuid.uuid4())
12
+
13
+ self.ws = None
14
+ self.handlers = {}
15
+ self.send_queue = asyncio.Queue()
16
+ self.running = False
17
+
18
+ def on(self, message_type, handler):
19
+ """
20
+ Register a handler for a message type.
21
+ """
22
+ self.handlers[message_type] = handler
23
+
24
+ async def send(self, payload: dict):
25
+ """
26
+ Queue a message to be sent.
27
+ """
28
+ await self.send_queue.put(payload)
29
+
30
+ async def pause(self):
31
+ await self.send({"type": "pause", "pausedTime": 0})
32
+
33
+ async def start(self):
34
+ url = (
35
+ f"wss://www.qbreader.org/play/mp/test"
36
+ f"?roomName={self.room}"
37
+ f"&userId={self.user_id}"
38
+ f"&username={self.username}"
39
+ )
40
+
41
+ async with websockets.connect(
42
+ url,
43
+ origin="https://www.qbreader.org",
44
+ ping_interval=None
45
+ ) as ws:
46
+
47
+ self.ws = ws
48
+ self.running = True
49
+
50
+ await asyncio.gather(
51
+ self._receiver_loop(),
52
+ self._sender_loop(),
53
+ self._heartbeat_loop(),
54
+ )
55
+
56
+ async def _receiver_loop(self):
57
+ async for message in self.ws:
58
+ try:
59
+ data = json.loads(message)
60
+ except Exception:
61
+ continue
62
+
63
+ msg_type = data.get("type")
64
+ handler = self.handlers.get(msg_type)
65
+
66
+ if handler:
67
+ await handler(data)
68
+
69
+ async def _sender_loop(self):
70
+ while self.running:
71
+ payload = await self.send_queue.get()
72
+ await self.ws.send(json.dumps(payload))
73
+
74
+ async def _heartbeat_loop(self):
75
+ while self.running:
76
+ await asyncio.sleep(1)
77
+ await self.send({"type": "ping"})
@@ -0,0 +1,35 @@
1
+ Metadata-Version: 2.4
2
+ Name: breadq
3
+ Version: 0.1.0
4
+ Summary: Multiplayer client for QBReader
5
+ Home-page: https://github.com/packjackisback/breadq
6
+ Author: Jack
7
+ Author-email: packjackisback@gmail.com
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: websockets>=11.0
11
+ Dynamic: author
12
+ Dynamic: author-email
13
+ Dynamic: description
14
+ Dynamic: description-content-type
15
+ Dynamic: home-page
16
+ Dynamic: requires-dist
17
+ Dynamic: requires-python
18
+ Dynamic: summary
19
+
20
+ # breadq
21
+
22
+ A QBReader multiplayer library.
23
+
24
+ This is meant for making clients, bots are very easy to make and boring.
25
+ (Coming soon (tm) a tui qbreader client)
26
+
27
+
28
+ Please don't use this to cheat, it really just looks bad on you.
29
+
30
+ TODO:
31
+
32
+ Adding the rest of the outgoing events
33
+ Adding state
34
+ Adding ratelimiting
35
+ Documentation
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ breadq/__init__.py
5
+ breadq/actions.py
6
+ breadq/client.py
7
+ breadq/qbreader_client.py
8
+ breadq.egg-info/PKG-INFO
9
+ breadq.egg-info/SOURCES.txt
10
+ breadq.egg-info/dependency_links.txt
11
+ breadq.egg-info/requires.txt
12
+ breadq.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ websockets>=11.0
@@ -0,0 +1 @@
1
+ breadq
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
breadq-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
breadq-0.1.0/setup.py ADDED
@@ -0,0 +1,21 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ with open("README.md", "r") as fh:
4
+ long_description = fh.read()
5
+
6
+ setup(
7
+ name="breadq",
8
+ version="0.1.0",
9
+ author="Jack",
10
+ author_email="packjackisback@gmail.com",
11
+ description="Multiplayer client for QBReader",
12
+ long_description=long_description,
13
+ long_description_content_type="text/markdown",
14
+ url="https://github.com/packjackisback/breadq",
15
+ packages=find_packages(),
16
+ install_requires=[
17
+ "websockets>=11.0",
18
+ ],
19
+ python_requires=">=3.9",
20
+ )
21
+