| Crates.io | oshatori |
| lib.rs | oshatori |
| version | 0.2.0 |
| created_at | 2026-01-08 23:42:17.632551+00 |
| updated_at | 2026-01-08 23:42:17.632551+00 |
| description | Multi-connection, protocol-agnostic, instant messaging. |
| homepage | |
| repository | |
| max_upload_size | |
| id | 2031312 |
| size | 163,891 |
A versatile multi-protocol chat library
[!WARNING] This library is in its early stages and the core structure will evolve as more protocols are implemented. If you're still considering contributing at this point in time, make sure to look out for a more stable release of oshatori before sending a pull request.
[!WARNING] README is currently out of date.
This project aims to generalize a broad range of text chat and social interactions. To start off let's go over the types provided by the library:
| Name | Kind | Fields / Variants | Description |
|---|---|---|---|
| Account | struct |
auth: Vec<AuthField>protocol_name: Stringprivate_profile: Option<Profile> |
Represents a user's account on a protocol, with auth fields and an optional private profile. |
| Profile | struct |
id: Option<String>username: Option<String>display_name: Option<String>color: Option<[u8;4]>picture: Option<String> |
Holds display info for a user (defaults all to None). |
| Message | struct |
id: Option<String>sender_id: Option<String>content: Vec<MessageFragment>timestamp: DateTime<Utc>message_type: MessageTypestatus: MessageStatus |
Encapsulates a single chat message with fragments, timestamp, type, and delivery status. |
| MessageStatus | enum |
SentDeliveredEditedDeletedFailed |
Tracks the state of a message. |
| MessageType | enum |
CurrentUserNormalServerMeta |
Categorizes if a message was sent by the current user, another user, the server, or internally by the protocol implementation itself. |
| MessageFragment | enum |
Text(String)Image { url: String, mime: String }Video { url: String, mime: String }Audio { url: String, mime: String }Url(String) |
A piece of a message: plaintext, media embed, or URL. |
| Channel | struct |
id: Stringname: Option<String>channel_type: ChannelType |
Represents a chat channel (group, direct, or broadcast). |
| ChannelType | enum |
GroupDirectBroadcast |
Defines the type of channel (multi-user, peer-to-peer, or broadcast-only). |
| Asset | enum |
Emote, Sticker, Audio { id: Option Command {id: Option |
An asset available for use by the user. |
| AssetSource | enum |
User, Server, Meta | Categorizes if the asset was added by the user, the protocol itself, or a connected server. |
| Protocol | struct |
name: Stringauth: Option<Vec<AuthField>> |
Describes a messaging protocol with its auth fields (or None if no authentication is needed. |
| AuthField | struct |
name: Stringdisplay: Option<String>value: FieldValuerequired: bool |
One input field needed for authentication (e.g. username, password). |
| FieldValue | enum |
Text(Option<String>)Password(Option<String>)Group(Vec<AuthField>) |
The type and current value of an AuthField: plain text, password, or nested group of fields. |
Common interface trait called Connection:
pub trait Connection: Send + Sync {
async fn connect(&mut self, auth: Vec<AuthField>) -> Result<(), String>;
async fn disconnect(&mut self) -> Result<(), String>;
async fn send(&mut self, event: ConnectionEvent) -> Result<(), String>;
fn subscribe(&self) -> broadcast::Receiver<ConnectionEvent>;
fn protocol_spec() -> Protocol;
}
Every implemented protocol can be interacted with using this interface, and the same set of events:
| Type | Variant | Fields |
|---|---|---|
| ChatEvent | New |
channel_id: Option<String>, message: Message |
Update |
channel_id: Option<String>, message_id: String, new_message: Message |
|
Remove |
channel_id: Option<String>, message_id: String |
|
| ChannelEvent | New |
channel: Channel |
Update |
channel_id: String, new_channel: Channel |
|
Remove |
channel_id: String |
|
Join |
channel_id: String |
|
Leave |
channel_id: String |
|
Switch |
channel_id: String |
|
Kick |
channel_id: Option<String>, reason: Option<String>, ban: bool |
|
Wipe |
channel_id: Option<String> |
|
ClearList |
(no fields) | |
| UserEvent | New |
channel_id: Option<String>, user: Profile |
Update |
channel_id: Option<String>, user_id: String, new_user: Profile |
|
Remove |
channel_id: Option<String>, user_id: String |
|
ClearList |
channel_id: Option<String> |
|
| StatusEvent | Ping |
artifact: Option<String> |
Connected |
artifact: Option<String> |
|
Disconnected |
artifact: Option<String> |
|
| AssetEvent | New |
channel_id: Option<String>, asset: Asset |
Update |
channel_id: Option<String>, asset_id: String, new_asset: Asset |
|
Remove |
channel_id: Option<String>, asset_id: String |
|
ClearList |
channel_id: Option<String> |
|
| ConnectionEvent | Chat |
event: ChatEvent |
User |
event: UserEvent |
|
Channel |
event: ChannelEvent |
|
Status |
event: StatusEvent |
|
Asset |
event: AssetEvent |
Here is an example straight from the mock_connection.rs test:
use chrono::Utc;
use oshatori::{
connection::{ChatEvent, ConnectionEvent, MockConnection},
Connection, Message, MessageFragment, MessageStatus, MessageType,
};
#[tokio::test]
async fn test_mock_connection_integration() {
let mut conn = MockConnection::new();
let mut rx = conn.subscribe();
let test_message = Message {
id: None,
sender_id: None,
content: vec![MessageFragment::Text("some text".to_string())],
timestamp: Utc::now(),
message_type: MessageType::Normal,
status: MessageStatus::Sent,
};
conn.send(ConnectionEvent::Chat {
event: ChatEvent::New {
channel_id: None,
message: test_message.clone(),
},
})
.await
.expect("failed to send");
let received = rx.recv().await.expect("failed to receive");
if let ConnectionEvent::Chat { event } = received {
if let ChatEvent::New {
channel_id,
message,
} = event
{
assert_eq!(channel_id, None);
match message.content.get(0) {
Some(fragment) => match fragment {
MessageFragment::Text(value) => {
assert_eq!(value.to_owned(), "some text".to_string())
}
_ => {}
},
None => {}
}
} else {
panic!("unexpected chat event");
}
} else {
panic!("unexpected connection event");
}
}
Currently these protocols are implemented:
The folder structure is used as follows:
src
connection - protocol implementations
mod.rs - Connection trait definitionsockchat.rsmock.rsutils - helper functions used by multiple protocols
bbcode.rs - bbcode parsercolor.rs - kanii_to_rgbahtml.rs - replacing <, >, and \s<br/>\s with <, >, and \nmod.rslib.rs - type definitionstests - tests for each protocol
mock_connection.rssockchat_connection