System Online

Banter Documentation_

C# Console Application Project Documentation. A modern, terminal-based chat application merging CLI nostalgia with real-time cloud power.

01. Project Overview

Purpose, Features, and Technology Stack

Purpose

Banter is a real-time, cloud-powered chat application built entirely for the terminal environment. The project aims to demonstrate modern C# programming techniques, including asynchronous programming, real-time data synchronization with Firebase Firestore, and advanced terminal UI development using Terminal.Gui. It bridges the gap between nostalgic command-line interfaces and contemporary cloud technology, proving that powerful applications don't always need graphical interfaces.

Features

User Authentication

Secure login system with username/password validation and account creation workflow

Real-Time Messaging

Instant message delivery using Firestore real-time listeners with automatic UI updates

Chatroom Management

Create individual or group chats, manage participants, and assign admin privileges

Message Pinning

Pin important messages for quick reference with dedicated pinned message viewer

Search Functionality

Search through chatrooms and message history with real-time filtering

Profanity Filter

Advanced profanity detection and censoring supporting multiple languages

Custom Color Schemes

Cyberpunk-inspired terminal aesthetics with customizable UI elements

Local Caching

LiteDB integration for offline data persistence and reduced API calls

Target Audience

Primary Users

Developers, system administrators, and CLI enthusiasts who appreciate efficient text-based interfaces and want to avoid the overhead of traditional GUI applications.

Secondary Users

Computer science students learning about asynchronous programming, cloud integration, and terminal application development.

Use Cases

Lightweight team communication, remote server administration chat, educational demonstrations of real-time systems, and nostalgic computing experiences.

Technology Stack

const BanterStack = {
  language: "C# (.NET 10.0)",
  ui_framework: "Terminal.Gui v1.19.0",
  cloud_database: "Google Cloud Firestore v3.11.0",
  local_cache: "LiteDB v5.0.21",
  ai_integration: "OpenAI API v2.6.0",
  authentication: "Firebase Auth (Custom)",
  deployment: "Cross-platform Console"
};

02. Requirements

Software, System, and Installation

Software Requirements

Component Version Purpose
.NET SDK 10.0 or higher Runtime environment for C# application
Visual Studio / VS Code Latest Integrated Development Environment
Git 2.0+ Version control system
Google Cloud Account Active Firestore database access
Firebase Service Account JSON Key Authentication credentials

System Requirements

  • Operating System: Windows 10/11, macOS 10.15+, or Linux (Ubuntu 20.04+)
  • Memory: Minimum 4GB RAM (8GB recommended for development)
  • Storage: 500MB free disk space
  • Network: Stable internet connection with minimum 1 Mbps bandwidth
  • Terminal: Modern terminal emulator with Unicode and color support

Installation Steps

Option 1: Quick Install (Recommended)

For the easiest experience, you don't need to build the code manually. Simply click the button below to download the standalone Windows installer directly.

Download Installer
OR BUILD FROM SOURCE

Step 1: Clone the Repository

git clone https://github.com/dreeyanzz/Banter.git
cd Banter

Step 2: Configure Firebase

  1. Create a Firebase project at console.firebase.google.com
  2. Enable Firestore Database in the Firebase Console
  3. Generate a service account key (JSON file)
  4. Place the JSON file in the project root as firebase-service-account.json
  5. Update Banter.csproj to embed the resource

Step 3: Restore Dependencies

dotnet restore

Step 4: Build the Application

dotnet build --configuration Release

Step 5: Run the Application

dotnet run

Important

Ensure your Firebase service account has the necessary permissions (Firestore read/write access). The application will fail to start without valid credentials.

03. File Handling Overview

File Types, Operations, and Error Handling

File Types and Purpose

File Type Usage Example
.json Firebase service account credentials firebase-service-account.json
.txt ASCII art assets (logo, branding) BanterLogo.txt
.db LiteDB local cache database banter-cache.db
.cs C# source code files Program.cs, FirebaseHelper.cs

File Operations

Reading Files

The application uses File.ReadAllLines() for reading text files such as the ASCII logo:

private void DisplayLogo()
{
    List<string> BanterLogo = [.. File.ReadAllLines("BanterLogo.txt")];
    BanterLogo.Insert(0, new string(' ', BanterLogo[0].Length));
    this.BanterLogo.SetSource(BanterLogo);
}

Embedded Resources

Firebase credentials are embedded as resources to avoid external file dependencies:

Assembly assembly = Assembly.GetExecutingAssembly();
string resourceName = "Banter.firebase-service-account.json";

GoogleCredential credential;
using (Stream? stream = assembly.GetManifestResourceStream(resourceName))
{
    if (stream == null)
        throw new Exception("Error: Could not find embedded JSON key.");
    
    credential = GoogleCredential.FromStream(stream);
}

Writing to Cloud (Firestore)

All persistent data is written to Firestore, not local files:

// Adding a new message
Dictionary<string, object> message = new()
{
    { "sender_id", senderId },
    { "text", messageText },
    { "timestamp", Timestamp.FromDateTime(DateTime.UtcNow) }
};

await db.Collection("Chatrooms")
    .Document(chatroomId)
    .Collection("messages")
    .AddAsync(message);

Error Handling

Missing Embedded Resource

if (stream == null)
{
    throw new Exception(
        "Error: Could not find the " +
        "embedded JSON key."
    );
}

Firestore Connection Failure

try
{
    Database = new FirestoreDbBuilder
    {
        ProjectId = ProjectId,
        Credential = credential
    }.Build();
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Database = null!;
}

04. Code Structure

Architecture, Classes, and Modularity

Project File Structure

Banter/
Program.cs // Entry point
Banter.csproj // Project config
BanterLogo.txt // ASCII logo
firebase-service-account.json
Utilities/
CustomColorScheme.cs
FirebaseHelper.cs
FirestoreManager.cs
Interfaces.cs
Models.cs
ProfanityChecker.cs
SessionHandler.cs
Validator.cs
WindowHelper.cs
Windows/
AbstractWindow.cs
LogInWindow.cs
CreateAccountWindow.cs
Window1.cs // Chatroom list
Window2.cs // Main chat
Window3.cs // Chat info
CreateChatroomWindow.cs
ChangeChatroomNameWindow.cs
ViewPinnedMessagesWindow.cs

Key Classes

FirebaseHelper

Static utility class providing all Firestore operations. Acts as the central API for database interactions.

AddAccount(User user)
GetUserName(string user_id)
CreateChatroom(List<string>)
PinChatroomMessage(...)

SessionHandler

Manages global application state including logged-in user info, current chatroom, and chatroom list with event-driven updates.

Username, UserId
CurrentChatroomId
IsLoggedIn
ClearSession()

ProfanityChecker

Implements both simple and robust profanity filtering with leetspeak normalization.

CensorTextSimple(string)
CensorTextRobust(string)
Handles obfuscated profanity

Validator

Provides validation methods for common data formats like email addresses.

IsValidEmail(string)
Uses comprehensive regex
Timeout protection

Real-Time Messaging Flow

Step 1: User Sends Message

private async void OnButtonSendClicked()
{
    string messageText = chatBox.Text.ToString() ?? "";
    
    if (string.IsNullOrWhiteSpace(messageText)) return;
    
    chatBox.Text = ""; // Clear input immediately
    
    Dictionary<string, object> message = new()
    {
        { "sender_id", SessionHandler.UserId },
        { "text", messageText },
        { "timestamp", Timestamp.FromDateTime(DateTime.UtcNow) }
    };
    
    await db.Collection("Chatrooms")
        .Document(SessionHandler.CurrentChatroomId)
        .Collection("messages")
        .AddAsync(message);
}

Step 2: Firestore Listener Receives Update

private void StartMessagesListener(string chatroom_id)
{
    CollectionReference messagesRef = db.Collection("Chatrooms")
        .Document(chatroom_id)
        .Collection("messages");
    
    Query query = messagesRef.OrderBy("timestamp");
    
    _listener = query.Listen(snapshot => 
        _ = OnMessageSnapshotReceived(snapshot));
}

Step 3: Update UI (Thread Marshalling)

private async Task OnMessageSnapshotReceived(QuerySnapshot snapshot)
{
    foreach (DocumentChange change in snapshot.Changes)
    {
        string message = change.Document.GetValue<string>("text");
        messages.Add(message);
    }
    
    // CRITICAL: Marshal to UI thread
    Application.MainLoop.Invoke(() => {
        chatHistory.SetSource(messages);
        ScrollToLatestChat();
    });
}

05. User Interface

Design, Usability, and Terminal.Gui

Design Philosophy

Banter leverages the powerful Terminal.Gui framework to create a rich, interactive terminal user interface. Unlike traditional console applications that rely solely on Console.WriteLine(), Terminal.Gui provides a widget-based system with full mouse and keyboard navigation, making the experience comparable to modern desktop applications.

Mouse Support

Keyboard Nav

Windows

Real-time

Application Layout

Window 1 (Left)

Chatroom List

  • • Search chatrooms
  • • Create new chats
  • • User info
  • • Logout button

Window 2 (Center)

Main Chat View

  • • Message history
  • • Search messages
  • • Input field
  • • Send button

Window 3 (Right)

Chat Info

  • • Clear messages
  • • Rename chatroom
  • • Leave/Delete chat
  • • Admin options

Input/Output Examples

Input Type Example Input Expected Output
Valid Login dreeyanzz / pass123 Successfully logged in, transition to main chat
Invalid Username nonexistent MessageBox: "Account not found."
Wrong Password wrongpass MessageBox: "Wrong password..."
Empty Fields (empty) MessageBox: "Username or Password cannot be empty!"
Send Message Hello world! Message appears in chat for all users
Profanity This is shit Displayed as: This is ****

Accessibility Features

Hotkeys

All buttons can be activated with Alt + underlined letter

Tab Navigation

Tab/Shift+Tab cycles through interactive elements

Enter Key

Default button activation (e.g., Send button)

Mouse Wheel

Scroll through message history

06. Challenges and Solutions

Technical Obstacles and Problem-Solving

CRITICAL

Challenge 1: Terminal.Gui Threading Issues

Problem

As a beginner to Terminal.Gui, the most significant challenge was understanding thread safety. Firestore's real-time listeners operate on background threads, but Terminal.Gui's UI components can only be safely modified from the main UI thread. Attempting to update a ListView or TextField from a background thread would cause race conditions, visual artifacts, and application crashes.

Solution

Use Application.MainLoop.Invoke() to marshal all UI updates back to the main thread:

// ✅ CORRECT: Marshal to main thread
private async Task OnMessageSnapshotReceived(QuerySnapshot snapshot)
{
    foreach (DocumentChange change in snapshot.Changes)
    {
        string message = change.Document.GetValue<string>("text");
        messages.Add(message);
    }
    
    // This lambda runs on the main UI thread
    Application.MainLoop.Invoke(() => {
        chatHistory.SetSource(messages);
        ScrollToLatestChat();
    });
}

Learning Outcome: This challenge taught me the fundamentals of UI thread affinity and asynchronous programming.

DIFFICULTY

Challenge 2: Terminal.Gui Layout System

Problem

Terminal.Gui's layout system uses Pos and Dim objects for positioning and sizing, which was initially confusing. Understanding the difference between Pos.At(), Pos.Center(), Pos.Percent(), and Pos.AnchorEnd() required significant trial and error.

Solution

// Proper centering
Button myButton = new()
{
    Text = "Click Me",
    X = Pos.Center(),              // Centers horizontally
    Y = Pos.Center() - Pos.At(2),  // Centers vertically, offset up
    Width = Dim.Sized(20)          // Fixed width
};

// Fill remaining space
TextField inputField = new()
{
    X = Pos.At(0),
    Y = Pos.AnchorEnd() - Pos.At(1),
    Width = Dim.Fill() - Dim.Width(sendButton)
};
PERFORMANCE

Challenge 3: Firestore Query Optimization

Problem

Each message displayed required fetching the sender's name from the Users collection. With hundreds of messages, this resulted in hundreds of Firestore read operations, quickly consuming free tier quotas and causing noticeable lag.

Solution

Implemented a caching strategy that fetches all chatroom participants once when the chatroom is opened:

private readonly Dictionary<string, string> currentChatroomParticipants = [];

private async Task FetchAndCacheParticipants(string chatroomId)
{
    currentChatroomParticipants.Clear();
    
    foreach (string id in participantIds)
    {
        string name = await GetUserName(id);
        currentChatroomParticipants[id] = name;
    }
}

// Now messages just look up the cached name - O(1)
string name = currentChatroomParticipants[senderId];

Before

300 messages = 300 reads

After

300 messages = 5-10 reads

CRITICAL

Challenge 4: Event Listener Memory Leaks

Problem

When switching between chatrooms, old Firestore listeners were not being properly disposed, causing multiple listeners to run simultaneously and resulting in duplicate messages appearing in the UI.

Solution

private FirestoreChangeListener? _listener;

private void OnChatroomChanged(string? chatroom_id)
{
    // CRITICAL: Stop old listener before starting new one
    _listener?.StopAsync();
    _listener = null;
    
    // Clear old data
    messages.Clear();
    
    if (string.IsNullOrEmpty(chatroom_id)) return;
    
    // Start new listener
    StartMessagesListener(chatroom_id);
}
COMPLEXITY

Challenge 5: Profanity Filter Bypass Detection

Problem

Simple word-based profanity filters could be easily bypassed with leetspeak (f4ck, @ss) or spacing (f u c k).

Solution

Implemented a two-tier system with a robust filter that normalizes text before checking:

public static string CensorTextRobust(string text)
{
    // 1. Remove non-alphanumeric characters
    // 2. Convert to lowercase
    // 3. Map leetspeak to normal letters (@ → a, 3 → e, 4 → a, etc.)
    string normalized = NormalizeText(text);
    
    // 4. Search for profanity in normalized text
    foreach (string profaneWord in ProfaneWordsArray)
    {
        if (normalized.Contains(profaneWord))
        {
            CensorOriginalText(text, profaneWord);
        }
    }
    
    return text;
}

Reflection

These challenges significantly improved my debugging skills. I learned to anticipate issues before they occur, use proper error handling, and think about edge cases. The experience of overcoming these obstacles was invaluable for my growth as a developer.

07. Testing

Test Cases, Results, and Limitations

Authentication Tests

Test ID Test Case Input Expected Result Status
AUTH-01 Valid Login dreeyanzz / pass123 Successful login, transition to chat PASS
AUTH-02 Invalid Username nonexistent Error: "Account not found." PASS
AUTH-03 Wrong Password wrongpass Error: "Wrong password..." PASS
AUTH-04 Empty Credentials (empty fields) Error: "Username or Password cannot be empty!" PASS
AUTH-05 Short Username abc (3 chars) Error: "Username must be at least 8 characters long" PASS
AUTH-06 Duplicate Username dreeyanzz (existing) Error: "Username already taken" PASS
AUTH-07 Invalid Email notanemail Error: "Invalid email format!" PASS
AUTH-08 Password Mismatch pass123 / pass456 Error: "Passwords must match!" PASS

Messaging Tests

Test ID Test Case Input Expected Result Status
MSG-01 Send Valid Message Hello world! Message appears in chat for all users PASS
MSG-02 Send Empty Message (empty) No message sent, input cleared PASS
MSG-03 Profanity Filter (Simple) This is shit Displayed as: This is **** PASS
MSG-04 Profanity Filter (Leetspeak) f4ck this Displayed as: **** this PASS
MSG-05 Profanity Filter (Spaced) f u c k Displayed as: ********* PASS
MSG-06 Pin Message Select message, press Enter Message pinned, indicated with PASS
MSG-07 View Pinned Messages Click "View pinned messages" Modal window shows all pinned messages PASS
MSG-08 Unpin Message Select in modal, press Enter Message unpinned, removed PASS

Test Results Summary

38

Total Tests

35

Passed

3

Known Issues

92%

Pass Rate

Known Limitations

  • No End-to-End Encryption: Messages are stored in plaintext in Firestore. User passwords are also stored in plaintext (NOT recommended for production).
  • Internet Required: The application cannot function offline. A future version could implement offline queueing.
  • Limited Unicode Support: Some emoji and special Unicode characters may not render correctly depending on the terminal emulator.
  • No File Sharing: Only text messages are supported. Future versions could add file uploads via Firebase Storage.
  • No Voice/Video: Text-only communication. WebRTC integration would be needed for real-time voice/video calls.

08. Future Enhancements

Planned Features and Improvements

Security Improvements

JWT Authentication

Replace plaintext password storage with JWT (JSON Web Tokens) for secure, stateless authentication. This would allow session management across multiple devices and provide token expiration for better security.

// Future implementation
public static async Task<string> Login(string username, string password)
{
    string hashedPassword = BCrypt.HashPassword(password);
    bool isValid = await ValidateCredentials(username, hashedPassword);
    
    if (isValid)
        return GenerateJWT(username);
    
    return null;
}

End-to-End Encryption

Implement E2EE using AES-256 encryption for messages. Each chatroom would have a unique encryption key shared only among participants, ensuring that even Firestore admins cannot read message contents.

// Future implementation
public static async Task SendEncryptedMessage(string message, string key)
{
    string encrypted = AES.Encrypt(message, key);
    await FirebaseHelper.AddMessage(encrypted);
}

AI Integration

PROPOSED

OpenAI-Powered Features

The project already includes the OpenAI package. Future versions could leverage GPT models for:

  • Smart Replies: Suggest contextual message responses
  • Message Translation: Real-time translation between languages
  • Chat Summarization: AI-generated summaries of long conversations
  • Moderation Bot: AI assistant for automatic spam detection
// Example: AI-powered chat summary
public static async Task<string> SummarizeChatHistory(List<string> messages)
{
    string chatText = string.Join("\n", messages);
    
    var completion = await openAiClient.GetChatCompletionsAsync(
        new ChatCompletionsOptions {
            Messages = {
                new ChatMessage(ChatRole.System, "Summarize this chat"),
                new ChatMessage(ChatRole.User, chatText)
            }
        }
    );
    
    return completion.Value.Choices[0].Message.Content;
}

Voice and Video Calls

WebRTC Integration

Add real-time voice and video calling capabilities using WebRTC. This would require:

  • Signaling server for peer connection negotiation
  • STUN/TURN servers for NAT traversal
  • Audio/video encoding/decoding libraries
  • UI for call controls (mute, camera toggle, screen share)

Challenge: Terminal applications cannot directly render video. Would need to spawn a separate window or use ASCII art video rendering (novelty feature).

Additional Features

Emoji Support

Add emoji picker and proper Unicode rendering

File Sharing

Upload/download files via Firebase Storage

Code Highlighting

Detect code blocks and apply syntax coloring

Message Reactions

React to messages with emoji/icons

Thread Replies

Reply to specific messages, creating threads

User Status

Online/Offline/Away indicators

09. Conclusion

Reflection and Takeaways

Reflection

This project was a transformative learning experience that bridged the gap between theoretical C# knowledge and practical application development. Building Banter from the ground up taught me not just how to write code, but how to architect a complete, production-ready application with real-time capabilities, cloud integration, and a polished user experience.

The most valuable aspect was encountering and overcoming real-world challenges that don't appear in textbooks or tutorials. Wrestling with Terminal.Gui's threading model, optimizing Firestore queries to avoid quota limits, and implementing a robust profanity filter that handles obfuscation - these challenges forced me to think critically and creatively.

I learned how to program even more effectively, reducing my time to debug because I can already foresee what is about to happen for specific scenarios. It also made me explore external APIs like Firestore and others, expanding my understanding of cloud services and real-time data synchronization.

Key Takeaways

Asynchronous Programming

Mastered async/await patterns, learned to prevent deadlocks, and understood the importance of ConfigureAwait. Can now write responsive applications that don't block the UI thread.

Cloud Integration

Gained hands-on experience with Firebase Firestore, including real-time listeners, document queries, batch operations, and authentication.

File Handling in C#

Learned to read text files, embed resources in assemblies, handle file I/O exceptions, and manage file lifecycle in different build configurations.

Error Handling

Implemented comprehensive try-catch blocks, graceful degradation, and user-friendly error messages. Learned when to throw exceptions vs. return nulls.

Application Architecture

Practiced separation of concerns, singleton pattern, event-driven architecture, and dependency management for maintainable code structures.

Debugging & Problem-Solving

Developed the ability to foresee potential issues before they occur, trace execution through complex async call stacks, and use logging effectively.

Personal Growth

Beyond technical skills, this project taught me valuable lessons about perseverance and growth mindset. There were moments of frustration when dealing with obscure Terminal.Gui bugs or when Firestore listeners wouldn't behave as expected. But pushing through those challenges and seeing the application come together was incredibly rewarding.

I've become more confident in my ability to tackle unfamiliar technologies and solve complex problems. When I started this project, I had never worked with Terminal.Gui, Firestore, or asynchronous programming at this scale. Now, I not only understand these technologies but can explain them to others and make informed architectural decisions.

Thank You

Thank you for reading this comprehensive documentation. Banter is more than just a chat application - it's a testament to what can be achieved with dedication, curiosity, and a willingness to learn.

If you have feedback, suggestions, or would like to contribute to Banter, please reach out via the links in the Appendix.

10. Appendix

References, Resources, and Links

Source Code

The complete source code for Banter is available on GitHub:

https://github.com/dreeyanzz/Banter

git clone https://github.com/dreeyanzz/Banter.git

References

Dependencies

Package Version License Purpose
Terminal.Gui 1.19.0 MIT Terminal user interface framework
Google.Cloud.Firestore 3.11.0 Apache 2.0 Cloud database integration
LiteDB 5.0.21 MIT Local NoSQL database
OpenAI 2.6.0 MIT AI integration (future use)

Connect with Developer

Project Statistics

2,500+

Lines of Code

23

Classes

100+

Hours Developed

4

External APIs