Skip to main content

ChatManager

Singleton manager class for the chat system. Manages chat server connection and message transmission/reception.

URL Verification

This API uses the service-api.playnanoo.com domain.

API Information

  • Server List URL: https://service-api.playnanoo.com/chat/v20211101/server
  • Filter URL: https://service-api.playnanoo.com/chat/v20211101/filter
  • Method: PUT
  • Authentication Required: No

Key Features

MethodDescription
ConnectConnect to chat server
IsConnectedCheck connection status
SubscribeSubscribe to channel (with previous message count option)
UnsubscribeUnsubscribe from channel
GetChannelsQuery channel list
GetPlayersOnlineQuery player online status
SendPublicMessageSend public message
SendPrivateMessageSend private message
FetchFilterWordsFetch prohibited words list
FilterApply prohibited word filter to message

Unity C# Implementation

using System.Collections;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Networking;

public class ChatManager : MonoBehaviour
{
public static ChatManager Instance
{
get
{
if (_instance == null)
{
var go = new GameObject("ChatManager");
_instance = go.AddComponent<ChatManager>();
}
return _instance;
}
}
private static ChatManager _instance;

private const string CHAT_SERVER_URL = "https://service-api.playnanoo.com/chat/v20211101/server";
private const string CHAT_FILTER_URL = "https://service-api.playnanoo.com/chat/v20211101/filter";

private ChatClient _chatClient;
private IChatListener _listener;
private string[] _filterWords;

void Awake()
{
if (_instance != null && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
DontDestroyOnLoad(gameObject);
}

public void Connect(IChatListener listener)
{
_listener = listener;
_chatClient = new ChatClient(listener, DataManager.Instance.Nickname);
StartCoroutine(FetchServerListAndConnect());
}

void Update()
{
// Continuously trigger events to receive real-time messages.
// If message reception event is not executed, user messages will not be received.
_chatClient?.Service();
}

void OnDestroy()
{
if (_instance == this)
_instance = null;
_chatClient?.Disconnect();
}

#region Server Connection

private IEnumerator FetchServerListAndConnect()
{
bool done = false;
ChatServerResponse response = null;
string error = null;

yield return HttpClient.Send<object, ChatServerResponse>(
UnityWebRequest.kHttpVerbPUT, CHAT_SERVER_URL, false, null,
res => { response = res; done = true; },
err => { error = err?.Message; done = true; });

while (!done) yield return null;

if (error != null || response?.Servers == null || response.Servers.Length == 0)
{
_listener?.OnError("SERVER_LIST_FAILED", "Failed to fetch server list");
yield break;
}

var server = response.Servers[Random.Range(0, response.Servers.Length)];
_chatClient.Connect(server);
}

#endregion

#region Filter

public void FetchFilterWords()
{
StartCoroutine(FetchFilterWordsCoroutine());
}

private IEnumerator FetchFilterWordsCoroutine()
{
bool done = false;
ChatFilterResponse response = null;

yield return HttpClient.Send<object, ChatFilterResponse>(
UnityWebRequest.kHttpVerbPUT, CHAT_FILTER_URL, false, null,
res => { response = res; done = true; },
err => { done = true; });

while (!done) yield return null;

if (response?.FilterWords != null)
_filterWords = response.FilterWords;
}

public string Filter(string message, char separator = '*')
{
if (_filterWords == null || _filterWords.Length == 0)
return message;

foreach (var word in _filterWords)
{
if (!string.IsNullOrEmpty(word))
message = Regex.Replace(message, word, new string(separator, word.Length), RegexOptions.IgnoreCase);
}
return message;
}

#endregion

#region Public API

public bool IsConnected() => _chatClient?.IsConnected ?? false;
public void Subscribe(string channel, int prevMessageCount = 0) => _chatClient?.Subscribe(channel, prevMessageCount);
public void Unsubscribe(string channel) => _chatClient?.Unsubscribe(channel);
public void GetChannels() => _chatClient?.GetChannels();
public void GetPlayersOnline(string[] userIds) => _chatClient?.GetPlayersOnline(userIds);
public void SendPublicMessage(string channel, string text) => _chatClient?.SendPublicMessage(channel, text);
public void SendPrivateMessage(string targetUserId, string text) => _chatClient?.SendPrivateMessage(targetUserId, text);

#endregion
}

Usage Example

public class ChatExample : MonoBehaviour, IChatListener
{
void Start()
{
ChatManager.Instance.Connect(this);
}

public void OnConnected()
{
Debug.Log("Chat server connection successful");
// Fetch prohibited words list
ChatManager.Instance.FetchFilterWords();
// Fetch 10 previous chat messages
ChatManager.Instance.Subscribe("global", 10);
}

public void OnDisconnected()
{
Debug.Log("Chat server disconnected");
}

public void OnError(string code, string message)
{
Debug.LogError($"Chat error: [{code}] {message}");
}

public void OnChannels(ChatChannelInfo[] channels)
{
foreach (var ch in channels)
{
Debug.Log($"Channel: {ch.channel}, Users: {ch.count}");
}
}

public void OnSubscribed(ChatUserInfo user)
{
Debug.Log($"{user.visitorName} has entered.");
}

public void OnUnSubscribed(ChatUserInfo user)
{
Debug.Log($"{user.visitorName} has left.");
}

public void OnPublicMessage(ChatUserInfo sender, string message)
{
// Apply prohibited word filter
Debug.Log($"[{sender.visitorName}]: {ChatManager.Instance.Filter(message)}");
}

public void OnPrivateMessage(ChatUserInfo sender, string message)
{
// Apply prohibited word filter
Debug.Log($"[Whisper][{sender.visitorName}]: {ChatManager.Instance.Filter(message)}");
}

public void OnNotifyMessage(ChatUserInfo sender, string message)
{
Debug.Log($"[Notification]: {ChatManager.Instance.Filter(message)}");
}

public void OnPlayerOnline(ChatPlayerInfo[] players)
{
foreach (var p in players)
{
Debug.Log($"Player: {p.userUniqueId}, Online: {p.online}");
}
}

void SendMessage()
{
// Send message (send original to server)
ChatManager.Instance.SendPublicMessage("global", "Hello!");
}
}