Banter

A Real-Time TUI Chat Application

Adrian Seth Tabotabo

(Use full screen for best experience, use arrow-keys for navigation)

What is Banter?

Core Tech: C# (.NET) | Google Firestore | Terminal.Gui | LiteDB

Installation & Setup

Key Features (1/2)

Key Features (2/2)

Key Features — Code Example

Applying Profanity Filter

// ... inside OnSnapshotReceived ...
if (...)
    senderName = "Unknown User";

string displayName =
    senderId == SessionHandler.UserId ? $"{senderName} (me)" : senderName;

string chatEntry = $"{displayName}: {ProfanityChecker.CensorTextRobust(message)}";
// ...

Data & Storage

A hybrid approach for cloud, local, and static data.

Project Source Structure

Filtered Source Tree


C:.
|   .gitignore
|   BanterLogo.txt
|   CpE261FinalProject.csproj
|   Program.cs
|   Schema.txt
|
+---Utilities
|       CachingOperations.cs
|       ClassWrappers.cs
|       CustomColorScheme.cs
|       FirebaseHelper.cs
|       FirestoreManager.cs
|       Interfaces.cs
|       Models.cs
|       ProfanityChecker.cs
|       SessionHandler.cs
|       Validator.cs
|       WindowHelper.cs
|
\---Windows
        AbstractWindow.cs
        ChangeNameWindow.cs
        CreateAccountWindow.cs
        CreateChatroomWindow.cs
        LogInWindow.cs
        ViewPinnedMessagesWindow.cs
        Window1.cs
        Window2.cs
        Window3.cs
          

Firebase Database Schema

/Users/{userId}


{
  "name": "Adrian Seth...",
  "username": "dreeyanzz",
  "email": "adriangwapoz125@...",
  "password": "scarara",
  "chatrooms": [
    "AyELCpLJgYy3TG0JedBH"
  ]
}
            

/Chatrooms/{chatroomId}


{
  "chatroom_name": "American Rejects",
  "type": "group",
  "participants": [...],
  "admins": [...],
  "pinned_messages": [...],

  "messages": { (subcollection)
    "{messageId}": {
      "sender_id": "ZzWLD...",
      "text": "asd123123",
      "timestamp": ...
    }
  }
}
            

Database Schema: /Users

Collection: /Users

Field Name Data Type Description
name string User's full name
username string User's unique public username
email string User's private email address
password string User's login password (plaintext)
chatrooms Array<string> List of Chatroom IDs the user is part of

Database Schema: /Chatrooms

Collection: /Chatrooms

Field Name Data Type Description
chatroom_name string Display name of the chat (e.g., "American Rejects")
type string "group" or "individual"
participants Array<string> List of User IDs in the chat
admins Array<string> List of User IDs with admin privileges
pinned_messages Array<string> List of Message IDs that are pinned
messages Subcollection Contains the actual chat messages (See below)

Database Schema: Messages

Subcollection: /Chatrooms/{id}/messages

Field Name Data Type Description
sender_id string The User ID of the participant who sent the message
text string The text content of the message
timestamp Timestamp The server-side timestamp of when the message was sent

Challenges & Solutions

Testing & Validation

A multi-layered strategy to ensure stability.

Testing (Test Cases)

Authentication:

FAIL » Tried login with incorrect username/password.

PASS » Error message shown, access denied.

Registration:

FAIL » Tried creating account with taken username, mismatched passwords, invalid email.

PASS » All validation checks handled the errors gracefully.

Admin Functionality:

TEST » As admin, deleted a group chat.

PASS » Confirmed with collaborators that the chat was instantly removed.

Limitations & Known Issues

Future Enhancements

(Planned features to mitigate known limitations)

Conclusion & Key Learnings

(1/2) - Asynchronous C#

Essential for managing network requests to Firebase without freezing the UI.

Code Example: Async Login Button Click

private async void OnLogInButtonClicked()
{
    // ... get username/password ...

    logInButton.Text = "Logging in...";
    SetInteractables(false);

    string? user_id = await FirebaseHelper.GetUserIdFromUsername(inputUsername);
    if (user_id == null)
    {
        // ... show error ...
        return;
    }

    // ... get user info, set session, etc. ...
    await SessionHandler.StartChatroomsListener();
}

Conclusion & Key Learnings

(2/2) - Event-Driven Architecture

Used C# events in SessionHandler to decouple code, allowing windows to react to state changes.

Code Example: Defining the Event

public static class SessionHandler
{
    // ... other properties ...
    public static event Action<string?>? CurrentChatroomChanged;
    // ...
}

Code Example: Subscribing to the Event

private Window2()
{
    // ...
    SessionHandler.CurrentChatroomChanged += OnChatroomChanged;
    SessionHandler.IsLoggedInChanged += OnLogInChanged;
    // ...
}

The "Spark"

My Most Impactful Discovery

The most surprising discovery was that a terminal app could be interacted with using a mouse.

That single Terminal.Gui feature was the inspiration for the project. A chat app is hard to navigate with just a keyboard, and this "sparked" the interest to build Banter.

References

Google Firestore Documentation

Terminal.Gui (gui.cs) Project Page

LiteDB Documentation

Thank You