Skip to content

Commit d15799d

Browse files
authored
Version 2.0
A lot of things were changed, and everything is now organized correctly.
1 parent 66885a7 commit d15799d

22 files changed

Lines changed: 309 additions & 143 deletions

README.md

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,25 @@
77
* All settings are located on **classes/config.py**
88

99
# Commands:
10-
* !chatid **[CHAT NAME]**
11-
* !id **[XAT USERNAME]**
12-
* !price **[POWER NAME]**
13-
* !reg **[XAT ID]**
14-
* !info
15-
* !reload
16-
* !latest
10+
* -chatid **[CHAT NAME]**
11+
* -id **[XAT USERNAME]**
12+
* -price **[POWER NAME]**
13+
* -reg **[XAT ID]**
14+
* -steal **[XAT ID]**
15+
* -enable **[PLUGIN NAME]**
16+
* -disable **[PLUGIN NAME]**
17+
* -info
18+
* -reload
19+
* -relogin
20+
* -latest
1721

1822
# Plugins:
23+
* **clickmsg.py**
24+
* Tell you ID of who ticked you.
1925
* **commands.py**
2026
* Handler for all commands.
21-
* **connected.py**
22-
* Just show a message when you connect to xat.
27+
* **guestlinks.py**
28+
* Allows you to see guest links.
2329
* **noclicks.py**
2430
* Disable your clicks on other people.
2531
* **nofollow.py**
@@ -33,7 +39,7 @@
3339
5. This is all, just don't bother me anymore.
3440

3541
# Version:
36-
* 1.4 stable
42+
* 2.0 stable
3743

3844
# Credits:
3945
* xLaming (obvious, im da best)

classes/client.py

Lines changed: 102 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,120 +1,122 @@
1-
from os import path
21
from socket import *
3-
from glob import glob
2+
from time import sleep
43
from select import select
54
from .config import Config
65
from threading import Thread
7-
from .webserver import WebServer
8-
from xml.etree.ElementTree import fromstring
96

107
class Client:
11-
def __init__(self):
12-
self.Threads = []
13-
self.WebServer = WebServer()
14-
self.RunServer = Thread(target=self.WebServer.start)
15-
self.RunServer.daemon = True
16-
self.RunServer.start()
17-
print("> Webserver started.")
18-
self.Plugins = []
19-
self.loadPlugins()
20-
print("> Plugins started.")
21-
self.RunSocket = Thread(target=self.createSocket)
22-
self.RunSocket.daemon = True
23-
self.RunSocket.start()
24-
print("> Socket started.")
25-
26-
def createSocket(self):
8+
def __init__(self, server, sock):
279
try:
28-
xSock = socket(AF_INET, SOCK_STREAM)
29-
xSock.bind((Config.CLIENT_SERVERIP, Config.CLIENT_SERVERPORT))
30-
while True:
31-
xSock.listen(1)
32-
(conn, (ip, port)) = xSock.accept()
33-
thread = Thread(target=self.addClient, args=(conn,))
34-
thread.start()
35-
self.Threads.append(thread)
36-
for t in self.Threads:
37-
t.join()
10+
self.isConnected = False
11+
self.server = server
12+
self.socks = [[], []]
13+
self.users = {}
14+
self.userID = 0
15+
self.rank = 0
16+
self.chatID = 0
17+
self.socks[1] = sock
18+
self.connectToXat()
3819
except:
3920
pass
4021

41-
def parsePlugins(self, packet, direction, sock):
42-
for p in self.Plugins:
43-
exec(p, globals())
44-
data = plugin(self, packet, direction, sock)
45-
if data:
46-
packet = data
47-
return packet
22+
def connectToXat(self):
23+
self.socks[0] = socket(AF_INET, SOCK_STREAM, SOL_TCP)
24+
self.socks[0].connect((Config.XAT_IP, Config.XAT_PORT))
25+
self.startHeartBeat()
26+
self.keepRunning()
4827

49-
def xml2Array(self, xml):
50-
try:
51-
returnArray = {}
52-
xml = xml.strip('\0')
53-
xml = fromstring(xml)
54-
returnArray['name'] = xml.tag
55-
for tag, attrib in xml.attrib.items():
56-
returnArray[tag] = attrib
57-
except:
58-
pass
59-
return returnArray
60-
61-
def buildPacket(self, node, packets):
62-
packet = ["<" + node]
63-
for (k, v) in packets.items():
64-
if str(k) != 'name':
65-
packet.append(str(k) + "=\"" + str(v) + "\"")
66-
packet.append("/>")
67-
return (' ' .join(packet) + '\x00').encode()
68-
69-
def fixUserID(self, uid):
70-
uid += "_"
71-
trim = uid.index('_')
72-
return uid[0:trim]
73-
74-
def loadPlugins(self):
75-
self.Plugins = []
76-
for pname in glob('plugins/*.py'):
77-
self.Plugins.append(open(pname).read())
78-
79-
def Logger(self, file, text):
80-
if path.exists('logs/' + file + '.log'):
81-
logs = open('logs/' + file + '.log', 'a+')
82-
logs.write(text + "\n")
83-
logs.close()
84-
85-
def addClient(self, conn):
86-
socks = [[], []]
87-
socks[0] = socket(AF_INET, SOCK_STREAM, SOL_TCP)
88-
socks[0].connect((Config.XAT_IP, Config.XAT_PORT))
89-
socks[0].setblocking(0)
90-
socks[1] = conn
28+
def keepRunning(self):
9129
try:
9230
while True:
93-
allSocks,_,_ = select(socks, [], [])
94-
for sock in allSocks:
31+
socks,_,_ = select(self.socks, [], [])
32+
for sock in socks:
9533
recv = ""
34+
sock.setblocking(0)
9635
while recv[-1:] != chr(0):
97-
recv += sock.recv(1204).decode('utf-8', 'ignore')
98-
if len(recv) <= 1:
36+
recv += sock.recv(2048).decode('utf-8', 'ignore')
37+
if len(recv) < 1:
38+
self.killSockets()
9939
break
10040
if recv:
10141
for packet in recv.split('\x00'):
10242
if '<f ' in packet:
103-
dataInfo = 1 if sock == socks[0] else 0
104-
socks[dataInfo].send((packet + '\x00').encode())
43+
dataInfo = 1 if sock == self.socks[0] else 0
44+
self.socks[dataInfo].send(bytes(packet + '\x00', encoding='utf-8'))
10545
else:
106-
data = self.xml2Array(packet)
107-
if data:
108-
dataInfo = [1, 'fromxat', 'RECV'] if sock == socks[0] else [0, 'toxat', 'SENT']
109-
data = self.parsePlugins(data, dataInfo[1], conn)
110-
toBeSend = self.buildPacket(data['name'], data)
111-
if data['name'] == 'policy-file-request':
112-
socks[1].send(Config.CROSSDOMAIN.encode() + b'\x00')
113-
elif data['name'] != 'HIDDEN':
114-
print('[' + dataInfo[2] + ']: ', toBeSend.decode('utf-8', 'ignore'))
115-
socks[dataInfo[0]].send(toBeSend)
116-
117-
except Exception as e:
118-
error = str(e)
119-
if not 'ConnectionAbortedError' in error and not '10053' in error:
120-
self.Logger('errors', error)
46+
self.parse(sock, packet)
47+
except:
48+
self.killSockets()
49+
50+
def parse(self, sock, packet):
51+
data = self.server.xml2Array(packet)
52+
if not data:
53+
return
54+
dataInfo = [1, 'fromxat', 'RECV'] if sock == self.socks[0] else [0, 'toxat', 'SENT']
55+
data = self.server.parsePlugins(data, dataInfo[1], self)
56+
if data['name'] == 'policy-file-request':
57+
self.sendToUser(Config.CROSSDOMAIN)
58+
elif data['name'] != 'HIDDEN':
59+
if data['name'] == 'j2':
60+
self.userID = int(data['u'])
61+
self.chatID = int(data['c'])
62+
elif data['name'] == 'i' and 'r' in data:
63+
self.rank = int(data['r'])
64+
elif data['name'] == 'l' and 'u' in data:
65+
if int(data['u']) in self.users:
66+
del self.users[int(data['u'])]
67+
elif data['name'] == 'done':
68+
self.isConnected = True
69+
self.announce('xatClient is running...')
70+
elif data['name'] == 'u' and 'u' in data:
71+
self.users[int(data['u'])] = {
72+
'name': data['n'],
73+
'avatar': data['a'],
74+
'home': data['h'],
75+
'rank': int(data['f']) & 7 if 'f' in data else 5,
76+
'reg': data['N'] if 'N' in data else False,
77+
'd0': data['d0'] if 'd0' in data else False,
78+
'd2': data['d2'] if 'd2' in data else False,
79+
'f': data['f'] if 'f' in data else False,
80+
}
81+
toBeSend = self.server.buildPacket(data['name'], data)
82+
print('[' + dataInfo[2] + ' - ' + str(self.chatID) + ']: ', toBeSend)
83+
self.socks[dataInfo[0]].send(bytes(toBeSend, encoding='utf-8'))
84+
85+
def sendToUser(self, packet):
86+
print('[RECV - ' + str(self.chatID) + ']: ', packet)
87+
return self.socks[1].send(bytes(packet + '\x00', encoding='utf-8'))
88+
89+
def sendToXat(self, packet):
90+
print('[SENT - ' + str(self.chatID) + ']: ', packet)
91+
return self.socks[0].send(bytes(packet + '\x00', encoding='utf-8'))
92+
93+
def announce(self, message):
94+
self.sendToUser(self.server.buildPacket('m', {'t': message, 'u': 0}))
95+
96+
def killSockets(self):
97+
for sock in self.socks:
98+
sock.close()
99+
100+
def sendHeartBeat(self):
101+
try:
102+
sleep(10) # first time only
103+
while True:
104+
if self.isConnected:
105+
self.sendToXat(self.server.buildPacket('c', {'u': self.userID, 't': '/KEEPALIVE'}))
106+
sleep(50)
107+
except:
108+
pass
109+
110+
111+
def startHeartBeat(self):
112+
thread = Thread(target=self.sendHeartBeat)
113+
thread.daemon = True
114+
thread.start()
115+
116+
def getById(self, userid):
117+
if not self.users:
118+
return False
119+
for uid, user in self.users.items():
120+
if userid == uid:
121+
return user
122+
return False

classes/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
class Config:
2-
CMD_CHARACTER = '!'
2+
CMD_CHARACTER = '-'
33

44
CLIENT_SERVERIP = '0.0.0.0'
55
CLIENT_SERVERPORT = 10000

classes/server.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
from os import path
2+
from socket import *
3+
from glob import glob
4+
from select import select
5+
from .config import Config
6+
from .client import Client
7+
from ntpath import basename
8+
from threading import Thread
9+
from .webserver import WebServer
10+
from xml.etree.ElementTree import fromstring
11+
12+
class Server:
13+
def __init__(self):
14+
try:
15+
self.Threads = []
16+
self.Disabled = []
17+
self.WebServer = WebServer()
18+
self.RunServer = Thread(target=self.WebServer.start)
19+
self.RunServer.daemon = True
20+
self.RunServer.start()
21+
print("> Webserver started.")
22+
self.Plugins = []
23+
self.loadPlugins()
24+
print("> Plugins started.")
25+
self.RunSocket = Thread(target=self.createSocket)
26+
self.RunSocket.daemon = True
27+
self.RunSocket.start()
28+
print("> Socket started.")
29+
except:
30+
pass
31+
32+
def createSocket(self):
33+
try:
34+
xSock = socket(AF_INET, SOCK_STREAM)
35+
xSock.settimeout(None)
36+
xSock.bind((Config.CLIENT_SERVERIP, Config.CLIENT_SERVERPORT))
37+
xSock.listen(100)
38+
while True:
39+
(conn, (ip, port)) = xSock.accept()
40+
thread = Thread(target=Client, args=(self, conn))
41+
thread.daemon = True
42+
thread.start()
43+
self.Threads.append(thread)
44+
for t in self.Threads:
45+
t.join()
46+
except:
47+
pass
48+
49+
def parsePlugins(self, packet, direction, user):
50+
for p in self.Plugins:
51+
exec(p, globals())
52+
data = plugin(self, packet, direction, user)
53+
if data:
54+
packet = data
55+
return packet
56+
57+
def xml2Array(self, xml):
58+
try:
59+
returnArray = {}
60+
xml = xml.strip('\0')
61+
xml = fromstring(xml)
62+
returnArray['name'] = xml.tag
63+
for tag, attrib in xml.attrib.items():
64+
returnArray[tag] = attrib
65+
except:
66+
pass
67+
return returnArray
68+
69+
def buildPacket(self, node, packets):
70+
packet = ["<" + node]
71+
for (k, v) in packets.items():
72+
if str(k) != 'name':
73+
packet.append(str(k) + "=\"" + self.sanatize(str(v)) + "\"")
74+
packet.append("/>")
75+
return ' ' .join(packet) + '\x00'
76+
77+
def fixUserID(self, uid):
78+
uid += "_"
79+
trim = uid.index('_')
80+
return uid[0:trim]
81+
82+
def sanatize(self, data):
83+
entities = {
84+
'"': '&quot;',
85+
'<': '&lt;',
86+
'˃': '\xcb\x83'
87+
}
88+
for (i, u) in entities.items():
89+
data = data.replace(i, u)
90+
return data
91+
92+
def loadPlugins(self):
93+
self.Plugins = []
94+
for pname in glob('plugins/*.py'):
95+
name = basename(pname).replace('.py', '')
96+
if not name in self.Disabled:
97+
self.Plugins.append(open(pname).read())
98+
99+
def disablePlugin(self, name):
100+
if path.exists('plugins/' + name + '.py') and not name in self.Disabled:
101+
self.Disabled.append(name)
102+
self.loadPlugins()
103+
return True
104+
return False
105+
106+
def enablePlugin(self, name):
107+
if name in self.Disabled:
108+
self.Disabled.remove(name)
109+
self.loadPlugins()
110+
return True
111+
return False

classes/webserver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def __init__(self):
1212
def start(self):
1313
self.sock = socket(AF_INET, SOCK_STREAM)
1414
self.sock.bind((Config.WEB_SERVERIP, Config.WEB_SERVERPORT))
15-
self.sock.listen(15)
15+
self.sock.listen(25)
1616
while(True):
1717
usock, address = self.sock.accept()
1818
recv = usock.recv(4096)

0 commit comments

Comments
 (0)