Ripple's bancho server https://ripple.moe

ircserver.py 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  1. """
  2. This file has been _rewritten_ taking by reference code from
  3. miniircd (https://github.com/jrosdahl/miniircd)
  4. by Joel Rosdahl, licensed under the GNU GPL 2 License.
  5. Most of the reference code from miniircd was used for the low-level logic.
  6. The high-level code has been rewritten to make it compatible with pep.py.
  7. """
  8. import hashlib
  9. import re
  10. import select
  11. import socket
  12. import sys
  13. import time
  14. import traceback
  15. import raven
  16. from common.log import logUtils as log
  17. from common.ripple import userUtils
  18. from helpers import chatHelper as chat
  19. from objects import glob
  20. class Client:
  21. __linesep_regexp = re.compile(r"\r?\n")
  22. def __init__(self, server, sock):
  23. """
  24. Initialize a Client object
  25. :param server: server object
  26. :param sock: socket connection object
  27. :return:
  28. """
  29. self.__timestamp = time.time()
  30. self.__readbuffer = ""
  31. self.__writebuffer = ""
  32. self.__sentPing = False
  33. self.__handleCommand = self.passHandler
  34. self.server = server
  35. self.socket = sock
  36. (self.ip, self.port) = sock.getpeername()
  37. self.IRCUsername = ""
  38. self.banchoUsername = ""
  39. self.supposedUsername = ""
  40. self.supposedUserID = 0
  41. self.joinedChannels = []
  42. def messageChannel(self, channel, command, message, includeSelf=False):
  43. line = ":{} {}".format(command, message)
  44. for _, value in self.server.clients.items():
  45. if channel in value.joinedChannels and (value != self or includeSelf):
  46. value.message(line)
  47. def message(self, msg):
  48. """
  49. Add a message (basic string) to client buffer.
  50. This is the lowest possible level.
  51. :param msg: message to add
  52. :return:
  53. """
  54. self.__writebuffer += msg + "\r\n"
  55. def writeBufferSize(self):
  56. """
  57. Return this client's write buffer size
  58. :return: write buffer size
  59. """
  60. return len(self.__writebuffer)
  61. def reply(self, msg):
  62. """
  63. Add an IRC-like message to client buffer.
  64. :param msg: message (without IRC stuff)
  65. :return:
  66. """
  67. self.message(":{} {}".format(self.server.host, msg))
  68. def replyCode(self, code, message, nickname="", channel=""):
  69. """
  70. Add an IRC-like message to client buffer with code
  71. :param code: response code
  72. :param message: response message
  73. :param nickname: receiver nickname
  74. :param channel: optional
  75. :return:
  76. """
  77. if nickname == "":
  78. nickname = self.IRCUsername
  79. if channel != "":
  80. channel = " "+channel
  81. self.reply("{code:03d} {nickname}{channel} :{message}".format(code=code, nickname=nickname, channel=channel, message=message))
  82. def reply403(self, channel):
  83. """
  84. Add a 403 reply (no such channel) to client buffer.
  85. :param channel:
  86. :return:
  87. """
  88. self.replyCode(403, "{} :No such channel".format(channel))
  89. def reply461(self, command):
  90. """
  91. Add a 461 reply (not enough parameters) to client buffer
  92. :param command: name of the command that had not enough parameters
  93. :return:
  94. """
  95. self.replyCode(403, "{} :Not enough parameters".format(command))
  96. def disconnect(self, quitmsg = "Client quit", callLogout = True):
  97. """
  98. Disconnects this client from the IRC server
  99. :param quitmsg: IRC quit message. Default: 'Client quit'
  100. :param callLogout: if True, call logoutEvent on bancho
  101. :return:
  102. """
  103. # Send error to client and close socket
  104. self.message("ERROR :{}".format(quitmsg))
  105. self.socket.close()
  106. log.info("[IRC] Disconnected connection from {}:{} ({})".format(self.ip, self.port, quitmsg))
  107. # Remove socket from server
  108. self.server.removeClient(self, quitmsg)
  109. # Bancho logout
  110. if callLogout and self.banchoUsername != "":
  111. chat.IRCDisconnect(self.IRCUsername)
  112. def readSocket(self):
  113. """
  114. Read data coming from this client socket
  115. :return:
  116. """
  117. try:
  118. # Try to read incoming data from socket
  119. data = self.socket.recv(2 ** 10)
  120. log.debug("[IRC] [{}:{}] -> {}".format(self.ip, self.port, data))
  121. quitmsg = "EOT"
  122. except socket.error as x:
  123. # Error while reading data, this client will be disconnected
  124. data = bytes()
  125. quitmsg = x
  126. if data:
  127. # Parse received data if needed
  128. self.__readbuffer += data.decode("latin_1")
  129. self.parseBuffer()
  130. self.__timestamp = time.time()
  131. self.__sentPing = False
  132. else:
  133. # No data, disconnect this socket
  134. self.disconnect(quitmsg)
  135. def parseBuffer(self):
  136. """
  137. Parse self.__readbuffer, get command, arguments and call its handler
  138. :return:
  139. """
  140. # Get lines from buffer
  141. lines = self.__linesep_regexp.split(self.__readbuffer)
  142. self.__readbuffer = lines[-1]
  143. lines = lines[:-1]
  144. # Process every line
  145. for line in lines:
  146. if not line:
  147. # Empty line. Ignore.
  148. continue
  149. # Get arguments
  150. x = line.split(" ", 1)
  151. # Command is the first argument, always uppercase
  152. command = x[0].upper()
  153. if len(x) == 1:
  154. # Command only, no arguments
  155. arguments = []
  156. else:
  157. # We have some arguments
  158. # Weird sorcery
  159. if len(x[1]) > 0 and x[1][0] == ":":
  160. arguments = [x[1][1:]]
  161. else:
  162. y = x[1].split(" :", 1)
  163. arguments = y[0].split()
  164. if len(y) == 2:
  165. arguments.append(y[1])
  166. # Handle command with its arguments
  167. self.__handleCommand(command, arguments)
  168. def writeSocket(self):
  169. """
  170. Write buffer to socket
  171. :return:
  172. """
  173. try:
  174. sent = self.socket.send(self.__writebuffer.encode())
  175. log.debug("[IRC] [{}:{}] <- {}".format(self.ip, self.port, self.__writebuffer[:sent]))
  176. self.__writebuffer = self.__writebuffer[sent:]
  177. except socket.error as x:
  178. self.disconnect(str(x))
  179. def checkAlive(self):
  180. """
  181. Check if this client is still connected.
  182. If the client is dead, disconnect it.
  183. :return:
  184. """
  185. now = time.time()
  186. if self.__timestamp + 180 < now:
  187. self.disconnect("ping timeout")
  188. return
  189. if not self.__sentPing and self.__timestamp + 90 < now:
  190. if self.__handleCommand == self.mainHandler:
  191. # Registered.
  192. self.message("PING :{}".format(self.server.host))
  193. self.__sentPing = True
  194. else:
  195. # Not registered.
  196. self.disconnect("ping timeout")
  197. def sendLusers(self):
  198. """
  199. Send lusers response to this client
  200. :return:
  201. """
  202. self.replyCode(251, "There are {} users and 0 services on 1 server".format(len(glob.tokens.tokens)))
  203. def sendMotd(self):
  204. """
  205. Send MOTD to this client
  206. :return:
  207. """
  208. self.replyCode(375, "- {} Message of the day - ".format(self.server.host))
  209. if len(self.server.motd) == 0:
  210. self.replyCode(422, "MOTD File is missing")
  211. else:
  212. for i in self.server.motd:
  213. self.replyCode(372, "- {}".format(i))
  214. self.replyCode(376, "End of MOTD command")
  215. """""""""
  216. HANDLERS
  217. """""""""
  218. def dummyHandler(self, command, arguments):
  219. pass
  220. def passHandler(self, command, arguments):
  221. """PASS command handler"""
  222. if command == "PASS":
  223. if len(arguments) == 0:
  224. self.reply461("PASS")
  225. else:
  226. # IRC token check
  227. m = hashlib.md5()
  228. m.update(arguments[0].encode("utf-8"))
  229. tokenHash = m.hexdigest()
  230. supposedUser = glob.db.fetch("SELECT users.username, users.id FROM users LEFT JOIN irc_tokens ON users.id = irc_tokens.userid WHERE irc_tokens.token = %s LIMIT 1", [tokenHash])
  231. if supposedUser:
  232. self.supposedUsername = chat.fixUsernameForIRC(supposedUser["username"])
  233. self.supposedUserID = supposedUser["id"]
  234. self.__handleCommand = self.registerHandler
  235. else:
  236. # Wrong IRC Token
  237. self.reply("464 :Password incorrect")
  238. elif command == "QUIT":
  239. self.disconnect()
  240. def registerHandler(self, command, arguments):
  241. """NICK and USER commands handler"""
  242. if command == "NICK":
  243. if len(arguments) < 1:
  244. self.reply("431 :No nickname given")
  245. return
  246. nick = arguments[0]
  247. # Make sure this is the first time we set our nickname
  248. if self.IRCUsername != "":
  249. self.reply("432 * %s :Erroneous nickname" % nick)
  250. return
  251. # Make sure the IRC token was correct:
  252. # (self.supposedUsername is already fixed for IRC)
  253. if nick.lower() != self.supposedUsername.lower():
  254. self.reply("464 :Password incorrect")
  255. return
  256. # Make sure that the user is not banned/restricted:
  257. if not userUtils.isAllowed(self.supposedUserID):
  258. self.reply("465 :You're banned")
  259. return
  260. # Make sure we are not connected to Bancho
  261. token = glob.tokens.getTokenFromUsername(chat.fixUsernameForBancho(nick), True)
  262. if token is not None:
  263. self.reply("433 * {} :Nickname is already in use".format(nick))
  264. return
  265. # Everything seems fine, set username (nickname)
  266. self.IRCUsername = nick # username for IRC
  267. self.banchoUsername = chat.fixUsernameForBancho(self.IRCUsername) # username for bancho
  268. # Disconnect other IRC clients from the same user
  269. for _, value in self.server.clients.items():
  270. if value.IRCUsername.lower() == self.IRCUsername.lower() and value != self:
  271. value.disconnect(quitmsg="Connected from another client")
  272. return
  273. elif command == "USER":
  274. # Ignore USER command, we use nickname only
  275. return
  276. elif command == "QUIT":
  277. # Disconnect if we have received a QUIT command
  278. self.disconnect()
  279. return
  280. else:
  281. # Ignore any other command while logging in
  282. return
  283. # If we now have a valid username, connect to bancho and send IRC welcome stuff
  284. if self.IRCUsername != "":
  285. # Bancho connection
  286. chat.IRCConnect(self.banchoUsername)
  287. # IRC reply
  288. self.replyCode(1, "Welcome to the Internet Relay Network")
  289. self.replyCode(2, "Your host is {}, running version pep.py-{}".format(self.server.host, glob.VERSION))
  290. self.replyCode(3, "This server was created since the beginning")
  291. self.replyCode(4, "{} pep.py-{} o o".format(self.server.host, glob.VERSION))
  292. self.sendLusers()
  293. self.sendMotd()
  294. self.__handleCommand = self.mainHandler
  295. def quitHandler(self, _, arguments):
  296. """QUIT command handler"""
  297. self.disconnect(self.IRCUsername if len(arguments) < 1 else arguments[0])
  298. def joinHandler(self, _, arguments):
  299. """JOIN command handler"""
  300. if len(arguments) < 1:
  301. self.reply461("JOIN")
  302. return
  303. # Get bancho token object
  304. token = glob.tokens.getTokenFromUsername(self.banchoUsername)
  305. if token is None:
  306. return
  307. # TODO: Part all channels
  308. if arguments[0] == "0":
  309. '''for (channelname, channel) in self.channels.items():
  310. self.message_channel(channel, "PART", channelname, True)
  311. self.channel_log(channel, "left", meta=True)
  312. server.remove_member_from_channel(self, channelname)
  313. self.channels = {}
  314. return'''
  315. return
  316. # Get channels to join list
  317. channels = arguments[0].split(",")
  318. for channel in channels:
  319. # Make sure we are not already in that channel
  320. # (we already check this bancho-side, but we need to do it
  321. # also here k maron)
  322. if channel.lower() in token.joinedChannels:
  323. continue
  324. # Attempt to join the channel
  325. response = chat.IRCJoinChannel(self.banchoUsername, channel)
  326. if response == 0:
  327. # Joined successfully
  328. self.joinedChannels.append(channel)
  329. # Let everyone in this channel know that we've joined
  330. self.messageChannel(channel, "{} JOIN".format(self.IRCUsername), channel, True)
  331. # Send channel description (topic)
  332. description = glob.channels.channels[channel].description
  333. if description == "":
  334. self.replyCode(331, "No topic is set", channel=channel)
  335. else:
  336. self.replyCode(332, description, channel=channel)
  337. # Build connected users list
  338. if "chat/{}".format(channel) not in glob.streams.streams:
  339. self.reply403(channel)
  340. continue
  341. users = glob.streams.streams["chat/{}".format(channel)].clients
  342. usernames = []
  343. for user in users:
  344. if user not in glob.tokens.tokens:
  345. continue
  346. usernames.append(chat.fixUsernameForIRC(glob.tokens.tokens[user].username))
  347. usernames = " ".join(usernames)
  348. # Send IRC users list
  349. self.replyCode(353, usernames, channel="= {}".format(channel))
  350. self.replyCode(366, "End of NAMES list", channel=channel)
  351. elif response == 403:
  352. # Channel doesn't exist (or no read permissions)
  353. self.reply403(channel)
  354. continue
  355. def partHandler(self, _, arguments):
  356. """PART command handler"""
  357. if len(arguments) < 1:
  358. self.reply461("PART")
  359. return
  360. # Get bancho token object
  361. token = glob.tokens.getTokenFromUsername(self.banchoUsername)
  362. if token is None:
  363. return
  364. # Get channels to part list
  365. channels = arguments[0].split(",")
  366. for channel in channels:
  367. # Make sure we in that channel
  368. # (we already check this bancho-side, but we need to do it
  369. # also here k maron)
  370. if channel.lower() not in token.joinedChannels:
  371. continue
  372. # Attempt to part the channel
  373. response = chat.IRCPartChannel(self.banchoUsername, channel)
  374. if response == 0:
  375. # No errors, remove channel from joinedChannels
  376. self.joinedChannels.remove(channel)
  377. elif response == 403:
  378. self.reply403(channel)
  379. elif response == 442:
  380. self.replyCode(442, "You're not on that channel", channel=channel)
  381. def noticePrivmsgHandler(self, command, arguments):
  382. """NOTICE and PRIVMSG commands handler (same syntax)"""
  383. # Syntax check
  384. if len(arguments) == 0:
  385. self.replyCode(411, "No recipient given ({})".format(command))
  386. return
  387. if len(arguments) == 1:
  388. self.replyCode(412, "No text to send")
  389. return
  390. recipientIRC = arguments[0]
  391. message = arguments[1]
  392. # Send the message to bancho and reply
  393. if not recipientIRC.startswith("#"):
  394. recipientBancho = chat.fixUsernameForBancho(recipientIRC)
  395. else:
  396. recipientBancho = recipientIRC
  397. response = chat.sendMessage(self.banchoUsername, recipientBancho, message, toIRC=False)
  398. if response == 404:
  399. self.replyCode(404, "Cannot send to channel", channel=recipientIRC)
  400. return
  401. elif response == 403:
  402. self.replyCode(403, "No such channel", channel=recipientIRC)
  403. return
  404. elif response == 401:
  405. self.replyCode(401, "No such nick/channel", channel=recipientIRC)
  406. return
  407. # Send the message to IRC and bancho
  408. if recipientIRC.startswith("#"):
  409. # Public message (IRC)
  410. if recipientIRC not in glob.channels.channels:
  411. self.replyCode(401, "No such nick/channel", channel=recipientIRC)
  412. return
  413. for _, value in self.server.clients.items():
  414. if recipientIRC in value.joinedChannels and value != self:
  415. value.message(":{} PRIVMSG {} :{}".format(self.IRCUsername, recipientIRC, message))
  416. else:
  417. # Private message (IRC)
  418. for _, value in self.server.clients.items():
  419. if value.IRCUsername == recipientIRC:
  420. value.message(":{} PRIVMSG {} :{}".format(self.IRCUsername, recipientIRC, message))
  421. def motdHandler(self, command, arguments):
  422. """MOTD command handler"""
  423. self.sendMotd()
  424. def lusersHandler(self, command, arguments):
  425. """LUSERS command handler"""
  426. self.sendLusers()
  427. def pingHandler(self, _, arguments):
  428. """PING command handler"""
  429. if len(arguments) < 1:
  430. self.replyCode(409, "No origin specified")
  431. return
  432. self.reply("PONG {} :{}".format(self.server.host, arguments[0]))
  433. def pongHandler(self, command, arguments):
  434. """(fake) PONG command handler"""
  435. pass
  436. def awayHandler(self, _, arguments):
  437. """AWAY command handler"""
  438. response = chat.IRCAway(self.banchoUsername, " ".join(arguments))
  439. self.replyCode(response, "You are no longer marked as being away" if response == 305 else "You have been marked as being away")
  440. def mainHandler(self, command, arguments):
  441. """
  442. Handler for post-login commands
  443. """
  444. handlers = {
  445. "AWAY": self.awayHandler,
  446. #"ISON": ison_handler,
  447. "JOIN": self.joinHandler,
  448. #"LIST": list_handler,
  449. "LUSERS": self.lusersHandler,
  450. #"MODE": mode_handler,
  451. "MOTD": self.motdHandler,
  452. #"NICK": nick_handler,
  453. #"NOTICE": notice_and_privmsg_handler,
  454. "PART": self.partHandler,
  455. "PING": self.pingHandler,
  456. "PONG": self.pongHandler,
  457. "PRIVMSG": self.noticePrivmsgHandler,
  458. "QUIT": self.quitHandler,
  459. #"TOPIC": topic_handler,
  460. #"WALLOPS": wallops_handler,
  461. #"WHO": who_handler,
  462. #"WHOIS": whois_handler,
  463. "USER": self.dummyHandler,
  464. }
  465. try:
  466. handlers[command](command, arguments)
  467. except KeyError:
  468. self.replyCode(421, "Unknown command ({})".format(command))
  469. class Server:
  470. def __init__(self, port):
  471. self.host = glob.conf.config["irc"]["hostname"]
  472. self.port = port
  473. self.clients = {} # Socket - - > Client instance.
  474. self.motd = ["Welcome to pep.py's embedded IRC server!", "This is a VERY simple IRC server and it's still in beta.", "Expect things to crash and not work as expected :("]
  475. def forceDisconnection(self, username, isBanchoUsername=True):
  476. """
  477. Disconnect someone from IRC if connected
  478. :param username: victim
  479. :param isBanchoUsername: if True, username is a bancho username, else convert it to a bancho username
  480. :return:
  481. """
  482. for _, value in self.clients.items():
  483. if (value.IRCUsername == username and not isBanchoUsername) or (value.banchoUsername == username and isBanchoUsername):
  484. value.disconnect(callLogout=False)
  485. break # or dictionary changes size during iteration
  486. def banchoJoinChannel(self, username, channel):
  487. """
  488. Let every IRC client connected to a specific client know that 'username' joined the channel from bancho
  489. :param username: username of bancho user
  490. :param channel: joined channel name
  491. :return:
  492. """
  493. username = chat.fixUsernameForIRC(username)
  494. for _, value in self.clients.items():
  495. if channel in value.joinedChannels:
  496. value.message(":{} JOIN {}".format(username, channel))
  497. def banchoPartChannel(self, username, channel):
  498. """
  499. Let every IRC client connected to a specific client know that 'username' parted the channel from bancho
  500. :param username: username of bancho user
  501. :param channel: joined channel name
  502. :return:
  503. """
  504. username = chat.fixUsernameForIRC(username)
  505. for _, value in self.clients.items():
  506. if channel in value.joinedChannels:
  507. value.message(":{} PART {}".format(username, channel))
  508. def banchoMessage(self, fro, to, message):
  509. """
  510. Send a message to IRC when someone sends it from bancho
  511. :param fro: sender username
  512. :param to: receiver username
  513. :param message: text of the message
  514. :return:
  515. """
  516. fro = chat.fixUsernameForIRC(fro)
  517. to = chat.fixUsernameForIRC(to)
  518. if to.startswith("#"):
  519. # Public message
  520. for _, value in self.clients.items():
  521. if to in value.joinedChannels and value.IRCUsername != fro:
  522. value.message(":{} PRIVMSG {} :{}".format(fro, to, message))
  523. else:
  524. # Private message
  525. for _, value in self.clients.items():
  526. if value.IRCUsername == to and value.IRCUsername != fro:
  527. value.message(":{} PRIVMSG {} :{}".format(fro, to, message))
  528. def removeClient(self, client, _):
  529. """
  530. Remove a client from connected clients
  531. :param client: client object
  532. :return:
  533. """
  534. if client.socket in self.clients:
  535. del self.clients[client.socket]
  536. def start(self):
  537. """
  538. Start IRC server main loop
  539. :return:
  540. """
  541. # Sentry
  542. sentryClient = None
  543. if glob.sentry:
  544. sentryClient = raven.Client(glob.conf.config["sentry"]["ircdns"])
  545. serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  546. serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  547. try:
  548. serversocket.bind(("0.0.0.0", self.port))
  549. except socket.error as e:
  550. log.error("[IRC] Could not bind port {}:{}".format(self.port, e))
  551. sys.exit(1)
  552. serversocket.listen(5)
  553. lastAliveCheck = time.time()
  554. # Main server loop
  555. while True:
  556. try:
  557. (iwtd, owtd, ewtd) = select.select(
  558. [serversocket] + [x.socket for x in self.clients.values()],
  559. [x.socket for x in self.clients.values()
  560. if x.writeBufferSize() > 0],
  561. [],
  562. 1)
  563. # Handle incoming connections
  564. for x in iwtd:
  565. if x in self.clients:
  566. self.clients[x].readSocket()
  567. else:
  568. (conn, addr) = x.accept()
  569. try:
  570. self.clients[conn] = Client(self, conn)
  571. log.info("[IRC] Accepted connection from {}:{}".format(addr[0], addr[1]))
  572. except socket.error:
  573. try:
  574. conn.close()
  575. except:
  576. pass
  577. # Handle outgoing connections
  578. for x in owtd:
  579. if x in self.clients: # client may have been disconnected
  580. self.clients[x].writeSocket()
  581. # Make sure all IRC clients are still connected
  582. now = time.time()
  583. if lastAliveCheck + 10 < now:
  584. for client in list(self.clients.values()):
  585. client.checkAlive()
  586. lastAliveCheck = now
  587. except:
  588. log.error("[IRC] Unknown error!\n```\n{}\n{}```".format(sys.exc_info(), traceback.format_exc()))
  589. if glob.sentry and sentryClient is not None:
  590. sentryClient.captureException()
  591. def main(port=6667):
  592. """
  593. Create and start an IRC server
  594. :param port: IRC port. Default: 6667
  595. :return:
  596. """
  597. glob.ircServer = Server(port)
  598. glob.ircServer.start()