### Create and Initialize F-Chat Client with Custom Listener Source: https://context7.com/feathrs/f-chat-rs/llms.txt Use ClientBuilder to configure and initialize an F-Chat client with a custom event listener. This example demonstrates setting client identification and authenticating with F-List servers. ```rust use f_chat_rs::client::{ClientBuilder, EventListener, async_trait}; use f_chat_rs::session::Session; use f_chat_rs::data::Character; use std::sync::Arc; // Define a custom event listener struct MyEventListener; #[async_trait] impl EventListener for MyEventListener { async fn ready(&self, ctx: Arc) { println!("Session ready for character: {:?}", ctx.character); } async fn message( &self, ctx: Arc, channel: f_chat_rs::data::MessageChannel, character: f_chat_rs::data::Character, message: f_chat_rs::data::MessageContent, ) { println!("Message from {:?}: {:?}", character, message); } } #[tokio::main] async fn main() -> Result<(), Box> { // Create a client with custom event listener let (client, receiver) = ClientBuilder::new(MyEventListener) .with_version("MyApp".to_string(), "1.0.0".to_string()) .init("your_username".to_string(), "your_password".to_string()) .await?; // Characters available after authentication println!("Available characters: {:?}", client.own_characters); // Start the event loop in a separate task tokio::spawn(async move { client.start(receiver).await; }); Ok(()) } ``` -------------------------------- ### Get API Ticket with Extra Data Source: https://context7.com/feathrs/f-chat-rs/llms.txt Authenticates with F-List to obtain an API ticket. Set the `include_extra_data` flag to true to also retrieve the character list, friends, and bookmarks. ```rust use f_chat_rs::http_endpoints::get_api_ticket; use reqwest::Client; #[tokio::main] async fn main() -> Result<(), Box> { let http_client = Client::new(); // Get ticket with extra data (characters, friends, bookmarks) let response = get_api_ticket( &http_client, "your_username", "your_password", true // Include extra data ).await?; println!("API Ticket: {}", response.ticket); if let Some(extra) = response.extra { println!("Your characters:"); for (character, id) in &extra.characters { println!(" - {:?} (ID: {:?})", character, id); } println!("Friends: {:?}", extra.friends.len()); println!("Bookmarks: {:?}", extra.bookmarks.len()); } Ok(()) } ``` -------------------------------- ### Get Character Profile Data Source: https://context7.com/feathrs/f-chat-rs/llms.txt Retrieves detailed profile information for a specified character using an API ticket obtained from `get_api_ticket`. This includes kinks, images, custom fields, and settings. ```rust use f_chat_rs::http_endpoints::{get_character_profile_data, get_api_ticket}; use f_chat_rs::data::Character; use f_chat_rs::util::StackString; use reqwest::Client; #[tokio::main] async fn main() -> Result<(), Box> { let http = Client::new(); // First authenticate let auth = get_api_ticket(&http, "username", "password", false).await?; // Fetch character profile let character = Character(StackString::new("SomeCharacter")); let profile = get_character_profile_data( &http, &auth.ticket, "username", character ).await?; println!("Character: {}", profile.inner.name); println!("Description: {}", profile.inner.description); println!("Views: {}", profile.inner.views); println!("Images: {}", profile.inner.images.len()); println!("Custom kinks: {}", profile.inner.custom_kinks.len()); // Check for API errors if !profile.error.is_empty() { eprintln!("API Error: {}", profile.error); } Ok(()) } ``` -------------------------------- ### Constructing and Parsing F-Chat Protocol Commands Source: https://context7.com/feathrs/f-chat-rs/llms.txt Demonstrates how to create client-side commands, serialize them for transmission, and parse incoming server messages using the protocol module. ```rust use f_chat_rs::protocol::{ ClientCommand, ServerCommand, Target, IdentifyMethod, parse_command, prepare_command }; use f_chat_rs::data::{Channel, Character, Status, TypingStatus}; use f_chat_rs::util::StackString; fn protocol_examples() { // Client commands (sent to server) let join = ClientCommand::JoinChannel { channel: Channel(StackString::new("Fantasy")) }; let message = ClientCommand::Message { channel: Channel(StackString::new("Fantasy")), message: "Hello world!".to_string() }; let pm = ClientCommand::PrivateMessage { recipient: Character(StackString::new("Friend")), message: "Hey there!".to_string() }; let status = ClientCommand::Status { status: Status::Looking, statusmsg: "Open for RP!".to_string() }; let typing = ClientCommand::Typing { character: Character(StackString::new("Friend")), status: TypingStatus::Typing }; // Serialize command for sending let wire_format = prepare_command(&join); println!("Wire format: {}", wire_format); // Output: JCH {"channel":"Fantasy"} // Parse incoming server message let raw_message = r#"MSG {"character":"SomeUser","message":"Hello!","channel":"Fantasy"}"#; let parsed = parse_command(raw_message); match parsed { ServerCommand::Message { character, message, channel } => { println!("Message in {:?} from {:?}: {}", channel, character, message); } ServerCommand::PrivateMessage { character, message } => { println!("PM from {:?}: {}", character, message); } ServerCommand::Error { number, message } => { println!("Error {}: {}", number, message); } _ => {} } } ``` -------------------------------- ### Initialize F-Chat Data Types in Rust Source: https://context7.com/feathrs/f-chat-rs/llms.txt Demonstrates instantiation of core data structures including characters, channels, statuses, and profile attributes using stack-allocated strings. ```rust use f_chat_rs::data::{ Character, Channel, Status, Gender, Orientation, FurryPreference, Role, ChannelMode, TypingStatus, MessageChannel, MessageContent, FriendRelation }; use f_chat_rs::util::StackString; fn data_type_examples() { // Characters and Channels use stack-allocated strings for efficiency let character = Character(StackString::new("MyCharacter")); let channel = Channel(StackString::new("ADH-abc123")); // Status options let statuses = [ Status::Online, Status::Looking, // Looking for RP Status::Busy, Status::Dnd, // Do Not Disturb Status::Away, Status::Idle, ]; // Character profile attributes let genders = [Gender::Male, Gender::Female, Gender::Herm, Gender::None]; let orientations = [Orientation::Straight, Orientation::Gay, Orientation::Bisexual]; let furry_prefs = [FurryPreference::Both, FurryPreference::FurryOnly, FurryPreference::HumanOnly]; let roles = [Role::Switch, Role::AlwaysDom, Role::AlwaysSub]; // Channel modes control what can be posted let modes = [ ChannelMode::Both, // Chat and ads allowed ChannelMode::ChatOnly, // Only chat messages ChannelMode::AdsOnly, // Only LFRP ads ]; // Typing indicators for PMs let typing_states = [ TypingStatus::Clear, // Not typing TypingStatus::Typing, // Currently typing TypingStatus::Paused, // Started but paused ]; // Message channels unify channel and PM contexts let channel_msg = MessageChannel::Channel(channel); let pm_msg = MessageChannel::PrivateMessage( Character(StackString::new("Me")), Character(StackString::new("Them")) ); // Friend relationships track both sides let friendship = FriendRelation { own_character: Character(StackString::new("MyChar")), other_character: Character(StackString::new("TheirChar")), }; } ``` -------------------------------- ### Manage Friends and Bookmarks via HTTP API Source: https://context7.com/feathrs/f-chat-rs/llms.txt Demonstrates using the HTTP endpoints to list friends, manage bookmarks, and handle friend requests using the reqwest client. ```rust use f_chat_rs::http_endpoints::{ get_friends_list, add_bookmark, remove_bookmark, send_friend_request, accept_friend_request, remove_friend }; use f_chat_rs::data::Character; use f_chat_rs::util::StackString; use reqwest::Client; #[tokio::main] async fn main() -> Result<(), Box> { let http = Client::new(); let ticket = "your_api_ticket"; let account = "your_username"; // Get full friends list including pending requests let friends = get_friends_list(&http, ticket, account).await?; println!("Friends: {}", friends.inner.friends.len()); for friend in &friends.inner.friends { println!(" {:?} <-> {:?}", friend.source, friend.dest); } println!("Bookmarks: {:?}", friends.inner.bookmarks); println!("Pending incoming: {}", friends.inner.pending_incoming.len()); println!("Pending outgoing: {}", friends.inner.pending_outgoing.len()); // Add a bookmark let character = Character(StackString::new("FavoriteCharacter")); add_bookmark(&http, ticket, account, character).await?; // Send a friend request let my_char = Character(StackString::new("MyCharacter")); let their_char = Character(StackString::new("TheirCharacter")); let request = send_friend_request(&http, ticket, account, my_char, their_char).await?; println!("Request ID: {}", request.inner.request.id); // Accept an incoming friend request accept_friend_request(&http, ticket, account, 12345).await?; Ok(()) } ``` -------------------------------- ### Establish WebSocket Session with F-Chat Source: https://context7.com/feathrs/f-chat-rs/llms.txt Use the `connect` method on an initialized client to establish a WebSocket session for a specific character. The client handles API ticket refresh automatically. ```rust use f_chat_rs::client::{Client, ClientBuilder, EventListener, async_trait}; use f_chat_rs::data::Character; use f_chat_rs::util::StackString; #[tokio::main] async fn main() -> Result<(), Box> { struct MyListener; #[async_trait] impl EventListener for MyListener { async fn ready(&self, ctx: std::sync::Arc) { println!("Connected as: {:?}", ctx.character); } } let (client, receiver) = ClientBuilder::new(MyListener) .init("username".to_string(), "password".to_string()) .await?; // Connect with a specific character let character = Character(StackString::new("MyCharacterName")); client.connect(character).await?; // Start event processing client.start(receiver).await; Ok(()) } ``` -------------------------------- ### Implement Custom Cache Trait Source: https://context7.com/feathrs/f-chat-rs/llms.txt Shows how to implement the Cache trait for custom storage backends, using an in-memory HashMap protected by RwLock. ```rust use f_chat_rs::cache::{Cache, PartialChannelData, PartialUserData, NoCacheError}; use f_chat_rs::data::{ Channel, Character, ChannelData, CharacterData, FriendRelation, Message, MessageChannel }; use f_chat_rs::util::timestamp::Timestamp; use std::borrow::Cow; use std::collections::HashMap; use std::sync::RwLock; // Example in-memory cache implementation struct MemoryCache { characters: RwLock>, channels: RwLock>, messages: RwLock>, } impl Cache for MemoryCache { type Error = NoCacheError; fn update_character( &self, character: Cow, data: PartialUserData, ) -> Result { let mut chars = self.characters.write().unwrap(); let entry = chars.entry(*character).or_default(); let mut changed = false; if let Some(status) = data.status { if entry.status != status { entry.status = status; changed = true; } } if let Some(gender) = data.gender { if entry.gender != gender { entry.gender = gender; changed = true; } } Ok(changed) // Return true if data changed (triggers event) } fn get_character(&self, character: &Character) -> Result, Self::Error> { Ok(self.characters.read().unwrap().get(character).cloned()) } // Implement remaining trait methods... fn insert_message(&self, _source: MessageChannel, message: Message) -> Result { self.messages.write().unwrap().push(message); Ok(true) } // ... other required methods } ``` -------------------------------- ### Post Roleplay Advertisements Source: https://context7.com/feathrs/f-chat-rs/llms.txt Posts an LFRP advertisement to a channel. Be aware of server-enforced cooldowns and character limits. ```rust use f_chat_rs::session::Session; use f_chat_rs::data::Channel; use f_chat_rs::util::StackString; use std::sync::Arc; async fn post_advertisement(session: Arc) -> Result<(), Box> { let channel = Channel(StackString::new("Fantasy")); let ad_text = r#"[b]Looking for adventure partners![/b] Experienced knight seeking companions for dungeon exploration. [i]Long-term RP preferred. PM friendly![/i]"#; session.send_ad(channel, ad_text.to_string()).await?; // Check ad cooldown from session variables println!("Ad cooldown: {} seconds", session.variables.ad_cooldown); println!("Max ad length: {} chars", session.variables.ad_max); Ok(()) } ``` -------------------------------- ### Join Chat Channels Source: https://context7.com/feathrs/f-chat-rs/llms.txt Sends a request to join official or private channels. Private channels must be prefixed with 'ADH-'. ```rust use f_chat_rs::session::Session; use f_chat_rs::data::Channel; use f_chat_rs::util::StackString; use std::sync::Arc; async fn join_channels(session: Arc) -> Result<(), Box> { // Join an official channel let official = Channel(StackString::new("Fantasy")); session.join_channel(official).await?; // Join an unofficial/private channel let unofficial = Channel(StackString::new("ADH-731af796f59d06bec167")); session.join_channel(unofficial).await?; Ok(()) } ``` -------------------------------- ### Handling F-Chat Protocol Errors Source: https://context7.com/feathrs/f-chat-rs/llms.txt Shows how to convert raw error codes into ProtocolError enums and differentiate between fatal and non-fatal error states. ```rust use f_chat_rs::protocol::ProtocolError; fn handle_error(error_code: i32, message: String) { let error: ProtocolError = error_code.into(); // Check if error is fatal (should not reconnect) if error.is_fatal() { match error { ProtocolError::Banned => { eprintln!("Account is banned from the server"); } ProtocolError::TooManySessions => { eprintln!("Too many concurrent connections"); } ProtocolError::Kick => { eprintln!("Kicked from server: {}", message); } ProtocolError::TimeOut => { eprintln!("Timed out: {}", message); } _ => eprintln!("Fatal error: {:?}", error) } return; } // Handle non-fatal errors match error { ProtocolError::MessageCooldown => { println!("Sending messages too fast, waiting..."); } ProtocolError::MessageTooLong => { println!("Message exceeds maximum length"); } ProtocolError::AlreadyInChannel => { println!("Already in this channel"); } ProtocolError::NoSuchChannel => { println!("Channel does not exist"); } ProtocolError::ChannelBanned => { println!("Banned from this channel"); } ProtocolError::Ignored => { // This error includes who is ignoring you println!("Being ignored: {}", message); } _ => { if error.has_message() { println!("Error {:?}: {}", error, message); } else { println!("Error: {:?}", error); } } } } ``` -------------------------------- ### Send Messages to Channels or Characters Source: https://context7.com/feathrs/f-chat-rs/llms.txt Utilize the `send_message` method on a `Session` to send text messages. This can be directed to a specific channel or a private message to another character. ```rust use f_chat_rs::session::Session; use f_chat_rs::protocol::Target; use f_chat_rs::data::{Channel, Character}; use f_chat_rs::util::StackString; use std::sync::Arc; async fn send_messages(session: Arc) -> Result<(), Box> { // Send a message to a channel let channel = Channel(StackString::new("ADH-abc123def456")); session.send_message( Target::Channel { channel }, "Hello, channel!".to_string() ).await?; // Send a private message to a character let recipient = Character(StackString::new("SomeCharacter")); session.send_message( Target::Character { recipient }, "Hello, friend!".to_string() ).await?; Ok(()) } ``` -------------------------------- ### Implement EventListener Trait Source: https://context7.com/feathrs/f-chat-rs/llms.txt Implement the EventListener trait to handle various F-Chat events asynchronously. All methods have default no-op implementations, so you only need to override the ones you require. ```rust use f_chat_rs::client::{EventListener, async_trait}; use f_chat_rs::session::{Session, SessionError}; use f_chat_rs::protocol::{ServerCommand, ProtocolError}; use f_chat_rs::data::{Channel, Character, MessageChannel, MessageContent, TypingStatus}; use std::sync::Arc; struct ChatBot; #[async_trait] impl EventListener for ChatBot { // Called when session is fully connected and ready async fn ready(&self, ctx: Arc) { println!("Bot connected as: {:?}", ctx.character); } // Called for every incoming message (channel or PM) async fn message( &self, ctx: Arc, channel: MessageChannel, character: Character, message: MessageContent, ) { match message { MessageContent::Message(text) => { println!("[{:?}] {:?}: {}", channel, character, text); } MessageContent::Roll(dice, results, total) => { println!("{:?} rolled {:?} = {}", character, dice, total); } _ => {} } } // Called when someone starts/stops typing in PM async fn typing(&self, ctx: Arc, character: Character, status: TypingStatus) { match status { TypingStatus::Typing => println!("{:?} is typing...", character), TypingStatus::Paused => println!("{:?} paused typing", character), TypingStatus::Clear => println!("{:?} stopped typing", character), } } // Called when channel data is updated async fn updated_channel(&self, channel: Channel) { println!("Channel updated: {:?}", channel); } // Called on protocol errors async fn error(&self, ctx: Arc, err: ProtocolError, message: String) { if err.is_fatal() { eprintln!("Fatal error: {:?} - {}", err, message); } else { eprintln!("Error: {:?} - {}", err, message); } } // Called when session disconnects async fn session_disconnected(&self, ctx: Arc, error: ProtocolError) { eprintln!("Disconnected: {:?}", error); } } ``` -------------------------------- ### Roll Dice Source: https://context7.com/feathrs/f-chat-rs/llms.txt Sends dice roll commands to channels or private messages using standard dice notation. ```rust use f_chat_rs::session::Session; use f_chat_rs::protocol::Target; use f_chat_rs::data::{Channel, Character}; use f_chat_rs::util::StackString; use std::sync::Arc; async fn roll_dice(session: Arc) -> Result<(), Box> { // Roll dice in a channel let channel = Channel(StackString::new("Tabletop Gaming")); session.send_dice( Target::Channel { channel }, "1d20+5".to_string() // Roll a d20 with +5 modifier ).await?; // Roll dice in a private message let recipient = Character(StackString::new("DungeonMaster")); session.send_dice( Target::Character { recipient }, "4d6".to_string() // Roll 4d6 ).await?; Ok(()) } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.