Advertisement
Cookie042

TwitchIRC Unity

Jan 6th, 2019
112
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 12.18 KB | None | 0 0
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using System;
  4. using System.Net.Sockets;
  5. using System.IO;
  6. using UnityEngine.UI;
  7. using System.Text.RegularExpressions;
  8. using System.Linq;
  9. using UnityEngine.EventSystems;
  10. using TMPro;
  11.  
  12. [ExecuteInEditMode]
  13. public class TwitchChat : MonoBehaviour
  14. {
  15.     public LineData selfUserData; // collected from GLOBALUSERSTATE on login
  16.  
  17.     public TMP_SpriteAsset defSpriteAsset;
  18.  
  19.     VerticalLayoutGroup contentGroup;
  20.     public GameObject chatMessagePrefab;
  21.  
  22.     //for sending messages to chat
  23.     public TMPro.TMP_InputField inputField;
  24.  
  25.     private TcpClient twitchClient;
  26.     private StreamReader reader;
  27.     private StreamWriter writer;
  28.  
  29.     public bool verbose = false;
  30.  
  31.     public bool connected = false;
  32.  
  33.     public List<EmojiPacker.TextureAtlas> textureAtlases = new List<EmojiPacker.TextureAtlas>();
  34.  
  35.     public void updateLookupTables()
  36.     {
  37.         defSpriteAsset.UpdateLookupTables();
  38.     }
  39.  
  40.     public string username, password, channelName; //pw from: https://twitchapps.com/tmi
  41.  
  42.     public EmoteJsonDataManager jsonDataManager;
  43.     public EmojiPacker emojiPacker;
  44.  
  45.     public delegate void Command(string[] commandData);
  46.  
  47.     public static Command CommandEventHandler;
  48.  
  49.     public void OnEnable()
  50.     {
  51.         contentGroup = transform.FindDeepChild("ChatLog").GetComponent<VerticalLayoutGroup>();
  52.         //Connect();
  53.  
  54.  
  55.         //jsonDataManager = new EmoteJsonDataManager(Application.dataPath +"/");
  56.         //jsonDataManager.LoadData(this);
  57.  
  58.         //emojiPacker = new EmojiPacker();
  59.        
  60.         //building a list of all the global id's for the sprite packer
  61.         //string[] ids = new string[jsonDataManager.globalData.Keys.Count/20];
  62.         //string[] names = jsonDataManager.globalData.Keys.ToArray();
  63.  
  64.         //for (int i = 0; i < ids.Length; i++)
  65.         //{
  66.         //    ids[i] = jsonDataManager.globalData[names[i]].id.ToString();
  67.         //}
  68.  
  69.         //StartCoroutine(emojiPacker.PackIDS(this, ids, FilterMode.Point, 2048,
  70.         //    (t2d, dict_idStr_uvRect) => {
  71.             //    StartCoroutine(emojiPacker.SaveTexture(t2d, Application.dataPath + "/Resources/Sprite Assets/Global.png"));
  72.             //    defSpriteAsset.spriteInfoList.Clear();
  73.  
  74.             //    defSpriteAsset.spriteSheet = t2d; // this produces a TypeMismatch in unity and doesnt seem to work;
  75.  
  76.             //    for (int j = 0; j < ids.Length; j++)
  77.             //    {
  78.             //        string id = ids[j];
  79.             //        string name = names[j];
  80.                    
  81.             //        var rect = dict_idStr_uvRect[id];
  82.  
  83.  
  84.             //        var sprite = new TMP_Sprite
  85.             //        {
  86.             //            id = j,
  87.             //            name = name.ToLower(),
  88.             //            x = rect.x * t2d.width,
  89.             //            y = rect.y * t2d.height,
  90.             //            width = rect.width * t2d.width,
  91.             //            height = rect.height * t2d.height,
  92.             //            unicode = 0,
  93.             //            hashCode = 0,
  94.             //            xOffset = 0,
  95.             //            pivot = Vector2.zero,
  96.             //            sprite = null,
  97.  
  98.             //            scale = 1,
  99.             //            yOffset = rect.width * t2d.width * .833f,
  100.             //            xAdvance = rect.width * t2d.width
  101.             //        };
  102.  
  103.             //        defSpriteAsset.spriteInfoList.Add(sprite);
  104.             //    }
  105.  
  106.  
  107.             //    defSpriteAsset.UpdateLookupTables();
  108.             //    Debug.Log(defSpriteAsset.GetSpriteIndexFromName("kappa"));
  109.             //    //defSpriteAsset.fallbackSpriteAssets.Add((TMP_SpriteAsset));
  110.             //}));
  111.  
  112.     }
  113.  
  114.  
  115.     public void OnDisable()
  116.     {
  117.         jsonDataManager = null;
  118.  
  119.         if (twitchClient != null)
  120.         {
  121.             twitchClient.Close();
  122.         }
  123.  
  124.         if (contentGroup != null)
  125.         {
  126.             for (int i = contentGroup.transform.childCount - 1; i >= 0; i--)
  127.             {
  128.  
  129.             }
  130.         }
  131.  
  132.     }
  133.  
  134.     //public void Start()
  135.     //{
  136.     //    if (!jsonDataManager.loaded)
  137.     //    {
  138.     //        jsonDataManager.LoadData(this);
  139.     //    }
  140.     //    Connect();
  141.     //}
  142.  
  143.     bool allowEnter;
  144.     void Update()
  145.     {
  146.         if (twitchClient == null)
  147.         {
  148.             Connect();
  149.             return;
  150.         }
  151.         if (!twitchClient.Connected)
  152.         {
  153.             Connect();
  154.             return;
  155.         }
  156.  
  157.         ReadChat();
  158.        
  159.         if (allowEnter && (inputField.text.Length > 0) && (Input.GetKey(KeyCode.Return) || Input.GetKey(KeyCode.KeypadEnter)))
  160.         {
  161.             SendChat();
  162.             inputField.text = "";
  163.             allowEnter = false;
  164.         }
  165.         else
  166.         {
  167.             allowEnter = inputField.isFocused;
  168.         }
  169.  
  170.         if (contentGroup.transform.childCount > 10)
  171.         {
  172.             var child = contentGroup.transform.GetChild(0);
  173.  
  174.             DestroyImmediate(child.gameObject);
  175.         }
  176.  
  177.     }
  178.  
  179.     private void Connect()
  180.     {
  181.         twitchClient = new TcpClient("irc.chat.twitch.tv", 6667);
  182.         reader = new StreamReader(twitchClient.GetStream());
  183.         writer = new StreamWriter(twitchClient.GetStream());
  184.  
  185.         writer.WriteLine("CAP REQ :twitch.tv/tags twitch.tv/commands twitch.tv/membership");
  186.         writer.Flush();
  187.         writer.WriteLine("PASS " + Oauth.auth);
  188.         writer.WriteLine("NICK " + username);
  189.         writer.WriteLine("USER " + username + " 8 * :" + username);
  190.         writer.WriteLine("JOIN #" + channelName);
  191.         writer.Flush();
  192.  
  193.         connected = true;
  194.     }
  195.    
  196.     public void Send(string text)
  197.     {
  198.         writer.WriteLine(text);
  199.         writer.Flush();
  200.     }
  201.  
  202.     public void SendChat()
  203.     {
  204.         if (inputField != null)
  205.         {
  206.             writer.WriteLine($"PRIVMSG #{channelName} :{inputField.text}");
  207.             writer.Flush();
  208.  
  209.             var newMessage = Instantiate(chatMessagePrefab, contentGroup.transform);
  210.             var tmp = newMessage.GetComponent<TMPro.TextMeshProUGUI>();
  211.             tmp.text = "<" + selfUserData.colorHex + ">" + username + "</color>: " + inputField.text;
  212.         }
  213.     }
  214.  
  215.     private void ReadChat()
  216.     {
  217.         if (twitchClient.Available > 0)
  218.         {
  219.             var message = reader.ReadLine(); //Read in the current message
  220.            
  221.             if (message == "PING :tmi.twitch.tv")
  222.             {
  223.                 Send("PONG :tmi.twitch.tv");
  224.                 Debug.Log("PONG :tmi.twitch.tv");
  225.             } else if (message.Contains("GLOBALUSERSTATE"))
  226.             {
  227.                 //Recieved GLOBALUSERSTATE data
  228.                 selfUserData = new LineData(message);
  229.             } else if (message.Contains("PRIVMSG"))
  230.             {
  231.                
  232.                 LineData data = new LineData(message);
  233.  
  234.                 //matching pattern !<command> <value>
  235.                 Match commandMatch = Regex.Match(data.chatText, "^!(\\w+)\\s(-?\\d+\\.?\\d*|\"(.*)\")");
  236.  
  237.                 if (commandMatch.Success)
  238.                 {
  239.                     string[] commandArray = new string[commandMatch.Groups.Count - 1];
  240.                     for (int i = 0; i < commandArray.Length; i++)
  241.                     {
  242.                         commandArray[i] = commandMatch.Groups[i+1].Value;
  243.                     }
  244.                     CommandEventHandler(commandArray);
  245.                 }
  246.  
  247.                 var newMessage = Instantiate(chatMessagePrefab, contentGroup.transform);
  248.                 var tmp = newMessage.GetComponent<TMPro.TextMeshProUGUI>();
  249.                
  250.                 tmp.text = "<" + data.colorHex + ">" + data.displayName + "</color>: " + data.chatText ;
  251.  
  252.                 //chatBox.text = chatBox.text + "\n" + String.Format("{0}: {1}", chatName, message);
  253.             }else if (verbose)
  254.             {
  255.                 print(message);
  256.             }
  257.         }
  258.     }
  259.  
  260.     public void ClearChatLog()
  261.     {
  262.         for (int i = contentGroup.transform.childCount - 1; i >= 0; i--)
  263.         {
  264.             DestroyImmediate(contentGroup.transform.GetChild(i).gameObject);
  265.         }
  266.     }
  267. }
  268.  
  269.  
  270. interface ICommand
  271. {
  272.     void OnCommand(string[] commandText);
  273.  
  274. }
  275.  
  276.  
  277. //https://dev.twitch.tv/docs/irc  PRIVMSG
  278. // EXAMPLE:
  279. // @badges=<badges>;color=<color>;display-name=<display-name>;emotes=<emotes>;id=<id-of-msg>;mod=<mod>;room-id=<room-id>;
  280. // subscriber=<subscriber>;tmi-sent-ts=<timestamp>;turbo=<turbo>;user-id=<user-id>;user-type=<user-type>
  281. // :<user>!<user>@<user>.tmi.twitch.tv PRIVMSG #<channel> :<message>
  282. //
  283. //Decomposing chat message from a user
  284. [System.Serializable]
  285. public class LineData
  286. {
  287.     public Color color = Color.white;
  288.     public string colorHex = "";
  289.     public string displayName = "";
  290.     public bool mod = false;
  291.     public bool subscriber = false;
  292.     public bool turbo = false;
  293.  
  294.     public string badges = "";
  295.     public string emotes = "";
  296.     public string id = "";
  297.     public string roomID = "";
  298.     public string tmi_timestamp = "";
  299.     public string userID = "";
  300.     public string userType = "";
  301.  
  302.     public bool emoteOnly = false;
  303.     public string channel = "";
  304.     public string chatText = "";
  305.  
  306.     public LineData(string messageText)
  307.     {
  308.         MatchCollection regexData = Regex.Matches(messageText.Substring(0, messageText.IndexOf("PRIVMSG")), @"(?<key>[\w-]+)(?:=)(?<value>[:\w\,d\/\#\-]+)?");
  309.  
  310.         Dictionary<string, string> matchData = regexData.Cast<Match>().ToDictionary(
  311.                   m => m.Groups["key"].Value,
  312.                   m => m.Groups["value"].Value
  313.                   );
  314.  
  315.         foreach (var item in matchData)
  316.         {
  317.             switch (item.Key)
  318.             {
  319.                 case "badges":
  320.                     badges = item.Value;
  321.                     break;
  322.                 case "display-name":
  323.                     displayName = item.Value;
  324.                     break;
  325.                 case "emotes":
  326.                     emotes = item.Value;
  327.                     break;
  328.                 case "emote-only":
  329.                     emoteOnly = item.Value == "1";
  330.                     break;
  331.                 case "id":
  332.                     id = item.Value;
  333.                     break;
  334.                 case "color":
  335.                     if (item.Value.Length < 7)
  336.                     {
  337.                         color = new Color(218 / 255f, 156 / 255f, 28 / 255f);
  338.                         colorHex = "#" + 218.ToString("X2") + 156.ToString("X2") + 28.ToString("X2");
  339.                     }
  340.                     else
  341.                     {
  342.                         colorHex = item.Value;
  343.                         int r = Convert.ToInt32(item.Value.Substring(1, 2), 16);
  344.                         int g = Convert.ToInt32(item.Value.Substring(3, 2), 16);
  345.                         int b = Convert.ToInt32(item.Value.Substring(5, 2), 16);
  346.                         color = new Color(r / 255f, g / 255f, b / 255f);
  347.                     }
  348.                     break;
  349.  
  350.                 case "mod":
  351.                     mod = item.Value == "1";
  352.                     break;
  353.  
  354.                 case "room-id":
  355.                     roomID = item.Value;
  356.                     break;
  357.  
  358.                 case "tmi-sent-ts":
  359.                     tmi_timestamp = item.Value;
  360.                     break;
  361.  
  362.                 case "turbo":
  363.                     turbo = item.Value == "1";
  364.                     break;
  365.                 case "user-id":
  366.                     userID = item.Value;
  367.                     break;
  368.                 case "user-type":
  369.                     userType = item.Value;
  370.                     break;
  371.             }
  372.         }
  373.  
  374.         var chatMatch = Regex.Match(messageText, @"(?:PRIVMSG.)#(\w+) :(.+)");
  375.  
  376.         if (chatMatch.Success)
  377.         {
  378.             channel = chatMatch.Groups[1].Value;
  379.             chatText = chatMatch.Groups[2].Value;
  380.         }
  381.     }
  382.  
  383.     string getAfterEquals(string str)
  384.     {
  385.         return str.Substring(str.IndexOf('=')+1);
  386.     }
  387.  
  388.     public interface ITwitchCommandEvents : IEventSystemHandler
  389.     {
  390.         // functions that can be called via the messaging system
  391.         void OnChatTwitchCommand(LineData message);
  392.         void Message2();
  393.     }
  394.  
  395. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement