Advertisement
NeoGriever

Untitled

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