Advertisement
NeoGriever

Untitled

Dec 19th, 2020
81
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.88 KB | None | 0 0
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Text.RegularExpressions;
  5. using UnityEditor;
  6. using UnityEngine.Events;
  7. using System.Net.Sockets;
  8. using System.Threading;
  9. using System.IO;
  10. using System.Diagnostics;
  11.  
  12. [System.Serializable]
  13. public class TwitchChat : MonoBehaviour {
  14. public Twitch twitchBox = new Twitch();
  15. public Twitch.MsgEvent inbox = new Twitch.MsgEvent();
  16. [SerializeField] public string un = "";
  17. [SerializeField] public string pw = "";
  18. [SerializeField] public string[] ch;
  19. [SerializeField] public bool logg = true;
  20. [SerializeField] public bool ac = false;
  21. [SerializeField] public CommandEntry[] commands;
  22. public void Start() {
  23. twitchBox.nickname = un;
  24. twitchBox.oauth = pw;
  25. twitchBox.channels = ch;
  26. twitchBox.logging = logg;
  27. twitchBox.messageRecievedEvent.AddListener(internalInbox);
  28. if(ac) {twitchBox.StartTwitchChat();}
  29. }
  30. public void Update() {
  31. twitchBox.Tick();
  32. }
  33. private void internalInbox(Twitch.MSGInfo msg) {
  34. if(twitchBox.logging) {
  35. UnityEngine.Debug.Log(msg.channel + " > " + msg.name + ": " + msg.message);
  36. }
  37. foreach(CommandEntry i in commands) {
  38. Regex r = new Regex("^" + i.commandTrigger);
  39. if(r.IsMatch(msg.message)) {
  40. if(twitchBox.logging) {
  41. UnityEngine.Debug.Log(msg.channel + " > " + msg.name + " used " + i.commandName);
  42. }
  43. i.action.Invoke();
  44. }
  45. }
  46. inbox.Invoke(msg);
  47. }
  48. public void SendChatMsg(string msg) {
  49. twitchBox.SendMsg(msg);
  50. }
  51. public void OnDisable() {
  52. twitchBox.StopTwitchChat();
  53. }
  54. public void OnDestroy() {
  55. twitchBox.StopTwitchChat();
  56. }
  57. }
  58. [System.Serializable]
  59. public class CommandEntry {
  60. [SerializeField] public string commandName;
  61. [SerializeField] public string commandTrigger;
  62. [SerializeField] public UnityEvent action;
  63. public CommandEntry (string name) {
  64. commandName = name;
  65. }
  66. }
  67.  
  68. [CustomEditor(typeof(TwitchChat))]
  69. public class TwitchChatEditor : Editor {
  70. Twitch th;
  71. string msg;
  72. string[] msgl;
  73. GUIStyle style;
  74. bool isActive;
  75. bool wasActive;
  76. bool showOauth;
  77. bool showMultichat;
  78. bool showchE;
  79. bool showcoE;
  80. public void DrawStringArray(SerializedProperty property, string foldoutName, ref bool fold) {
  81. GUILayout.BeginHorizontal();
  82. fold = EditorGUILayout.Foldout(fold, new GUIContent(foldoutName), true);
  83. var arraySizeProp = property.FindPropertyRelative("Array.size");
  84. if(fold) {
  85. EditorGUILayout.LabelField("Size", GUILayout.Width(50));
  86. arraySizeProp.intValue = EditorGUILayout.IntField(arraySizeProp.intValue, GUILayout.Width(70));
  87. GUILayout.EndHorizontal();
  88. if(arraySizeProp.intValue < 1) {
  89. EditorGUILayout.LabelField("No entries", style);
  90. }
  91. for (var i = 0; i < arraySizeProp.intValue; i++) {
  92. GUILayout.BeginHorizontal();
  93. EditorGUILayout.LabelField("", GUILayout.Width(10));
  94. EditorGUILayout.LabelField(i.ToString(), GUILayout.Width(40));
  95. property.GetArrayElementAtIndex(i).stringValue = EditorGUILayout.TextField(property.GetArrayElementAtIndex(i).stringValue);
  96. GUILayout.EndHorizontal();
  97. }
  98. } else {
  99. EditorGUILayout.LabelField(arraySizeProp.intValue.ToString(), GUILayout.Width(68));
  100. GUILayout.EndHorizontal();
  101. }
  102. }
  103. public void DrawPropertyArray(SerializedProperty property, string foldoutName, ref bool fold) {
  104. GUILayout.BeginHorizontal();
  105. fold = EditorGUILayout.Foldout(fold, new GUIContent(foldoutName), true);
  106. var arraySizeProp = property.FindPropertyRelative("Array.size");
  107. if (fold) {
  108. EditorGUILayout.LabelField("Size", GUILayout.Width(50));
  109. arraySizeProp.intValue = EditorGUILayout.IntField(arraySizeProp.intValue, GUILayout.Width(70));
  110. GUILayout.EndHorizontal();
  111. if(arraySizeProp.intValue < 1) {
  112. EditorGUILayout.LabelField("No entries", style);
  113. }
  114. for (var i = 0; i < arraySizeProp.intValue; i++) {
  115. GUILayout.BeginHorizontal();
  116. EditorGUILayout.LabelField("", GUILayout.Width(10));
  117. EditorGUILayout.PropertyField(property.GetArrayElementAtIndex(i), new GUIContent(i.ToString()));
  118. GUILayout.EndHorizontal();
  119. }
  120. } else {
  121. EditorGUILayout.LabelField(arraySizeProp.intValue.ToString(), GUILayout.Width(68));
  122. GUILayout.EndHorizontal();
  123. }
  124. }
  125. public void addUserLoginFields() {
  126. GUILayout.BeginHorizontal();
  127. EditorGUILayout.LabelField("User:", GUILayout.Width(80));
  128. serializedObject.FindProperty("un").stringValue = EditorGUILayout.TextField(serializedObject.FindProperty("un").stringValue);
  129. GUILayout.EndHorizontal();
  130.  
  131. GUILayout.BeginHorizontal();
  132. EditorGUILayout.LabelField("OAuth:", GUILayout.Width(80));
  133. showOauth = EditorGUILayout.Toggle(GUIContent.none, showOauth, GUILayout.Width(30));
  134. if(showOauth) {
  135. serializedObject.FindProperty("pw").stringValue = EditorGUILayout.TextField(serializedObject.FindProperty("pw").stringValue);
  136. } else {
  137. serializedObject.FindProperty("pw").stringValue = EditorGUILayout.PasswordField(serializedObject.FindProperty("pw").stringValue);
  138. }
  139. if(GUILayout.Button("TMI")) {
  140. Application.OpenURL("https://www.twitchapps.com/tmi/");
  141. }
  142. GUILayout.EndHorizontal();
  143. GUILayout.Space(4);
  144. EditorGUILayout.LabelField("Be careful! Checkbox reveals oauth field!", style);
  145. GUILayout.Space(10);
  146. }
  147. public void addChannelArrayFields() {
  148. DrawStringArray(chE, "Channel list:", ref showchE);
  149. }
  150. public void addCommandArrayFields() {
  151. DrawPropertyArray(coE, "Commands:", ref showcoE);
  152. }
  153. public void addDebugOption() {
  154. GUILayout.BeginHorizontal();
  155. serializedObject.FindProperty("logg").boolValue = EditorGUILayout.Toggle(serializedObject.FindProperty("logg").boolValue, GUILayout.Width(20));
  156. EditorGUILayout.LabelField(new GUIContent("Debug Log"), GUILayout.Width(100));
  157. EditorGUILayout.LabelField("Show every action from chat inside unity log.", style);
  158. GUILayout.EndHorizontal();
  159. GUILayout.Space(4);
  160. }
  161. public void addConfigArea() {
  162. isActive = EditorGUILayout.Foldout(isActive, "Configuration");
  163. EditorGUI.indentLevel = 1;
  164. if(isActive) {
  165. addDebugOption();
  166. addUserLoginFields();
  167. addChannelArrayFields();
  168. addCommandArrayFields();
  169. }
  170. EditorGUI.indentLevel = 0;
  171. }
  172. public void addOnTheRunHandling() {
  173. GUI.enabled = true;if (!EditorApplication.isPlaying && !Application.isPlaying) {GUI.enabled = false;}
  174. GUILayout.Space(15);
  175.  
  176. GUILayout.BeginHorizontal();
  177. if(th.isConnected) {
  178. if(GUILayout.Button("Disconnect")) {
  179. th.StopTwitchChat();
  180. msg = "";
  181. }
  182. } else {
  183. if(GUILayout.Button("Connect")) {
  184. th.StartTwitchChat();
  185. }
  186. GUI.enabled = true;if (!EditorApplication.isPlaying && !Application.isPlaying) {GUI.enabled = false;}
  187. }
  188. GUI.enabled = true;
  189. serializedObject.FindProperty("ac").boolValue = EditorGUILayout.ToggleLeft("Auto-Connect", serializedObject.FindProperty("ac").boolValue, GUILayout.Width(110));
  190. GUI.enabled = true;if (!EditorApplication.isPlaying && !Application.isPlaying) {GUI.enabled = false;}
  191. GUILayout.EndHorizontal();
  192. if (!EditorApplication.isPlaying && !Application.isPlaying) {
  193. EditorGUILayout.LabelField("On the run controls only available while running", style);
  194. }
  195. GUI.enabled = th.isConnected;
  196. GUILayout.Space(5);
  197. if(msgl == null || chE.arraySize > msgl.Length) {
  198. msgl = new string[chE.arraySize];
  199. }
  200. if(chE.arraySize == 1) {
  201. msg = GUILayout.TextField(msg);
  202. if (GUILayout.Button("Send", GUILayout.Width(60))) {
  203. th.SendMsg(msg);
  204. msg = "";
  205. }
  206. }else{
  207. showMultichat = EditorGUILayout.Foldout(showMultichat, "Channel List (Direct chat)");
  208. if(showMultichat) {
  209. GUILayout.BeginHorizontal();
  210. GUILayout.Label("All", GUILayout.Width(35));
  211. msg = GUILayout.TextField(msg);
  212. if (GUILayout.Button("Send", GUILayout.Width(60))) {
  213. th.SendMsg(msg);
  214. msg = "";
  215. }
  216. GUILayout.EndHorizontal();
  217. for(int chIndex = 0;chIndex < chE.arraySize;chIndex++) {
  218. string chName = chE.GetArrayElementAtIndex(chIndex).stringValue;
  219. GUILayout.BeginHorizontal();
  220. GUILayout.Label("", GUILayout.Width(4));
  221. GUILayout.Label(chName, GUILayout.Width(90));
  222. msgl[chIndex] = GUILayout.TextField(msgl[chIndex]);
  223. if (GUILayout.Button("Send", GUILayout.Width(60))) {
  224. th.SendMsg(msgl[chIndex], chIndex);
  225. msgl[chIndex] = "";
  226. }
  227. GUILayout.EndHorizontal();
  228. }
  229. }
  230. }
  231. GUI.enabled = true;
  232. }
  233. SerializedProperty chE = null;
  234. SerializedProperty coE = null;
  235. public override void OnInspectorGUI() {
  236. serializedObject.Update();
  237. chE = serializedObject.FindProperty("ch");
  238. coE = serializedObject.FindProperty("commands");
  239. th = (serializedObject.targetObject as TwitchChat).twitchBox;
  240. if(style == null) {
  241. style = new GUIStyle();
  242. style.alignment = TextAnchor.MiddleCenter;
  243. style.fontSize = 10;
  244. style.normal.textColor = Color.yellow;
  245. }
  246. if (EditorApplication.isPlaying && Application.isPlaying) {
  247. GUI.enabled = false;
  248. }
  249. if(th != null) {
  250. addConfigArea();
  251. addOnTheRunHandling();
  252. }
  253. if(serializedObject.hasModifiedProperties) {
  254. serializedObject.ApplyModifiedProperties();
  255. }
  256. }
  257. }
  258. public class Twitch {
  259. public string oauth;
  260. public string nickname;
  261. public string[] channels;
  262.  
  263. public class MsgEvent : UnityEvent<MSGInfo> { }
  264. public MsgEvent messageRecievedEvent = new MsgEvent();
  265.  
  266. [HideInInspector]
  267. public string server = "irc.twitch.tv";
  268.  
  269. [HideInInspector]
  270. public int port = 6667;
  271.  
  272. [HideInInspector]
  273. public bool logging = true;
  274.  
  275. private string buffer = string.Empty;
  276. private bool stopThreads = false;
  277.  
  278. private Queue<string> commandQueue = new Queue<string>();
  279. private List<string> recievedMsgs = new List<string>();
  280.  
  281. private Thread inProc, outProc;
  282.  
  283. private Regex r = new Regex("^(.*?)PRIVMSG #(.*?) :(.*)$");
  284. private Regex r2 = new Regex("^(.*?):");
  285.  
  286. [HideInInspector]
  287. public bool isConnected = false;
  288.  
  289. private void StartIRC() {
  290. // start connection
  291. TcpClient sock = new TcpClient();
  292. sock.Connect(server, port);
  293. if (!sock.Connected) {
  294. // connection failed
  295. UnityEngine.Debug.Log("Failed to connect!");
  296. return;
  297. }
  298. // get stream-handlers
  299. NetworkStream networkStream = sock.GetStream();
  300. TextReader input = (new StreamReader(networkStream)) as TextReader;
  301. StreamWriter output = new StreamWriter(networkStream);
  302.  
  303. // do the login
  304. output.WriteLine("PASS " + oauth);
  305. output.WriteLine("NICK " + nickname.ToLower());
  306. // add tags-request
  307. output.WriteLine("CAP REQ :twitch.tv/tags");
  308. output.Flush();
  309.  
  310. // start threats
  311. isConnected = true;
  312. outProc = new Thread(() => IRCOutputProcedure(output));
  313. outProc.Start();
  314. inProc = new Thread(() => IRCInputProcedure(input, networkStream));
  315. inProc.Start();
  316. }
  317. private void IRCInputProcedure(TextReader input, NetworkStream networkStream) {
  318. while (!stopThreads) {
  319. if (!networkStream.DataAvailable) {
  320. continue;
  321. }
  322. buffer = input.ReadLine();
  323. if (buffer.Contains("PRIVMSG #")) {
  324. lock (recievedMsgs) {
  325. recievedMsgs.Add(buffer);
  326. }
  327. }
  328. if (buffer.StartsWith("PING ")) {
  329. SendCommand(buffer.Replace("PING", "PONG"));
  330. }
  331. if (buffer.Split(' ')[1] == "001") {
  332. for(int i = 0;i < channels.Length;i++) {
  333. SendCommand("JOIN #" + channels[i]);
  334. }
  335. }
  336. }
  337. isConnected = false;
  338. }
  339. private void IRCOutputProcedure(TextWriter output) {
  340. Stopwatch stopWatch = new Stopwatch();
  341. stopWatch.Start();
  342. while (!stopThreads) {
  343. lock (commandQueue) {
  344. if (commandQueue.Count > 0) {
  345. if (stopWatch.ElapsedMilliseconds > 1750) {
  346. output.WriteLine(commandQueue.Peek());
  347. output.Flush();
  348. commandQueue.Dequeue();
  349. stopWatch.Reset();
  350. stopWatch.Start();
  351. }
  352. }
  353. }
  354. }
  355. isConnected = false;
  356. }
  357. public void SendCommand(string cmd) {
  358. lock (commandQueue) {
  359. commandQueue.Enqueue(cmd);
  360. }
  361. }
  362. public void SendMsg(string msg) {
  363. for(int i = 0;i < channels.Length;i++) {
  364. SendMsg(msg, i);
  365. }
  366. }
  367. public void SendMsg(string msg, int channelIndex) {
  368. lock (commandQueue) {
  369. commandQueue.Enqueue("PRIVMSG #" + channels[channelIndex] + " :" + msg);
  370. }
  371. MSGInfo mi = new MSGInfo(new string[] {"color=#ff0000", "display-name=" + nickname, "subscriber=0", "mod=0"}, msg, channels[channelIndex]);
  372. messageRecievedEvent.Invoke(mi);
  373. }
  374. public void StartTwitchChat() {
  375. stopThreads = false;
  376. StartIRC();
  377. }
  378. public void StopTwitchChat() {
  379. stopThreads = true;
  380. }
  381. public void Tick() {
  382. lock (recievedMsgs) {
  383. if (recievedMsgs.Count > 0) {
  384. for (int i = 0; i < recievedMsgs.Count; i++) {
  385. handleMsg(recievedMsgs[i]);
  386. }
  387. recievedMsgs.Clear();
  388. }
  389. }
  390. }
  391. private void handleMsg(string msg) {
  392. Match match = r.Match(msg);
  393. string infoBlock = match.Groups[1].Value;
  394. Match match2 = r2.Match(infoBlock);
  395. MSGInfo mi = new MSGInfo(match2.Groups[1].Value.Split(';'), match.Groups[3].Value, match.Groups[2].Value);
  396. messageRecievedEvent.Invoke(mi);
  397. }
  398. public class MSGInfo {
  399. public string channel {get; private set;}
  400. public string name {get; private set;}
  401. public Color color {get; private set;}
  402. public bool isSubscriber {get; private set;}
  403. public bool isModerator {get; private set;}
  404. public string message {get; private set;}
  405. public MSGInfo(string[] data, string txt, string chn) {
  406. channel = chn;
  407. message = txt;
  408. string colorData = "";
  409. foreach(string j in data) {
  410. string[] v = j.Split('=');
  411. if(v[0] == "display-name") {
  412. name = v[1];
  413. } else if(v[0] == "color") {
  414. colorData = v[1];
  415. } else if(v[0] == "subscriber") {
  416. isSubscriber = (v[1] == "1");
  417. } else if(v[0] == "mod") {
  418. isModerator = (v[1] == "1");
  419. }
  420. }
  421. color = cfu(name);
  422. if(colorData != "") {
  423. Color genColor = color;
  424. if(ColorUtility.TryParseHtmlString(colorData, out genColor)) {
  425. color = genColor;
  426. }
  427. }
  428. }
  429. private Color cfu(string username) {
  430. Random.InitState(username.Length + (int)username[0] + (int)username[username.Length - 1]);
  431. return new Color(Random.Range(0.25f, 0.55f), Random.Range(0.20f, 0.55f), Random.Range(0.25f, 0.55f));
  432. }
  433. }
  434. }
  435.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement