How to retrieve proper username formatting and user permissions in Twitch chat bot

I built a Twitch chat integration system using multiple tutorials and documentation sources. The connection works fine, and I can read messages from the chat, but I’m encountering two specific problems.

The first issue is that all usernames come through as lowercase, even though the actual Twitch usernames contain uppercase letters. I need to display the names with their correct capitalization.

Next, I’m unable to determine user roles like moderators, subscribers, or VIPs. While this isn’t as urgent, it would be nice to have.

My primary focus is fixing the username capitalization. Has anyone experienced this before? Is there a method to retrieve the correctly formatted usernames from the IRC connection?

using System;
using System.IO;
using System.Net.Sockets;
using UnityEngine;

public class StreamChatHandler : MonoBehaviour
{
    public static StreamChatHandler instance;
    
    public ChatCredentials credentials = new();
    
    public event Action<string, string> OnMessageReceived;
    public event Action<string> OnConnectionSuccess;
    public event Action OnDisconnected;
    public event Action<string> OnAuthFailed;
    
    public bool isConnected = false;
    
    private TcpClient chatClient;
    private StreamReader inputStream;
    private StreamWriter outputStream;
    
    private const string SERVER_URL = "irc.chat.twitch.tv";
    private const int SERVER_PORT = 6667;
    
    private float heartbeatTimer = 0;
    private string credentialsFile = "chatauth.json";
    
    private void Awake()
    {
        DontDestroyOnLoad(gameObject);
        instance = this;
        credentialsFile = Application.persistentDataPath + "/chatauth.json";
        LoadCredentials();
    }
    
    public void SetupConnection(string username, string token, string channelName)
    {
        credentials.Username = username;
        credentials.AuthToken = token;
        credentials.ChannelName = channelName;
    }
    
    public void EstablishConnection()
    {
        CloseConnection();
        
        chatClient = new TcpClient(SERVER_URL, SERVER_PORT);
        inputStream = new StreamReader(chatClient.GetStream());
        outputStream = new StreamWriter(chatClient.GetStream());
        
        credentials.Username = credentials.Username.ToLower();
        credentials.ChannelName = credentials.ChannelName.ToLower();
        
        outputStream.WriteLine("PASS " + credentials.AuthToken);
        outputStream.WriteLine("NICK " + credentials.Username);
        outputStream.WriteLine($"USER {credentials.Username} 8 * :{credentials.Username}");
        outputStream.WriteLine("JOIN #" + credentials.ChannelName);
        outputStream.Flush();
    }
    
    private void Update()
    {
        if (chatClient == null) return;
        
        if (!chatClient.Connected && isConnected)
        {
            EstablishConnection();
        }
        
        heartbeatTimer += Time.deltaTime;
        if (heartbeatTimer > 45)
        {
            outputStream.WriteLine("PING " + SERVER_URL);
            outputStream.Flush();
            heartbeatTimer = 0;
        }
        
        if (chatClient.Available > 0)
        {
            string rawMessage = inputStream.ReadLine();
            
            if (rawMessage.Contains("PRIVMSG"))
            {
                int nameEnd = rawMessage.IndexOf("!");
                string sender = rawMessage[1..nameEnd];
                
                int contentStart = rawMessage.IndexOf(":", 1);
                string content = rawMessage[(contentStart + 1)..];
                
                OnMessageReceived?.Invoke(sender, content);
            }
            else if (rawMessage.Contains(":Welcome, GLHF!"))
            {
                SaveCredentials();
                isConnected = true;
                OnConnectionSuccess?.Invoke(credentials.Username);
            }
        }
    }
}

Indeed, IRC provides usernames in lowercase by default. To retrieve usernames with the correct capitalization, ensure to request Twitch tags prior to joining the chat channel. You can do this by adding the following line right after your NICK command:

outputStream.WriteLine("CAP REQ :twitch.tv/tags");

This will include additional metadata in the PRIVMSG messages, allowing you to access the display-name for the correct capitalization and badges for user roles like moderators and subscribers. The tags will appear at the beginning of the message, separated by semicolons. I implemented this in my bot, and it resolved both issues effectively. Just be aware that it slightly complicates your parsing logic, but it is worth the effort for the accurate user information.

The lowercase username issue arises because Twitch’s IRC normalizes the nick field. To obtain the display-name with proper capitalization, you need to enable the IRCv3 tags capability. Include this line before joining the channel:

outputStream.WriteLine("CAP REQ :twitch.tv/tags twitch.tv/membership");

After that, adjust your message parsing to extract the display-name from the tags section. Tags appear before the colon as key-value pairs, separated by semicolons, like so: display-name=SwiftCoder42;badges=moderator/1. This solution also addresses your second problem since badges contain role information. I encountered the same situation while developing my first Twitch bot, and this method resolved both concerns.