Name | chio JSON |
Version |
1.1.20
JSON |
| download |
home_page | None |
Summary | A python library for serializing and deserializing bancho packets. |
upload_time | 2025-08-08 00:46:10 |
maintainer | None |
docs_url | None |
author | Lekuru |
requires_python | None |
license | None |
keywords |
osu
osugame
python
bancho
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# Chio



**Chio (Bancho I/O)** is a python library for serializing and deserializing bancho packets, with support for all versions of osu! that use bancho (2008-2025).
It was made with the intention of documenting everything about the bancho protocol, and to provide a base for server frameworks, since the packet handling part is most often the annoying part.
Having *any* client be able to connect to it is a very sweet addition on top, if you are interested in those as well.
**If you wish to use this library, I would appreciate some credit for my work. Thanks!**
## Usage
This library requires an installation of python **3.8** or higher.
You can install the library with pip:
```shell
pip install chio
```
Or you can also install it from source directly, if preferred:
```shell
pip install git+https://github.com/Lekuruu/chio.py
```
Here is a very basic example of how to use this library, and how to get a client to log in:
```python
import chio
# Chio expects you to have the `chio.Stream` class
# implemented, i.e. it needs a `read()` and `write()`
# function to work properly
stream = chio.Stream()
# The client version is how chio determines what
# protocol to use. This one can be parsed through the
# initial login request, that the client makes.
client_version = 20140127
# Chio has combined the user presence, stats and status
# into one class, to support more clients. You are also
# able to provide your own player class, as long as you
# have the same fields added on to it.
info = chio.UserInfo(
id=2,
name="peppy",
presence=chio.UserPresence(),
stats=chio.UserStats(),
status=chio.UserStatus()
)
# Select a client protocol to use for encoding/decoding
io = chio.select_client(client_version)
# Send the users information (userId, presence & stats)
io.write_packet(stream, chio.PacketType.BanchoLoginReply, info.id)
io.write_packet(stream, chio.PacketType.BanchoUserPresence, info)
io.write_packet(stream, chio.PacketType.BanchoUserStats, info)
# Force client to join #osu
io.write_packet(stream, chio.PacketType.BanchoChannelJoinSuccess, "#osu")
# Send a message in #osu from BanchoBot
io.write_packet(
stream,
chio.PacketType.BanchoMessage,
chio.Message(content="Hello, World!", sender="BanchoBot", target="#osu")
)
packet, data = io.read_packet(stream)
print(f"Received packet '{packet.name}' with {data}.")
```
You can also read & write from bytes directly, for example when using HTTP clients instead of TCP clients:
```python
encoded = io.write_packet_to_bytes(chio.PacketType.BanchoLoginReply, info.id)
packet, data = io.read_packet_from_bytes(b"...")
```
If you are using **asyncio**, you may want to use the `read_packet_async` & `write_packet_async` functions respectively for asynchronous usage.
This feature is currently untested, but should work in theory. If you encounter any bugs with it, don't be afraid to report them.
```python
encoded = await io.write_packet_async(stream, chio.PacketType.BanchoLoginReply, info.id)
packet, data = await io.read_packet_async(stream)
```
### Patching
You are able to overwrite specifc packet readers/writers, with the `chio.patch` decorator.
As an example, to patch the `BanchoUserStats` packet inside of `b20120723`:
```python
@chio.patch(PacketType.BanchoUserStats, 20120723)
def write_user_stats(cls, info: UserInfo):
stream = MemoryStream()
write_s32(stream, info.id)
stream.write(cls.write_status_update(info.status))
write_u64(stream, info.stats.rscore)
write_f32(stream, info.stats.accuracy)
write_u32(stream, info.stats.playcount)
write_u64(stream, info.stats.tscore)
write_u32(stream, info.stats.rank)
write_u16(stream, info.stats.pp)
yield PacketType.BanchoUserStats, stream.data
```
Additionally, it's possible to set a certain slot size & protocol version for each version:
```python
# Set protocol version to 10 for b20120818
chio.set_protocol_version(10, 20120818)
# Override slot size to 32 for b20160404
chio.set_slot_size(32, 20160404)
```
### Datatypes
Depending on the packet you send or receive, you will need to account for different datatypes.
Here is a list of them for each packet:
| Packet | Type |
|:------------------------------:|:-----------------------------------:|
| OsuUserStatus | `chio.UserStatus` |
| OsuMessage | `chio.Message` |
| OsuExit | `bool` (IsUpdating) |
| OsuStatusUpdateRequest | N/A |
| OsuPong | N/A |
| BanchoLoginReply | `int` (UserId) or `chio.LoginError` |
| BanchoMessage | `chio.Message` |
| BanchoPing | N/A |
| BanchoIrcChangeUsername | `str` (old name), `str` (new name) |
| BanchoIrcQuit | `str` (Username) |
| BanchoUserStats | `chio.UserInfo` |
| BanchoUserQuit | `chio.UserQuit` |
| BanchoSpectatorJoined | `int` (UserId) |
| BanchoSpectatorLeft | `int` (UserId) |
| BanchoSpectateFrames | `chio.ReplayFrameBundle` |
| OsuStartSpectating | `int` (UserId) |
| OsuStopSpectating | `int` (UserId) |
| OsuSpectateFrames | `chio.ReplayFrameBundle` |
| BanchoVersionUpdate | N/A |
| OsuErrorReport | `str` (Exception) |
| OsuCantSpectate | N/A |
| BanchoSpectatorCantSpectate | `int` (UserId) |
| BanchoGetAttention | N/A |
| BanchoAnnounce | `str` (Message) |
| OsuPrivateMessage | `chio.Message` |
| BanchoMatchUpdate | `chio.Match` |
| BanchoMatchNew | `chio.Match` |
| BanchoMatchDisband | `int` (MatchId) |
| OsuLobbyPart | N/A |
| OsuLobbyJoin | N/A |
| OsuMatchCreate | `chio.Match` |
| OsuMatchJoin | `int` (MatchId) |
| OsuMatchPart | N/A |
| BanchoLobbyJoin | `int` (UserId) |
| BanchoLobbyPart | `int` (UserId) |
| BanchoMatchJoinSuccess | `chio.Match` |
| BanchoMatchJoinFail | N/A |
| OsuMatchChangeSlot | `int` (SlotId) |
| OsuMatchReady | N/A |
| OsuMatchLock | `int` (SlotId) |
| OsuMatchChangeSettings | `chio.Match` |
| BanchoFellowSpectatorJoined | `int` (UserId) |
| BanchoFellowSpectatorLeft | `int` (UserId) |
| OsuMatchStart | N/A |
| BanchoMatchStart | `chio.Match` |
| OsuMatchScoreUpdate | `chio.ScoreFrame` |
| BanchoMatchScoreUpdate | `chio.ScoreFrame` |
| OsuMatchComplete | N/A |
| BanchoMatchTransferHost | N/A |
| OsuMatchChangeMods | `chio.Mods` |
| OsuMatchLoadComplete | N/A |
| BanchoMatchAllPlayersLoaded | N/A |
| OsuMatchNoBeatmap | N/A |
| OsuMatchNotReady | N/A |
| OsuMatchFailed | N/A |
| BanchoMatchPlayerFailed | `int` (SlotId) |
| BanchoMatchComplete | N/A |
| OsuMatchHasBeatmap | N/A |
| OsuMatchSkipRequest | N/A |
| BanchoMatchSkip | N/A |
| OsuChannelJoin | `str` (Channel Name) |
| BanchoChannelJoinSuccess | `str` (Channel Name) |
| BanchoChannelAvailable | `chio.Channel` |
| BanchoChannelRevoked | `str` (Channel Name) |
| BanchoChannelAvailableAutojoin | `chio.Channel` |
| OsuBeatmapInfoRequest | `chio.BeatmapInfoRequest` |
| BanchoBeatmapInfoReply | `chio.BeatmapInfoReply` |
| OsuMatchTransferHost | `int` (SlotId) |
| BanchoLoginPermissions | `chio.Permissions` or `int` |
| BanchoFriendsList | `list[int]` |
| OsuFriendsAdd | `int` (UserId) |
| OsuFriendsRemove | `int` (UserId) |
| BanchoProtocolNegotiation | N/A or `int` |
| BanchoTitleUpdate | `chio.TitleUpdate` |
| OsuMatchChangeTeam | N/A |
| OsuChannelLeave | `str` (Channel Name) |
| OsuReceiveUpdates | `chio.PresenceFilter` |
| BanchoMonitor | N/A |
| BanchoMatchPlayerSkipped | `int` (SlotId) |
| OsuSetIrcAwayMessage | `chio.Message` |
| BanchoUserPresence | `chio.UserInfo` |
| OsuUserStatsRequest | `list[int]` |
| BanchoRestart | `int` (Retry After Milliseconds) |
| OsuInvite | `int` (UserId) |
| BanchoInvite | `chio.Message` |
| OsuMatchChangePassword | `chio.Match` |
| BanchoMatchChangePassword | `str` (New Password) |
| BanchoSilenceInfo | `int` (Locked Until Seconds) |
| OsuTournamentMatchInfo | `int` (MatchId) |
| BanchoUserSilenced | `int` (UserId) |
| BanchoUserPresenceSingle | `int` (UserId) |
| BanchoUserPresenceBundle | `list[int]` (UserIDs) |
| OsuPresenceRequest | `list[int]` (UserIDs) |
| OsuPresenceRequestAll | N/A |
| OsuChangeFriendOnlyDms | `bool` (Enabled/Disabled) |
| BanchoUserDmsBlocked | `chio.Message` |
| BanchoTargetIsSilenced | `chio.Message` |
| BanchoVersionUpdateForced | N/A |
| BanchoSwitchServer | `int` (After Idle Time) |
| BanchoAccountRestricted | N/A |
| BanchoRTX | `str` (Message) |
| BanchoMatchAbort | N/A |
| BanchoSwitchTournamentServer | `str` (Server) |
| OsuTournamentJoinMatchChannel | `int` (MatchId) |
| OsuTournamentLeaveMatchChannel | `int` (MatchId) |
Raw data
{
"_id": null,
"home_page": null,
"name": "chio",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": "osu, osugame, python, bancho",
"author": "Lekuru",
"author_email": "contact@lekuru.xyz",
"download_url": "https://files.pythonhosted.org/packages/8f/f6/32eea7aaa6fd835403235c1540ddda386662186bf015ef5d2f854f0d61c5/chio-1.1.20.tar.gz",
"platform": null,
"description": "\n# Chio\n\n\n\n\n\n**Chio (Bancho I/O)** is a python library for serializing and deserializing bancho packets, with support for all versions of osu! that use bancho (2008-2025).\n\nIt was made with the intention of documenting everything about the bancho protocol, and to provide a base for server frameworks, since the packet handling part is most often the annoying part. \nHaving *any* client be able to connect to it is a very sweet addition on top, if you are interested in those as well.\n\n**If you wish to use this library, I would appreciate some credit for my work. Thanks!**\n\n## Usage\n\nThis library requires an installation of python **3.8** or higher. \nYou can install the library with pip:\n\n```shell\npip install chio\n```\n\nOr you can also install it from source directly, if preferred:\n\n```shell\npip install git+https://github.com/Lekuruu/chio.py\n```\n\nHere is a very basic example of how to use this library, and how to get a client to log in:\n\n```python\nimport chio\n\n# Chio expects you to have the `chio.Stream` class\n# implemented, i.e. it needs a `read()` and `write()`\n# function to work properly\nstream = chio.Stream()\n\n# The client version is how chio determines what\n# protocol to use. This one can be parsed through the\n# initial login request, that the client makes.\nclient_version = 20140127\n\n# Chio has combined the user presence, stats and status\n# into one class, to support more clients. You are also\n# able to provide your own player class, as long as you\n# have the same fields added on to it.\ninfo = chio.UserInfo(\n id=2,\n name=\"peppy\",\n presence=chio.UserPresence(),\n stats=chio.UserStats(),\n status=chio.UserStatus()\n)\n\n# Select a client protocol to use for encoding/decoding\nio = chio.select_client(client_version)\n\n# Send the users information (userId, presence & stats)\nio.write_packet(stream, chio.PacketType.BanchoLoginReply, info.id)\nio.write_packet(stream, chio.PacketType.BanchoUserPresence, info)\nio.write_packet(stream, chio.PacketType.BanchoUserStats, info)\n\n# Force client to join #osu\nio.write_packet(stream, chio.PacketType.BanchoChannelJoinSuccess, \"#osu\")\n\n# Send a message in #osu from BanchoBot\nio.write_packet(\n stream,\n chio.PacketType.BanchoMessage,\n chio.Message(content=\"Hello, World!\", sender=\"BanchoBot\", target=\"#osu\")\n)\n\npacket, data = io.read_packet(stream)\nprint(f\"Received packet '{packet.name}' with {data}.\")\n```\n\nYou can also read & write from bytes directly, for example when using HTTP clients instead of TCP clients:\n\n```python\nencoded = io.write_packet_to_bytes(chio.PacketType.BanchoLoginReply, info.id)\npacket, data = io.read_packet_from_bytes(b\"...\")\n```\n\nIf you are using **asyncio**, you may want to use the `read_packet_async` & `write_packet_async` functions respectively for asynchronous usage.\nThis feature is currently untested, but should work in theory. If you encounter any bugs with it, don't be afraid to report them.\n\n```python\nencoded = await io.write_packet_async(stream, chio.PacketType.BanchoLoginReply, info.id)\npacket, data = await io.read_packet_async(stream)\n```\n\n### Patching\n\nYou are able to overwrite specifc packet readers/writers, with the `chio.patch` decorator.\nAs an example, to patch the `BanchoUserStats` packet inside of `b20120723`:\n\n```python\n@chio.patch(PacketType.BanchoUserStats, 20120723)\ndef write_user_stats(cls, info: UserInfo):\n stream = MemoryStream()\n write_s32(stream, info.id)\n stream.write(cls.write_status_update(info.status))\n write_u64(stream, info.stats.rscore)\n write_f32(stream, info.stats.accuracy)\n write_u32(stream, info.stats.playcount)\n write_u64(stream, info.stats.tscore)\n write_u32(stream, info.stats.rank)\n write_u16(stream, info.stats.pp)\n yield PacketType.BanchoUserStats, stream.data\n```\n\nAdditionally, it's possible to set a certain slot size & protocol version for each version:\n\n```python\n# Set protocol version to 10 for b20120818\nchio.set_protocol_version(10, 20120818)\n\n# Override slot size to 32 for b20160404\nchio.set_slot_size(32, 20160404)\n```\n\n### Datatypes\n\nDepending on the packet you send or receive, you will need to account for different datatypes. \nHere is a list of them for each packet:\n\n| Packet | Type |\n|:------------------------------:|:-----------------------------------:|\n| OsuUserStatus | `chio.UserStatus` |\n| OsuMessage | `chio.Message` |\n| OsuExit | `bool` (IsUpdating) |\n| OsuStatusUpdateRequest | N/A |\n| OsuPong | N/A |\n| BanchoLoginReply | `int` (UserId) or `chio.LoginError` |\n| BanchoMessage | `chio.Message` |\n| BanchoPing | N/A |\n| BanchoIrcChangeUsername | `str` (old name), `str` (new name) |\n| BanchoIrcQuit | `str` (Username) |\n| BanchoUserStats | `chio.UserInfo` |\n| BanchoUserQuit | `chio.UserQuit` |\n| BanchoSpectatorJoined | `int` (UserId) |\n| BanchoSpectatorLeft | `int` (UserId) |\n| BanchoSpectateFrames | `chio.ReplayFrameBundle` |\n| OsuStartSpectating | `int` (UserId) |\n| OsuStopSpectating | `int` (UserId) |\n| OsuSpectateFrames | `chio.ReplayFrameBundle` |\n| BanchoVersionUpdate | N/A |\n| OsuErrorReport | `str` (Exception) |\n| OsuCantSpectate | N/A |\n| BanchoSpectatorCantSpectate | `int` (UserId) |\n| BanchoGetAttention | N/A |\n| BanchoAnnounce | `str` (Message) |\n| OsuPrivateMessage | `chio.Message` |\n| BanchoMatchUpdate | `chio.Match` |\n| BanchoMatchNew | `chio.Match` |\n| BanchoMatchDisband | `int` (MatchId) |\n| OsuLobbyPart | N/A |\n| OsuLobbyJoin | N/A |\n| OsuMatchCreate | `chio.Match` |\n| OsuMatchJoin | `int` (MatchId) |\n| OsuMatchPart | N/A |\n| BanchoLobbyJoin | `int` (UserId) |\n| BanchoLobbyPart | `int` (UserId) |\n| BanchoMatchJoinSuccess | `chio.Match` |\n| BanchoMatchJoinFail | N/A |\n| OsuMatchChangeSlot | `int` (SlotId) |\n| OsuMatchReady | N/A |\n| OsuMatchLock | `int` (SlotId) |\n| OsuMatchChangeSettings | `chio.Match` |\n| BanchoFellowSpectatorJoined | `int` (UserId) |\n| BanchoFellowSpectatorLeft | `int` (UserId) |\n| OsuMatchStart | N/A |\n| BanchoMatchStart | `chio.Match` |\n| OsuMatchScoreUpdate | `chio.ScoreFrame` |\n| BanchoMatchScoreUpdate | `chio.ScoreFrame` |\n| OsuMatchComplete | N/A |\n| BanchoMatchTransferHost | N/A |\n| OsuMatchChangeMods | `chio.Mods` |\n| OsuMatchLoadComplete | N/A |\n| BanchoMatchAllPlayersLoaded | N/A |\n| OsuMatchNoBeatmap | N/A |\n| OsuMatchNotReady | N/A |\n| OsuMatchFailed | N/A |\n| BanchoMatchPlayerFailed | `int` (SlotId) |\n| BanchoMatchComplete | N/A |\n| OsuMatchHasBeatmap | N/A |\n| OsuMatchSkipRequest | N/A |\n| BanchoMatchSkip | N/A |\n| OsuChannelJoin | `str` (Channel Name) |\n| BanchoChannelJoinSuccess | `str` (Channel Name) |\n| BanchoChannelAvailable | `chio.Channel` |\n| BanchoChannelRevoked | `str` (Channel Name) |\n| BanchoChannelAvailableAutojoin | `chio.Channel` |\n| OsuBeatmapInfoRequest | `chio.BeatmapInfoRequest` |\n| BanchoBeatmapInfoReply | `chio.BeatmapInfoReply` |\n| OsuMatchTransferHost | `int` (SlotId) |\n| BanchoLoginPermissions | `chio.Permissions` or `int` |\n| BanchoFriendsList | `list[int]` |\n| OsuFriendsAdd | `int` (UserId) |\n| OsuFriendsRemove | `int` (UserId) |\n| BanchoProtocolNegotiation | N/A or `int` |\n| BanchoTitleUpdate | `chio.TitleUpdate` |\n| OsuMatchChangeTeam | N/A |\n| OsuChannelLeave | `str` (Channel Name) |\n| OsuReceiveUpdates | `chio.PresenceFilter` |\n| BanchoMonitor | N/A |\n| BanchoMatchPlayerSkipped | `int` (SlotId) |\n| OsuSetIrcAwayMessage | `chio.Message` |\n| BanchoUserPresence | `chio.UserInfo` |\n| OsuUserStatsRequest | `list[int]` |\n| BanchoRestart | `int` (Retry After Milliseconds) |\n| OsuInvite | `int` (UserId) |\n| BanchoInvite | `chio.Message` |\n| OsuMatchChangePassword | `chio.Match` |\n| BanchoMatchChangePassword | `str` (New Password) |\n| BanchoSilenceInfo | `int` (Locked Until Seconds) |\n| OsuTournamentMatchInfo | `int` (MatchId) |\n| BanchoUserSilenced | `int` (UserId) |\n| BanchoUserPresenceSingle | `int` (UserId) |\n| BanchoUserPresenceBundle | `list[int]` (UserIDs) |\n| OsuPresenceRequest | `list[int]` (UserIDs) |\n| OsuPresenceRequestAll | N/A |\n| OsuChangeFriendOnlyDms | `bool` (Enabled/Disabled) |\n| BanchoUserDmsBlocked | `chio.Message` |\n| BanchoTargetIsSilenced | `chio.Message` |\n| BanchoVersionUpdateForced | N/A |\n| BanchoSwitchServer | `int` (After Idle Time) |\n| BanchoAccountRestricted | N/A |\n| BanchoRTX | `str` (Message) |\n| BanchoMatchAbort | N/A |\n| BanchoSwitchTournamentServer | `str` (Server) |\n| OsuTournamentJoinMatchChannel | `int` (MatchId) |\n| OsuTournamentLeaveMatchChannel | `int` (MatchId) |\n",
"bugtrack_url": null,
"license": null,
"summary": "A python library for serializing and deserializing bancho packets.",
"version": "1.1.20",
"project_urls": null,
"split_keywords": [
"osu",
" osugame",
" python",
" bancho"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "68ce46ff179cb7e4ebe35e6420e32f15eb01a954c8b28e6a81abb3af7253af9d",
"md5": "36fbbfe0fa433bce3d1886d20df0201f",
"sha256": "c7aa42cc9a137001bd34372279cc0305e30dc390961a1a8c86705d7a36091d77"
},
"downloads": -1,
"filename": "chio-1.1.20-py3-none-any.whl",
"has_sig": false,
"md5_digest": "36fbbfe0fa433bce3d1886d20df0201f",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 69957,
"upload_time": "2025-08-08T00:46:09",
"upload_time_iso_8601": "2025-08-08T00:46:09.296309Z",
"url": "https://files.pythonhosted.org/packages/68/ce/46ff179cb7e4ebe35e6420e32f15eb01a954c8b28e6a81abb3af7253af9d/chio-1.1.20-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "8ff632eea7aaa6fd835403235c1540ddda386662186bf015ef5d2f854f0d61c5",
"md5": "5f0f7706a115c52461ece68d1a77f814",
"sha256": "33f682d278d78e9eb8d87f7ad87f9d378c5d96307a718036069a50b452604e61"
},
"downloads": -1,
"filename": "chio-1.1.20.tar.gz",
"has_sig": false,
"md5_digest": "5f0f7706a115c52461ece68d1a77f814",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 40050,
"upload_time": "2025-08-08T00:46:10",
"upload_time_iso_8601": "2025-08-08T00:46:10.492709Z",
"url": "https://files.pythonhosted.org/packages/8f/f6/32eea7aaa6fd835403235c1540ddda386662186bf015ef5d2f854f0d61c5/chio-1.1.20.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-08 00:46:10",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "chio"
}