1
0
Fork 0
nemubot/server/__init__.py

174 lines
5.6 KiB
Python

# -*- coding: utf-8 -*-
# Nemubot is a modulable IRC bot, built around XML configuration files.
# Copyright (C) 2012 Mercier Pierre-Olivier
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
import socket
import threading
class Server(threading.Thread):
def __init__(self, socket = None):
self.stop = False
self.stopping = threading.Event()
self.s = socket
self.connected = self.s is not None
self.closing_event = None
self.moremessages = dict()
self.logger = logging.getLogger(__name__ + "/" + self.id)
threading.Thread.__init__(self)
def isDCC(self, to=None):
return to is not None and to in self.dcc_clients
@property
def ip(self):
"""Convert common IP representation to little-endian integer representation"""
sum = 0
if self.node.hasAttribute("ip"):
ip = self.node["ip"]
else:
#TODO: find the external IP
ip = "0.0.0.0"
for b in ip.split("."):
sum = 256 * sum + int(b)
return sum
def toIP(self, input):
"""Convert little-endian int to IPv4 adress"""
ip = ""
for i in range(0,4):
mod = input % 256
ip = "%d.%s" % (mod, ip)
input = (input - mod) / 256
return ip[:len(ip) - 1]
@property
def id(self):
"""Gives the server identifiant"""
raise NotImplemented()
def accepted_channel(self, msg, sender=None):
return True
def msg_treated(self, origin):
"""Action done on server when a message was treated"""
raise NotImplemented()
def send_response(self, res, origin):
"""Analyse a Response and send it"""
# TODO: how to send a CTCP message to a different person
if res.ctcp:
self.send_ctcp(res.sender, res.get_message())
elif res.channel is not None and res.channel != self.nick:
self.send_msg(res.channel, res.get_message())
if not res.alone:
if hasattr(self, "send_bot"):
self.send_bot("NOMORE %s" % res.channel)
self.moremessages[res.channel] = res
elif res.sender is not None:
self.send_msg_usr(res.sender, res.get_message())
if not res.alone:
self.moremessages[res.sender] = res
def send_ctcp(self, to, msg, cmd="NOTICE", endl="\r\n"):
"""Send a message as CTCP response"""
if msg is not None and to is not None:
for line in msg.split("\n"):
if line != "":
self.send_msg_final(to.split("!")[0], "\x01" + line + "\x01", cmd, endl)
def send_dcc(self, msg, to):
"""Send a message through DCC connection"""
raise NotImplemented()
def send_msg_final(self, channel, msg, cmd="PRIVMSG", endl="\r\n"):
"""Send a message without checks or format"""
raise NotImplemented()
def send_msg_usr(self, user, msg):
"""Send a message to a user instead of a channel"""
raise NotImplemented()
def send_msg(self, channel, msg, cmd="PRIVMSG", endl="\r\n"):
"""Send a message to a channel"""
if msg is not None:
for line in msg.split("\n"):
if line != "":
self.send_msg_final(channel, line, cmd, endl)
def send_msg_verified(self, sender, channel, msg, cmd="PRIVMSG", endl="\r\n"):
"""A more secure way to send messages"""
raise NotImplemented()
def send_global(self, msg, cmd="PRIVMSG", endl="\r\n"):
"""Send a message to all channels on this server"""
raise NotImplemented()
def disconnect(self):
"""Close the socket with the server"""
if self.connected:
self.stop = True
try:
self.s.shutdown(socket.SHUT_RDWR)
except socket.error:
pass
self.stopping.wait()
return True
else:
return False
def kill(self):
"""Just stop the main loop, don't close the socket directly"""
if self.connected:
self.stop = True
self.connected = False
#Send a message in order to close the socket
try:
self.s.send(("Bye!\r\n").encode ())
except:
pass
self.stopping.wait()
return True
else:
return False
def launch(self, receive_action, verb=True):
"""Connect to the server if it is no yet connected"""
self._receive_action = receive_action
if not self.connected:
self.stop = False
self.logger.info("Entering main loop for server")
try:
self.start()
except RuntimeError:
pass
elif verb:
print (" Already connected.")
def treat_msg(self, line, private=False):
self._receive_action(self, line, private)
def run(self):
raise NotImplemented()