################################################################################# # This file is part of Spheres. # Copyright (C) 2019 Jesusalva # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ################################################################################# # Chat controller # TODO: Pub interface, using irc_send() and irc_buffer init python: irc=None irc_online=False def irc_san(txt): return txt.replace("[", "(").replace("]", ")").replace("\n", "").replace("{", "(").replace("}", ")").replace("%", "pc.") def irc_open(): if not persistent.irc_enable: return global irc, irc_buffer, irc_nick, irc_auth, irc_channel, irc_online import socket, sys, random reload(sys) sys.setdefaultencoding('utf8') irc_online=False server = "irc.libera.chat" irc_channel = "#mana-spheres" # TODO: Replace this with some uuid4()? irc_nick = "GS_" + get_token()[:3] + str(random.randint(1, 10000)) irc_auth=IRC_AUTH_NONE irc_buffer=[] irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) stdout("\nConnecting to:" + server) irc.connect((server, 6667)) return def irc_receive(raw, irc_sender=""): global irc_buffer if irc_sender == "": sender="IRC" # Control buffer length if (len(irc_buffer) >= MAX_IRC_BUFFER): nil=irc_buffer.pop(0) del nil # Find sender if not supplied try: p1=raw.find('<')+1 p2=raw.find('>') if p2 <= p1: raise Exception("Invalid sender") sender=raw[p1:p2] except: try: p1=raw.find("!") sender="IRC."+raw[1:p1] except: sender="IRC" else: sender = irc_sender # Extract message try: p1=raw.find(" :") # Perhaps we're looking for ">" instead msg=raw[p1:] except: msg="Unparseable" # UNIX format msg=msg.replace('\r', '') irc_buffer.append((irc_san(sender), irc_san(msg))) return def irc_send(sender, msg): global irc, irc_channel, irc_online if not irc_online: return False try: irc.send("PRIVMSG %s :<%s> %s\n" % (irc_channel, sender, msg)) irc_buffer.append((irc_san(sender), irc_san(msg))) return True except: return False def irc_kill(): global irc, irc_online if irc_online: irc.send("QUIT :I have to go for now!\n") stdout("[IRC] Closing connection normally...") irc.shutdown(2) irc.close() irc_online=False stdout("[IRC] IRC Bridge is now Offline.") #del irc irc=None return def irc_loop(): if not persistent.irc_enable: return global irc, irc_buffer, irc_nick, irc_auth, irc_channel, irc_online if irc is None: irc_open() try: while 1: text = irc.recv(2048) if len(text) > 0: if debug: stdout("[IRC] %s" % text) else: continue # Handle ping requests if text.find("PING") != -1: irc.send("PONG " + text.split()[1] + "\n") # Authentication: Step 1 if irc_auth == IRC_AUTH_NONE: irc.send("USER " + irc_nick + " " + irc_nick + " " + irc_nick + " :This is a fun bot\n") irc_auth=IRC_AUTH_USER continue # Authentication: Step 2 if irc_auth == IRC_AUTH_USER: irc.send("NICK " + irc_nick + "\n") irc_auth=IRC_AUTH_NICK continue # Authentication: Step 3 if text.find("255 " + irc_nick) != -1: irc.send("JOIN " + irc_channel + "\n") if text.find("376 " + irc_nick) != -1: if irc_auth == IRC_AUTH_NICK: irc_auth=IRC_AUTH_CHAN irc_online=True else: stdout("[IRC] Erroneous Authentication on 376 message") irc_online=True # Would be nice to show MOTD if text.find("372 " + irc_nick) != -1: for line in text.split("\n"): if "372" in line: print("[372] %s" % line) irc_receive(line, "MOTD") if irc_auth == IRC_AUTH_CHAN: # IRC Syntax # :nick!hostname PRIVMSG #tmw2-spheres : msg # We should use , not :nick! # TODO: Handle CTCP ACTION if text.find(irc_channel) != -1: if "PRIVMSG" in text: irc_receive(text) # Handle disconnections if not irc_online: raise Exception("Connection terminated.") finally: try: irc.send("QUIT :I have to go for now!\n") irc.shutdown(2) irc.close() except: stdout("[IRC] [ERROR] Connection was already closed.") stdout("\n") stdout("Closing IRC connection...") irc=None return screen pub(): frame: background Frame("gui/frame.png", 0, 0) xalign 0.5 yalign 1.0 ymargin 15 ypadding 10 xmargin 10 ymaximum 0.82 vbox: spacing 30 textbutton _("Exit") action Return("") #label _("PUB RAID - NOT YET IMPLEMENTED") label _("IRC Lobby - %s at %s" % (irc_channel, "libera.chat")) null height 20 viewport: #child_size (0.4, 0.8) mousewheel True draggable True arrowkeys True xfill False ymaximum 0.75 scrollbars "vertical" vbox: xalign 0.5 ymaximum 0.75 spacing 5 label _("%s" % persistent.nickname) null height 40 showif irc_online: for chat in irc_buffer: label _("%s: {size=22}%s{/size}" % (chat[0], chat[1].replace("<%s>" % chat[0], ""))) null height 40 input: id "msgmsg" color ((128, 128, 128, 220)) italic True size 26 copypaste True allow "qwertyuiopasdfghjklzxcvbnm QWERTYUIOPASDFGHJKKLZXCVBNM,.?!+-=:;'()" textbutton _("Send") action None # Function(irc_send) else: if persistent.irc_enable: label _("Connecting...") else: label _("IRC Integration is not active.") # FIXME inform if IRC is disabled # Ask user to comply with Libera Chat TOS etc. timer 0.5 action Function(renpy.restart_interaction) repeat True