From 65aacbcf2a78c446df5f0dca48271d76cbe8d195 Mon Sep 17 00:00:00 2001 From: MennoMax Date: Thu, 20 May 2021 23:43:16 +0200 Subject: [PATCH] Update README.md, missing changes --- ...n.png-487276ed1e3a0c39cad0279d744ee560.md5 | 3 - ....png-487276ed1e3a0c39cad0279d744ee560.stex | Bin 3480 -> 0 bytes ...n.png-b5cf707f4ba91fefa5df60a746e02900.md5 | 3 - ....png-b5cf707f4ba91fefa5df60a746e02900.stex | Bin 381 -> 0 bytes ChatContainer.gd | 10 +-- Gift.gd | 61 ++++++++---------- README.md | 4 ++ addons/gift/gift_node.gd | 1 - addons/gift/icon.png | Bin 363 -> 2090 bytes addons/gift/icon.png.import | 0 icon.png | Bin 3433 -> 2201 bytes 11 files changed, 36 insertions(+), 46 deletions(-) delete mode 100644 .import/icon.png-487276ed1e3a0c39cad0279d744ee560.md5 delete mode 100644 .import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex delete mode 100644 .import/icon.png-b5cf707f4ba91fefa5df60a746e02900.md5 delete mode 100644 .import/icon.png-b5cf707f4ba91fefa5df60a746e02900.stex mode change 100755 => 100644 addons/gift/icon.png.import diff --git a/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.md5 b/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.md5 deleted file mode 100644 index f75f542..0000000 --- a/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.md5 +++ /dev/null @@ -1,3 +0,0 @@ -source_md5="8dd9ff1eebf38898a54579d8c01b0a88" -dest_md5="da70afec3c66d4e872db67f808e12edb" - diff --git a/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex b/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex deleted file mode 100644 index 46e6d2aa00f753b8d41a94d2b00c13c9954b627c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3480 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 W;?v%%@8to5jlt8^&t;ucLK6Vsx0W0L diff --git a/ChatContainer.gd b/ChatContainer.gd index 0f986d2..d98a390 100644 --- a/ChatContainer.gd +++ b/ChatContainer.gd @@ -13,12 +13,12 @@ func put_chat(senderdata : SenderData, msg : String): for d in data[1].split(","): var start_end = d.split("-") locations.append(EmoteLocation.new(data[0], int(start_end[0]), int(start_end[1]))) - locations.sort_custom(self, "greater") + locations.sort_custom(EmoteLocation, "smaller") var offset = 0 for loc in locations: var emote_string = "[img=center]" + $"../Gift".image_cache.get_emote(loc.id).resource_path +"[/img]" msg = msg.substr(0, loc.start + offset) + emote_string + msg.substr(loc.end + offset + 1) - offset += emote_string.length() - loc.end - loc.start - 1 + offset += emote_string.length() + loc.start - loc.end - 1 var bottom : bool = $Chat/ScrollContainer.scroll_vertical == $Chat/ScrollContainer.get_v_scrollbar().max_value - $Chat/ScrollContainer.get_v_scrollbar().rect_size.y msgnode.set_msg(str(time["hour"]) + ":" + ("0" + str(time["minute"]) if time["minute"] < 10 else str(time["minute"])), senderdata, msg, badges) $Chat/ScrollContainer/ChatMessagesContainer.add_child(msgnode) @@ -26,9 +26,6 @@ func put_chat(senderdata : SenderData, msg : String): if (bottom): $Chat/ScrollContainer.scroll_vertical = $Chat/ScrollContainer.get_v_scrollbar().max_value -func smaller(a : EmoteLocation, b : EmoteLocation): - return a.start < b.start - class EmoteLocation extends Reference: var id : String var start : int @@ -38,3 +35,6 @@ class EmoteLocation extends Reference: self.id = emote_id self.start = start_idx self.end = end_idx + + static func smaller(a : EmoteLocation, b : EmoteLocation): + return a.start < b.start diff --git a/Gift.gd b/Gift.gd index 3592dc8..241152c 100644 --- a/Gift.gd +++ b/Gift.gd @@ -1,7 +1,19 @@ extends Gift func _ready() -> void: - connect("cmd_no_permission", self, "no_permission") + # I use a file in the working directory to store auth data + # so that I don't accidentally push it to the repository. + # Replace this or create a auth file with 3 lines in your + # project directory: + # + # + # + var authfile := File.new() + authfile.open("./auth", File.READ) + var botname := authfile.get_line() + var token := authfile.get_line() + var initial_channel = authfile.get_line() + connect_to_twitch() yield(self, "twitch_connected") @@ -9,31 +21,34 @@ func _ready() -> void: # You will have to either get a oauth token yourself or use # https://twitchapps.com/tokengen/ # to generate a token with custom scopes. - authenticate_oauth("", "") + authenticate_oauth(botname, token) if(yield(self, "login_attempt") == false): print("Invalid username or token.") return - join_channel("") + join_channel(initial_channel) + + connect("cmd_no_permission", get_parent(), "no_permission") + connect("chat_message", get_parent(), "chat_message") # Adds a command with a specified permission flag. # All implementations must take at least one arg for the command info. # Implementations that recieve args requrires two args, # the second arg will contain all params in a PoolStringArray # This command can only be executed by VIPS/MODS/SUBS/STREAMER - add_command("test", self, "command_test", 0, 0, PermissionFlag.NON_REGULAR) + add_command("test", get_parent(), "command_test", 0, 0, PermissionFlag.NON_REGULAR) # These two commands can be executed by everyone - add_command("helloworld", self, "hello_world") - add_command("greetme", self, "greet_me") + add_command("helloworld", get_parent(), "hello_world") + add_command("greetme", get_parent(), "greet_me") # This command can only be executed by the streamer - add_command("streamer_only", self, "streamer_only", 0, 0, PermissionFlag.STREAMER) + add_command("streamer_only", get_parent(), "streamer_only", 0, 0, PermissionFlag.STREAMER) # Command that requires exactly 1 arg. - add_command("greet", self, "greet", 1, 1) + add_command("greet", get_parent(), "greet", 1, 1) # Command that prints every arg seperated by a comma (infinite args allowed), at least 2 required - add_command("list", self, "list", -1, 2) + add_command("list", get_parent(), "list", -1, 2) # Adds a command alias add_alias("test","test1") @@ -55,32 +70,10 @@ func _ready() -> void: # Send a chat message to the only connected channel () # Fails, if connected to more than one channel. - chat("TEST") +# chat("TEST") # Send a chat message to channel - chat("TEST", "") +# chat("TEST", initial_channel) # Send a whisper to target user - whisper("TEST", "") - -# Check the CommandInfo class for the available info of the cmd_info. -func command_test(cmd_info : CommandInfo) -> void: - print("A") - -func hello_world(cmd_info : CommandInfo) -> void: - chat("HELLO WORLD!") - -func streamer_only(cmd_info : CommandInfo) -> void: - chat("Streamer command executed") - -func no_permission(cmd_info : CommandInfo) -> void: - chat("NO PERMISSION!") - -func greet(cmd_info : CommandInfo, arg_ary : PoolStringArray) -> void: - chat("Greetings, " + arg_ary[0]) - -func greet_me(cmd_info : CommandInfo) -> void: - chat("Greetings, " + cmd_info.sender_data.tags["display-name"] + "!") - -func list(cmd_info : CommandInfo, arg_ary : PoolStringArray) -> void: - chat(arg_ary.join(", ")) \ No newline at end of file +# whisper("TEST", initial_channel) diff --git a/README.md b/README.md index 6e16268..eec9a6b 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,10 @@ Godot IRC For Twitch addon *** +Below is a working example of this plugin, which is included in this project. A replication of the twitch chat. + +![image](https://user-images.githubusercontent.com/12477395/119052327-b9fc9980-b9c4-11eb-8f45-a2a8f2d98977.png) + ### Examples The following code is also [included](https://github.com/MennoMax/gift/blob/master/Gift.gd) in this repository. diff --git a/addons/gift/gift_node.gd b/addons/gift/gift_node.gd index b454c4a..4b76685 100644 --- a/addons/gift/gift_node.gd +++ b/addons/gift/gift_node.gd @@ -81,7 +81,6 @@ func _ready() -> void: websocket.connect("connection_error", self, "connection_error") if(get_images): image_cache = ImageCache.new(disk_cache, disk_cache_path) -# add_child(image_cache) func connect_to_twitch() -> void: if(websocket.connect_to_url("wss://irc-ws.chat.twitch.tv:443") != OK): diff --git a/addons/gift/icon.png b/addons/gift/icon.png index b72c82db7ee549777dd19a28138619ae36f123ba..ca073b333d39fd1a27368c8e5d18e35f917f4a27 100755 GIT binary patch delta 2031 zcmVaB^>EX>4U6ba`-PAZ2)IW&i+q+PzmO1SBDr<6xGExxpNN9*dpMs;ug)o{lk*MrM)3dlHbQ{quJ>f8k+{);JB(C+{sD zmt5iuA=>q%`6lb;dwa5Chi`1w-nK(cs?>L z54ql*L_4o`a$8YqOYkX%yQ{}mRQAB>%Ko{_tyPV0^VAT4QdJld<|ZU|zT@&h#*ZYU zR=go&9Em_yVKX~uD+3@;%ew%ycYvNiJ|*)J{Y&})KIh#oAF%WrBM2`$NI$g8WrpvD zaIulE{zZ)Iv7kqLDx07S5@aE1g7SWlwl5e&?M2slO%l;Jsx z;{yzkDpxqkPQB)Fi#hY(^&AxDWWYV6YubDZEw{8o85my!$}?7m`Z~&Q0N-5cDlHGF!vU3M&nz&@voROgStP#oB?&8d3(WH9p_w` zjIAsjm|8*hp})m!IOcJv<^Nj$&x*R!A@EUyh@E1zQd*w#F3}y0nyQZrf51rLiDUmZBn6c^fVsNjTjyaHq!Ybx*}jUvU!) z)Tb11&ic5Qfb3qr7Rk2dZO4@kjGmx>jeqDL;%UO{EL(pFTZjC-!m z6rn<>6nNgNw7S4z7YA_yOYP=A`H%CH^ldw21NGxF7HC zJ?`EC{ziqVX4eFuYL<~sCVzx%ZdK@hMG!;iLmW|wnfjb4rrU5saWpZjz4 zD|wRvK7n|a>4rtTK|H-_>74h8!>lAJ#OK8023?T&k?XR{Z=8z``*~*A$fW0q!^A?d zgXIopB|{~iB917kM*04%%L?Z$&T6H`TKD8H4CS?zWvoO54hX`hM#oFkQ^yM(_bh6?`QN)IbiS> z=v{MrYwhFo0mx8SsbCx6;1GxwDSO@H-94SX{d=a>-w&r`a=i3iJG=k@00v@9M??Vs z0RI60puMM)00009a7bBm000XU000XU0RWnu7ytkO2XskIMF-^t6$>;0uGhXdf(+Y6aYv4wn;W&iu|`TATCrPaZFS_ui?rPb>KAOXJL7k>aOXA`cx8@xQI$mJ+r9+1hD z%SD38ktN6+p)(rO9u5$`i|}3A!vUSqm^!Sqzh#vr==GNUW=A#NW>=9`S#p4vO$ITm z*b6>O;N1ALfD^}&D6M*Km_~9;Ybq7$Os4ctkTaPcTe<0&?5I1YANyW_*Rq|AP5=M^ N07*qoLPx$B}qg3_69P4aB&dYy5x39Z-_Aw zq4b;X;eOwD{%{WbCzJ|}QlUA;ahikx{oBVp1_Kbsk*IP-ozTkyF76%m&-;dAN=UgZ&70joF zk?>twy)FO};QM`l0l;!L;mW(g%Ys5|Tk*00EmJNR31yA6q;dBl}<40E&I)mqP@+o5Uq4_z&|pm%TgWiSqA6g&jwB$N5Zb^xhOA^V;_)j qq?RfD6JjmX<5jMr=>qGH=?D8>fY)D2ro;dM002ovPDHK)LSTXq_KDp9 diff --git a/addons/gift/icon.png.import b/addons/gift/icon.png.import old mode 100755 new mode 100644 diff --git a/icon.png b/icon.png index 2b658158b6a2211b656d83fee35e3941e5ad376a..43962e304bb24a66eedfbcb461715d953d280d31 100644 GIT binary patch delta 2190 zcmV;92yyr68krH0BYy<aB^>EX>4U6ba`-PAZ2)IW&i+q+Pzm>lH@21 z{O1&V1SBCI$H6=z_6B?WO&)f+x~qGtXD8N66Pi(c0S`mAmc}p zQ7WFOV;qT~uEGWd32$Q{UTs4qFt%G9aNvQPm;$BhF@<^pj>Ta>_wiwvFIxT$lHBvm5xE8Gm^bHW!E znts#tS17`BB@AVPF{u0XZNBUL$!&TsgCer=;tIHUM1K}w4CTfXqaXCAz55#}H#oF(*!fHBggJA;pwZ&TKLqW!TCX zpV4ygA%{Ek@JBe}k&avtKE)L+zJwA>D!Fo%ie;ku8fvVm=0>JrrD(2c^DVU4Qp+8& z)}_0y-S^OAPd#6%ZB;+rKTwUfYCM%1hGsS~l0q3Eb!cNM4aFd#YsA{=((a+$Te=yIZ|TOrQqBzN{)BP{(tXzLg=%%| zb7d;FvT$%}1=)xG7PH})$EKG5YxzGL>P|<6j~YbmtVS!P<=NjE>!0}pU}kCFbAH&} z*ncI)b0{=gh)}KOHPD~qGT-3fqU4mc#(=HHt(TTk!_qkF$g)RiW`)X8*Dy~lCtZ-6 zNGjKqB`;SciaTB1O0(uw$**~7Dc84c*Lo<81&Oi@6{*VGaq&pP2@5ASs&S+4skn(1 zC$T_%)&llfANLfH-3x1xY+K%bT6iMFZg*xA+P z4Ukf4Cu)t$!C_m(L2JKNk&<7qxFDYHnwQeAIdr{(%55Eo;b?*GL(OCCJx-r#zJ=o>LRXfj@h2ir`A6`{PB`0= z#8Xf^_O|WcgOfG8!I2N?6OHCM?tk8({EN^uh|M!yZh>|S(H73EIm*k`dM%{pyOs1O ze4D4SU-SKH8MyJCnpsnbtv|B;x7arNhuAjd53z0ZeYQiYdkk`cT0HhdoA=Yu0RL(g z{u$e*{eW%Vi*76}eB5x%_j~>OM+)=14F3?(`zJ7SgZ!)V+mny^>;xy+gnte1Ul1o$ zx;ip;bN~PWglR)VP)S2WAaHVTW@&6?004NLeUUv#!$2IxUt5c!6$dMbIAo}uEQl3x z)G8FALZ}s5buhW~3z{?}DK3tJYr(;f#j1mgv#t)Vf*|+-;^yY0=prTlFDbN$@!+^0 z@9sVB-U0qbg{fxO1fXh`k$+AmglukA=zc{IL+C>sQHh!QoG7N?Ilk`U}f(S1DkIihOTMET+BgcSe)02uEB zme*HB`76@E`{lW<@!EL~Q016MXwB>SumS)80NVpTZ)ZvS=%_!D4Ya|vkEh->MzcFn zer%n*P=Q=8zQt000#9fYft@zFTi+j2 zM+gZ3!1q2NmTRkXiu8YVgTfQg{gW$<1OSi}*}u_?pF delta 3431 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 JPDHLkV1mG6(2M{8