Prepare server to incoming split
This commit is contained in:
parent
85981b4d99
commit
3839455f42
7 changed files with 7 additions and 7 deletions
241
server/DCC.py
Normal file
241
server/DCC.py
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
# -*- 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 imp
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
import message
|
||||
import server
|
||||
|
||||
#Store all used ports
|
||||
PORTS = list()
|
||||
|
||||
class DCC(server.Server):
|
||||
def __init__(self, srv, dest, socket=None):
|
||||
server.Server.__init__(self)
|
||||
|
||||
self.error = False # An error has occur, closing the connection?
|
||||
self.messages = list() # Message queued before connexion
|
||||
|
||||
# Informations about the sender
|
||||
self.sender = dest
|
||||
if self.sender is not None:
|
||||
self.nick = (self.sender.split('!'))[0]
|
||||
if self.nick != self.sender:
|
||||
self.realname = (self.sender.split('!'))[1]
|
||||
else:
|
||||
self.realname = self.nick
|
||||
|
||||
# Keep the server
|
||||
self.srv = srv
|
||||
self.treatement = self.treat_msg
|
||||
|
||||
# Found a port for the connection
|
||||
self.port = self.foundPort()
|
||||
|
||||
if self.port is None:
|
||||
print ("No more available slot for DCC connection")
|
||||
self.setError("Il n'y a plus de place disponible sur le serveur"
|
||||
" pour initialiser une session DCC.")
|
||||
|
||||
def foundPort(self):
|
||||
"""Found a free port for the connection"""
|
||||
for p in range(65432, 65535):
|
||||
if p not in PORTS:
|
||||
PORTS.append(p)
|
||||
return p
|
||||
return None
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
"""Gives the server identifiant"""
|
||||
return self.srv.id + "/" + self.sender
|
||||
|
||||
def setError(self, msg):
|
||||
self.error = True
|
||||
self.srv.send_msg_usr(self.sender, msg)
|
||||
|
||||
def accept_user(self, host, port):
|
||||
"""Accept a DCC connection"""
|
||||
self.s = socket.socket()
|
||||
try:
|
||||
self.s.connect((host, port))
|
||||
print ('Accepted user from', host, port, "for", self.sender)
|
||||
self.connected = True
|
||||
self.stop = False
|
||||
except:
|
||||
self.connected = False
|
||||
self.error = True
|
||||
return False
|
||||
self.start()
|
||||
return True
|
||||
|
||||
|
||||
def request_user(self, type="CHAT", filename="CHAT", size=""):
|
||||
"""Create a DCC connection"""
|
||||
#Open the port
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
s.bind(('', self.port))
|
||||
except:
|
||||
try:
|
||||
self.port = self.foundPort()
|
||||
s.bind(('', self.port))
|
||||
except:
|
||||
self.setError("Une erreur s'est produite durant la tentative"
|
||||
" d'ouverture d'une session DCC.")
|
||||
return False
|
||||
print ('Listen on', self.port, "for", self.sender)
|
||||
|
||||
#Send CTCP request for DCC
|
||||
self.srv.send_ctcp(self.sender,
|
||||
"DCC %s %s %d %d %s" % (type, filename, self.srv.ip,
|
||||
self.port, size),
|
||||
"PRIVMSG")
|
||||
|
||||
s.listen(1)
|
||||
#Waiting for the client
|
||||
(self.s, addr) = s.accept()
|
||||
print ('Connected by', addr)
|
||||
self.connected = True
|
||||
return True
|
||||
|
||||
def send_dcc_raw(self, line):
|
||||
self.s.sendall(line + b'\n')
|
||||
|
||||
def send_dcc(self, msg, to = None):
|
||||
"""If we talk to this user, send a message through this connection
|
||||
else, send the message to the server class"""
|
||||
if to is None or to == self.sender or to == self.nick:
|
||||
if self.error:
|
||||
self.srv.send_msg_final(self.nick, msg)
|
||||
elif not self.connected or self.s is None:
|
||||
try:
|
||||
self.start()
|
||||
except RuntimeError:
|
||||
pass
|
||||
self.messages.append(msg)
|
||||
else:
|
||||
for line in msg.split("\n"):
|
||||
self.send_dcc_raw(line.encode())
|
||||
else:
|
||||
self.srv.send_dcc(msg, to)
|
||||
|
||||
def send_file(self, filename):
|
||||
"""Send a file over DCC"""
|
||||
if os.path.isfile(filename):
|
||||
self.messages = filename
|
||||
try:
|
||||
self.start()
|
||||
except RuntimeError:
|
||||
pass
|
||||
else:
|
||||
print("File not found `%s'" % filename)
|
||||
|
||||
def run(self):
|
||||
self.stopping.clear()
|
||||
|
||||
# Send file connection
|
||||
if not isinstance(self.messages, list):
|
||||
self.request_user("SEND",
|
||||
os.path.basename(self.messages),
|
||||
os.path.getsize(self.messages))
|
||||
if self.connected:
|
||||
with open(self.messages, 'rb') as f:
|
||||
d = f.read(268435456) #Packets size: 256Mo
|
||||
while d:
|
||||
self.s.sendall(d)
|
||||
self.s.recv(4) #The client send a confirmation after each packet
|
||||
d = f.read(268435456) #Packets size: 256Mo
|
||||
|
||||
# Messages connection
|
||||
else:
|
||||
if not self.connected:
|
||||
if not self.request_user():
|
||||
#TODO: do something here
|
||||
return False
|
||||
|
||||
#Start by sending all queued messages
|
||||
for mess in self.messages:
|
||||
self.send_dcc(mess)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
readbuffer = b''
|
||||
self.nicksize = len(self.srv.nick)
|
||||
self.Bnick = self.srv.nick.encode()
|
||||
while not self.stop:
|
||||
raw = self.s.recv(1024) #recieve server messages
|
||||
if not raw:
|
||||
break
|
||||
readbuffer = readbuffer + raw
|
||||
temp = readbuffer.split(b'\n')
|
||||
readbuffer = temp.pop()
|
||||
|
||||
for line in temp:
|
||||
self.treatement(line)
|
||||
|
||||
if self.connected:
|
||||
self.s.close()
|
||||
self.connected = False
|
||||
|
||||
#Remove from DCC connections server list
|
||||
if self.realname in self.srv.dcc_clients:
|
||||
del self.srv.dcc_clients[self.realname]
|
||||
|
||||
print ("Closing connection with", self.nick)
|
||||
self.stopping.set()
|
||||
if self.closing_event is not None:
|
||||
self.closing_event()
|
||||
#Rearm Thread
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
def treat_msg(self, line):
|
||||
"""Treat a receive message, *can be overwritten*"""
|
||||
if line == b'NEMUBOT###':
|
||||
bot = self.srv.add_networkbot(self.srv, self.sender, self)
|
||||
self.treatement = bot.treat_msg
|
||||
self.send_dcc("NEMUBOT###")
|
||||
elif (line[:self.nicksize] == self.Bnick and
|
||||
line[self.nicksize+1:].strip()[:10] == b'my name is'):
|
||||
name = line[self.nicksize+1:].strip()[11:].decode('utf-8',
|
||||
'replace')
|
||||
if re.match("^[a-zA-Z0-9_-]+$", name):
|
||||
if name not in self.srv.dcc_clients:
|
||||
del self.srv.dcc_clients[self.sender]
|
||||
self.nick = name
|
||||
self.sender = self.nick + "!" + self.realname
|
||||
self.srv.dcc_clients[self.realname] = self
|
||||
self.send_dcc("Hi " + self.nick)
|
||||
else:
|
||||
self.send_dcc("This nickname is already in use"
|
||||
", please choose another one.")
|
||||
else:
|
||||
self.send_dcc("The name you entered contain"
|
||||
" invalid char.")
|
||||
else:
|
||||
self.srv.treat_msg(
|
||||
(":%s PRIVMSG %s :" % (
|
||||
self.sender,self.srv.nick)).encode() + line,
|
||||
True)
|
||||
295
server/IRC.py
Normal file
295
server/IRC.py
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
# -*- 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 errno
|
||||
import os
|
||||
import ssl
|
||||
import socket
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
from channel import Channel
|
||||
from server.DCC import DCC
|
||||
from hooks import Hook
|
||||
import message
|
||||
import server
|
||||
import xmlparser
|
||||
|
||||
class IRCServer(server.Server):
|
||||
"""Class to interact with an IRC server"""
|
||||
|
||||
def __init__(self, node, nick, owner, realname, ssl=False):
|
||||
"""Initialize an IRC server
|
||||
|
||||
Arguments:
|
||||
node -- server node from XML configuration
|
||||
nick -- nick used by the bot on this server
|
||||
owner -- nick used by the bot owner on this server
|
||||
realname -- string used as realname on this server
|
||||
ssl -- require SSL?
|
||||
"""
|
||||
server.Server.__init__(self)
|
||||
|
||||
self.node = node
|
||||
|
||||
self.nick = nick
|
||||
self.owner = owner
|
||||
self.realname = realname
|
||||
self.ssl = ssl
|
||||
|
||||
# Listen private messages?
|
||||
self.listen_nick = True
|
||||
|
||||
self.dcc_clients = dict()
|
||||
|
||||
self.channels = dict()
|
||||
for chn in self.node.getNodes("channel"):
|
||||
chan = Channel(chn["name"], chn["password"])
|
||||
self.channels[chan.name] = chan
|
||||
|
||||
|
||||
@property
|
||||
def host(self):
|
||||
"""Return the server hostname"""
|
||||
if self.node is not None and self.node.hasAttribute("server"):
|
||||
return self.node["server"]
|
||||
else:
|
||||
return "localhost"
|
||||
|
||||
@property
|
||||
def port(self):
|
||||
"""Return the connection port used on this server"""
|
||||
if self.node is not None and self.node.hasAttribute("port"):
|
||||
return self.node.getInt("port")
|
||||
else:
|
||||
return "6667"
|
||||
|
||||
@property
|
||||
def password(self):
|
||||
"""Return the password used to connect to this server"""
|
||||
if self.node is not None and self.node.hasAttribute("password"):
|
||||
return self.node["password"]
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def allow_all(self):
|
||||
"""If True, treat message from all channels, not only listed one"""
|
||||
return (self.node is not None and self.node.hasAttribute("allowall")
|
||||
and self.node["allowall"] == "true")
|
||||
|
||||
@property
|
||||
def autoconnect(self):
|
||||
"""Autoconnect the server when added"""
|
||||
if self.node is not None and self.node.hasAttribute("autoconnect"):
|
||||
value = self.node["autoconnect"].lower()
|
||||
return value != "no" and value != "off" and value != "false"
|
||||
else:
|
||||
return False
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
"""Gives the server identifiant"""
|
||||
return self.host + ":" + str(self.port)
|
||||
|
||||
def register_hooks(self):
|
||||
self.add_hook(Hook(self.evt_channel, "JOIN"))
|
||||
self.add_hook(Hook(self.evt_channel, "PART"))
|
||||
self.add_hook(Hook(self.evt_server, "NICK"))
|
||||
self.add_hook(Hook(self.evt_server, "QUIT"))
|
||||
self.add_hook(Hook(self.evt_channel, "332"))
|
||||
self.add_hook(Hook(self.evt_channel, "353"))
|
||||
|
||||
def evt_server(self, msg, srv):
|
||||
for chan in self.channels:
|
||||
self.channels[chan].treat(msg.cmd, msg)
|
||||
|
||||
def evt_channel(self, msg, srv):
|
||||
if msg.channel is not None:
|
||||
if msg.channel in self.channels:
|
||||
self.channels[msg.channel].treat(msg.cmd, msg)
|
||||
|
||||
def accepted_channel(self, chan, sender=None):
|
||||
"""Return True if the channel (or the user) is authorized"""
|
||||
if self.allow_all:
|
||||
return True
|
||||
elif self.listen_nick:
|
||||
return (chan in self.channels and (sender is None or sender in
|
||||
self.channels[chan].people)
|
||||
) or chan == self.nick
|
||||
else:
|
||||
return chan in self.channels and (sender is None or sender
|
||||
in self.channels[chan].people)
|
||||
|
||||
def join(self, chan, password=None, force=False):
|
||||
"""Join a channel"""
|
||||
if force or (chan is not None and
|
||||
self.connected and chan not in self.channels):
|
||||
self.channels[chan] = Channel(chan, password)
|
||||
if password is not None:
|
||||
self.s.send(("JOIN %s %s\r\n" % (chan, password)).encode())
|
||||
else:
|
||||
self.s.send(("JOIN %s\r\n" % chan).encode())
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def leave(self, chan):
|
||||
"""Leave a channel"""
|
||||
if chan is not None and self.connected and chan in self.channels:
|
||||
if isinstance(chan, list):
|
||||
for c in chan:
|
||||
self.leave(c)
|
||||
else:
|
||||
self.s.send(("PART %s\r\n" % self.channels[chan].name).encode())
|
||||
del self.channels[chan]
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# Main loop
|
||||
def run(self):
|
||||
if not self.connected:
|
||||
self.s = socket.socket() #Create the socket
|
||||
if self.ssl:
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
self.s = ctx.wrap_socket(self.s)
|
||||
try:
|
||||
self.s.connect((self.host, self.port)) #Connect to server
|
||||
except socket.error as e:
|
||||
self.s = None
|
||||
print ("\033[1;31mError:\033[0m Unable to connect to %s:%d: %s"
|
||||
% (self.host, self.port, os.strerror(e.errno)))
|
||||
return
|
||||
self.stopping.clear()
|
||||
|
||||
if self.password != None:
|
||||
self.s.send(b"PASS " + self.password.encode () + b"\r\n")
|
||||
self.s.send(("NICK %s\r\n" % self.nick).encode ())
|
||||
self.s.send(("USER %s %s bla :%s\r\n" % (self.nick, self.host,
|
||||
self.realname)).encode())
|
||||
raw = self.s.recv(1024)
|
||||
if not raw:
|
||||
print ("Unable to connect to %s:%d" % (self.host, self.port))
|
||||
return
|
||||
self.connected = True
|
||||
print ("Connection to %s:%d completed" % (self.host, self.port))
|
||||
|
||||
if len(self.channels) > 0:
|
||||
for chn in self.channels.keys():
|
||||
self.join(self.channels[chn].name,
|
||||
self.channels[chn].password, force=True)
|
||||
|
||||
|
||||
readbuffer = b'' #Here we store all the messages from server
|
||||
try:
|
||||
while not self.stop:
|
||||
readbuffer = readbuffer + raw
|
||||
temp = readbuffer.split(b'\n')
|
||||
readbuffer = temp.pop()
|
||||
|
||||
for line in temp:
|
||||
self.treat_msg(line)
|
||||
raw = self.s.recv(1024) #recieve server messages
|
||||
except socket.error:
|
||||
pass
|
||||
|
||||
if self.connected:
|
||||
self.s.close()
|
||||
self.connected = False
|
||||
if self.closing_event is not None:
|
||||
self.closing_event()
|
||||
print ("Server `%s' successfully stopped." % self.id)
|
||||
self.stopping.set()
|
||||
# Rearm Thread
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
|
||||
# Overwritted methods
|
||||
|
||||
def disconnect(self):
|
||||
"""Close the socket with the server and all DCC client connections"""
|
||||
#Close all DCC connection
|
||||
clts = [c for c in self.dcc_clients]
|
||||
for clt in clts:
|
||||
self.dcc_clients[clt].disconnect()
|
||||
return server.Server.disconnect(self)
|
||||
|
||||
|
||||
|
||||
# Abstract methods
|
||||
|
||||
def send_pong(self, cnt):
|
||||
"""Send a PONG command to the server with argument cnt"""
|
||||
self.s.send(("PONG %s\r\n" % cnt).encode())
|
||||
|
||||
def msg_treated(self, origin):
|
||||
"""Do nothing; here for implement abstract class"""
|
||||
pass
|
||||
|
||||
def send_dcc(self, msg, to):
|
||||
"""Send a message through DCC connection"""
|
||||
if msg is not None and to is not None:
|
||||
realname = to.split("!")[1]
|
||||
if realname not in self.dcc_clients.keys():
|
||||
d = DCC(self, to)
|
||||
self.dcc_clients[realname] = d
|
||||
self.dcc_clients[realname].send_dcc(msg)
|
||||
|
||||
def send_msg_final(self, channel, line, cmd="PRIVMSG", endl="\r\n"):
|
||||
"""Send a message without checks or format"""
|
||||
#TODO: add something for post message treatment here
|
||||
if channel == self.nick:
|
||||
print ("\033[1;35mWarning:\033[0m Nemubot talks to himself: %s" % msg)
|
||||
traceback.print_stack()
|
||||
if line is not None and channel is not None:
|
||||
if self.s is None:
|
||||
print ("\033[1;35mWarning:\033[0m Attempt to send message on a non connected server: %s: %s" % (self.id, line))
|
||||
traceback.print_stack()
|
||||
elif len(line) < 442:
|
||||
self.s.send (("%s %s :%s%s" % (cmd, channel, line, endl)).encode ())
|
||||
else:
|
||||
print ("\033[1;35mWarning:\033[0m Message truncated due to size (%d ; max : 442) : %s" % (len(line), line))
|
||||
traceback.print_stack()
|
||||
self.s.send (("%s %s :%s%s" % (cmd, channel, line[0:442]+"<…>", endl)).encode ())
|
||||
|
||||
def send_msg_usr(self, user, msg):
|
||||
"""Send a message to a user instead of a channel"""
|
||||
if user is not None and user[0] != "#":
|
||||
realname = user.split("!")[1]
|
||||
if realname in self.dcc_clients or user in self.dcc_clients:
|
||||
self.send_dcc(msg, user)
|
||||
else:
|
||||
for line in msg.split("\n"):
|
||||
if line != "":
|
||||
self.send_msg_final(user.split('!')[0], msg)
|
||||
|
||||
def send_msg(self, channel, msg, cmd="PRIVMSG", endl="\r\n"):
|
||||
"""Send a message to a channel"""
|
||||
if self.accepted_channel(channel):
|
||||
server.Server.send_msg(self, channel, msg, cmd, endl)
|
||||
|
||||
def send_msg_verified(self, sender, channel, msg, cmd = "PRIVMSG", endl = "\r\n"):
|
||||
"""Send a message to a channel, only if the source user is on this channel too"""
|
||||
if self.accepted_channel(channel, sender):
|
||||
self.send_msg_final(channel, msg, cmd, endl)
|
||||
|
||||
def send_global(self, msg, cmd="PRIVMSG", endl="\r\n"):
|
||||
"""Send a message to all channels on this server"""
|
||||
for channel in self.channels.keys():
|
||||
self.send_msg(channel, msg, cmd, endl)
|
||||
169
server/__init__.py
Normal file
169
server/__init__.py
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
# -*- 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 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()
|
||||
|
||||
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" % self.nick).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
|
||||
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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue