Advertisement
NeoGriever

Untitled

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