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 InstallerStep 1: Clone the Repository
git clone https://github.com/dreeyanzz/Banter.git
cd Banter
Step 2: Configure Firebase
-
Create a Firebase project at
console.firebase.google.com - Enable Firestore Database in the Firebase Console
- Generate a service account key (JSON file)
-
Place the JSON file in the project root as
firebase-service-account.json - Update
Banter.csprojto 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
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, UserIdCurrentChatroomIdIsLoggedInClearSession()ProfanityChecker
Implements both simple and robust profanity filtering with leetspeak normalization.
CensorTextSimple(string)CensorTextRobust(string)Validator
Provides validation methods for common data formats like email addresses.
IsValidEmail(string)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
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.
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)
};
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
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);
}
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
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
Microsoft .NET
docs.microsoft.com/dotnetTerminal.Gui
github.com/gui-cs/Terminal.GuiGoogle Firestore
firebase.google.com/docs/firestoreLiteDB
www.litedb.orgDependencies
| 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