initial commit
This commit is contained in:
commit
344ee23c66
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
*.pyc
|
||||||
|
*~
|
||||||
|
.idea/
|
||||||
|
test/
|
||||||
|
libs/
|
44
README.md
Normal file
44
README.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
FILE BOT - Share files with friends and family using [Tox](https://tox.chat/)
|
||||||
|
|
||||||
|
|
||||||
|
# Supported OS:
|
||||||
|
- Windows
|
||||||
|
- Linux
|
||||||
|
|
||||||
|
#Install:
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
1. [Download and install latest Python 2.7](https://www.python.org/downloads/windows/)
|
||||||
|
2. [Download file bot](https://github.com/ingvar1995/filebot/archive/master.zip)
|
||||||
|
3. Unpack archive
|
||||||
|
4. Download latest [libtox.dll](https://build.tox.chat/view/libtoxcore/job/libtoxcore_build_windows_x86_shared_release/lastSuccessfulBuild/artifact/libtoxcore_build_windows_x86_shared_release.zip) build, download latest [libsodium.a](https://build.tox.chat/view/libsodium/job/libsodium_build_windows_x86_static_release/lastSuccessfulBuild/artifact/libsodium_build_windows_x86_static_release.zip) build, put it into libs\
|
||||||
|
5. Run app:
|
||||||
|
``python main.py path_to_profile``
|
||||||
|
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
1. [Download file bot](https://github.com/ingvar1995/filebot/archive/master.zip)
|
||||||
|
2. Unpack archive
|
||||||
|
3. Install [toxcore](https://github.com/irungentoo/toxcore/blob/master/INSTALL.md) in your system
|
||||||
|
4. Run app:
|
||||||
|
``python main.py path_to_profile``
|
||||||
|
|
||||||
|
#Commands:
|
||||||
|
``help - list of commands
|
||||||
|
rights - get access rights
|
||||||
|
files - show list of files (get access)
|
||||||
|
stats - downloads statistics (get access)
|
||||||
|
id - get bot's id (get access)
|
||||||
|
share <ToxID> <file_name> - send file to friend (get access)
|
||||||
|
share --all <file_name> - send file to all friends (get access)
|
||||||
|
size <file_name> - get size of file (get access)
|
||||||
|
get <file_name> - get file with specified filename (get access)
|
||||||
|
del <file_name> - remove file with specified filename (delete access)
|
||||||
|
rename <file_name> --new <new_file_name> - rename file (delete access)
|
||||||
|
user <ToxID> <rights> - new rights (example: rwdm) for user (masters only)
|
||||||
|
status <new_status> - new status message (masters only)
|
||||||
|
name <new_name> - new name (masters only)
|
||||||
|
message <ToxID> <message_text> - send message to friend (masters only)
|
||||||
|
message --all <message_text> - send message to all friends (masters only)``
|
87
bootstrap.py
Normal file
87
bootstrap.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
class Node(object):
|
||||||
|
def __init__(self, ip, port, tox_key, rand):
|
||||||
|
self._ip, self._port, self._tox_key, self.rand = ip, port, tox_key, rand
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
return self._ip, self._port, self._tox_key
|
||||||
|
|
||||||
|
|
||||||
|
def node_generator():
|
||||||
|
nodes = []
|
||||||
|
ips = [
|
||||||
|
"144.76.60.215", "23.226.230.47", "195.154.119.113", "biribiri.org",
|
||||||
|
"46.38.239.179", "178.62.250.138", "130.133.110.14", "104.167.101.29",
|
||||||
|
"205.185.116.116", "198.98.51.198", "80.232.246.79", "108.61.165.198",
|
||||||
|
"212.71.252.109", "194.249.212.109", "185.25.116.107", "192.99.168.140",
|
||||||
|
"46.101.197.175", "95.215.46.114", "5.189.176.217", "148.251.23.146",
|
||||||
|
"104.223.122.15", "78.47.114.252", "d4rk4.ru", "81.4.110.149",
|
||||||
|
"95.31.20.151", "104.233.104.126", "51.254.84.212", "home.vikingmakt.com.br",
|
||||||
|
"5.135.59.163", "185.58.206.164", "188.244.38.183", "mrflibble.c4.ee",
|
||||||
|
"82.211.31.116", "128.199.199.197", "103.230.156.174", "91.121.66.124",
|
||||||
|
"92.54.84.70", "tox1.privacydragon.me"
|
||||||
|
]
|
||||||
|
ports = [
|
||||||
|
33445, 33445, 33445, 33445,
|
||||||
|
33445, 33445, 33445, 33445,
|
||||||
|
33445, 33445, 33445, 33445,
|
||||||
|
33445, 33445, 33445, 33445,
|
||||||
|
443, 33445, 5190, 2306,
|
||||||
|
33445, 33445, 1813, 33445,
|
||||||
|
33445, 33445, 33445, 33445,
|
||||||
|
33445, 33445, 33445, 33445,
|
||||||
|
33445, 33445, 33445, 33445,
|
||||||
|
33445, 33445
|
||||||
|
]
|
||||||
|
ids = [
|
||||||
|
"04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F",
|
||||||
|
"A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074",
|
||||||
|
"E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354",
|
||||||
|
"F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67",
|
||||||
|
"F5A1A38EFB6BD3C2C8AF8B10D85F0F89E931704D349F1D0720C3C4059AF2440A",
|
||||||
|
"788236D34978D1D5BD822F0A5BEBD2C53C64CC31CD3149350EE27D4D9A2F9B6B",
|
||||||
|
"461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F",
|
||||||
|
"5918AC3C06955962A75AD7DF4F80A5D7C34F7DB9E1498D2E0495DE35B3FE8A57",
|
||||||
|
"A179B09749AC826FF01F37A9613F6B57118AE014D4196A0E1105A98F93A54702",
|
||||||
|
"1D5A5F2F5D6233058BF0259B09622FB40B482E4FA0931EB8FD3AB8E7BF7DAF6F",
|
||||||
|
"CF6CECA0A14A31717CC8501DA51BE27742B70746956E6676FF423A529F91ED5D",
|
||||||
|
"8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832",
|
||||||
|
"C4CEB8C7AC607C6B374E2E782B3C00EA3A63B80D4910B8649CCACDD19F260819",
|
||||||
|
"3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B",
|
||||||
|
"DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43",
|
||||||
|
"6A4D0607A296838434A6A7DDF99F50EF9D60A2C510BBF31FE538A25CB6B4652F",
|
||||||
|
"CD133B521159541FB1D326DE9850F5E56A6C724B5B8E5EB5CD8D950408E95707",
|
||||||
|
"5823FB947FF24CF83DDFAC3F3BAA18F96EA2018B16CC08429CB97FA502F40C23",
|
||||||
|
"2B2137E094F743AC8BD44652C55F41DFACC502F125E99E4FE24D40537489E32F",
|
||||||
|
"7AED21F94D82B05774F697B209628CD5A9AD17E0C073D9329076A4C28ED28147",
|
||||||
|
"0FB96EEBFB1650DDB52E70CF773DDFCABE25A95CC3BB50FC251082E4B63EF82A",
|
||||||
|
"1C5293AEF2114717547B39DA8EA6F1E331E5E358B35F9B6B5F19317911C5F976",
|
||||||
|
"53737F6D47FA6BD2808F378E339AF45BF86F39B64E79D6D491C53A1D522E7039",
|
||||||
|
"9E7BD4793FFECA7F32238FA2361040C09025ED3333744483CA6F3039BFF0211E",
|
||||||
|
"9CA69BB74DE7C056D1CC6B16AB8A0A38725C0349D187D8996766958584D39340",
|
||||||
|
"EDEE8F2E839A57820DE3DA4156D88350E53D4161447068A3457EE8F59F362414",
|
||||||
|
"AEC204B9A4501412D5F0BB67D9C81B5DB3EE6ADA64122D32A3E9B093D544327D",
|
||||||
|
"188E072676404ED833A4E947DC1D223DF8EFEBE5F5258573A236573688FB9761",
|
||||||
|
"2D320F971EF2CA18004416C2AAE7BA52BF7949DB34EA8E2E21AF67BD367BE211",
|
||||||
|
"24156472041E5F220D1FA11D9DF32F7AD697D59845701CDD7BE7D1785EB9DB39",
|
||||||
|
"15A0F9684E2423F9F46CFA5A50B562AE42525580D840CC50E518192BF333EE38",
|
||||||
|
"FAAB17014F42F7F20949F61E55F66A73C230876812A9737F5F6D2DCE4D9E4207",
|
||||||
|
"AF97B76392A6474AF2FD269220FDCF4127D86A42EF3A242DF53A40A268A2CD7C",
|
||||||
|
"B05C8869DBB4EDDD308F43C1A974A20A725A36EACCA123862FDE9945BF9D3E09",
|
||||||
|
"5C4C7A60183D668E5BD8B3780D1288203E2F1BAE4EEF03278019E21F86174C1D",
|
||||||
|
"4E3F7D37295664BBD0741B6DBCB6431D6CD77FC4105338C2FC31567BF5C8224A",
|
||||||
|
"5625A62618CB4FCA70E147A71B29695F38CC65FF0CBD68AD46254585BE564802",
|
||||||
|
"31910C0497D347FF160D6F3A6C0E317BAFA71E8E03BC4CBB2A185C9D4FB8B31E"
|
||||||
|
]
|
||||||
|
for i in xrange(len(ips)):
|
||||||
|
nodes.append(Node(ips[i], ports[i], ids[i], random.randint(0, 1000000)))
|
||||||
|
arr = sorted(nodes, key=lambda x: x.rand)[:4]
|
||||||
|
for elem in arr:
|
||||||
|
yield elem.get_data()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
for elem in node_generator():
|
||||||
|
print str(elem)
|
382
bot.py
Normal file
382
bot.py
Normal file
@ -0,0 +1,382 @@
|
|||||||
|
from tox import Tox
|
||||||
|
import os
|
||||||
|
from settings import *
|
||||||
|
from toxcore_enums_and_consts import *
|
||||||
|
from ctypes import *
|
||||||
|
from util import log, Singleton
|
||||||
|
from file_transfers import *
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
|
||||||
|
class Bot(Singleton):
|
||||||
|
|
||||||
|
def __init__(self, tox):
|
||||||
|
"""
|
||||||
|
:param tox: tox instance
|
||||||
|
"""
|
||||||
|
super(Bot, self).__init__()
|
||||||
|
self._tox = tox
|
||||||
|
self._file_transfers = {} # dict of file transfers. key - tuple (friend_number, file_number)
|
||||||
|
self._downloads = defaultdict(int)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Edit current user's data
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def set_name(self, value):
|
||||||
|
self._tox.self_set_name(value.encode('utf-8'))
|
||||||
|
|
||||||
|
def set_status_message(self, value):
|
||||||
|
self._tox.self_set_status_message(value.encode('utf-8'))
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Private messages
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def send_message(self, number, message, message_type=TOX_MESSAGE_TYPE['NORMAL']):
|
||||||
|
"""
|
||||||
|
Message splitting
|
||||||
|
:param number: friend's number
|
||||||
|
:param message_type: type of message
|
||||||
|
:param message: message text
|
||||||
|
"""
|
||||||
|
while len(message) > TOX_MAX_MESSAGE_LENGTH:
|
||||||
|
size = TOX_MAX_MESSAGE_LENGTH * 4 / 5
|
||||||
|
last_part = message[size:TOX_MAX_MESSAGE_LENGTH]
|
||||||
|
if ' ' in last_part:
|
||||||
|
index = last_part.index(' ')
|
||||||
|
elif ',' in last_part:
|
||||||
|
index = last_part.index(',')
|
||||||
|
elif '.' in last_part:
|
||||||
|
index = last_part.index('.')
|
||||||
|
else:
|
||||||
|
index = TOX_MAX_MESSAGE_LENGTH - size
|
||||||
|
index += size + 1
|
||||||
|
self._tox.friend_send_message(number, message_type, message[:index])
|
||||||
|
message = message[index:]
|
||||||
|
self._tox.friend_send_message(number, message_type, message)
|
||||||
|
|
||||||
|
def new_message(self, friend_num, message):
|
||||||
|
"""
|
||||||
|
New message
|
||||||
|
:param friend_num: number of friend who sent message
|
||||||
|
:param message: text of message
|
||||||
|
"""
|
||||||
|
id = self._tox.friend_get_public_key(friend_num)
|
||||||
|
settings = Settings.get_instance()
|
||||||
|
message = message.strip()
|
||||||
|
if message == 'files':
|
||||||
|
if id in settings['read']:
|
||||||
|
s = ''
|
||||||
|
for f in os.listdir(settings['folder']):
|
||||||
|
f = unicode(f)
|
||||||
|
if os.path.isfile(os.path.join(settings['folder'], f)):
|
||||||
|
s += u'{} ({} bytes)\n'.format(f, os.path.getsize(os.path.join(settings['folder'], f)))
|
||||||
|
if not s:
|
||||||
|
s = 'Nothing found'
|
||||||
|
self.send_message(friend_num, s.encode('utf-8'), TOX_MESSAGE_TYPE['NORMAL'])
|
||||||
|
else:
|
||||||
|
self.send_message(friend_num, 'Not enough rights'.encode('utf-8'))
|
||||||
|
elif message.startswith('get '):
|
||||||
|
if id in settings['read']:
|
||||||
|
path = settings['folder'] + '/' + unicode(message[4:])
|
||||||
|
if os.path.exists(unicode(path)):
|
||||||
|
self.send_file(unicode(path), friend_num)
|
||||||
|
else:
|
||||||
|
self.send_message(friend_num, 'Wrong file name'.encode('utf-8'))
|
||||||
|
else:
|
||||||
|
self.send_message(friend_num, 'Not enough rights'.encode('utf-8'))
|
||||||
|
elif message == 'help':
|
||||||
|
self.send_message(friend_num, """help - list of commands\n
|
||||||
|
rights - get access rights\n
|
||||||
|
files - show list of files (get access)\n
|
||||||
|
stats - downloads statistics (get access)\n
|
||||||
|
id - get bot's id (get access)\n
|
||||||
|
share <ToxID> <file_name> - send file to friend (get access)\n
|
||||||
|
share --all <file_name> - send file to all friends (get access)\n
|
||||||
|
size <file_name> - get size of file (get access)\n
|
||||||
|
get <file_name> - get file with specified filename (get access)\n
|
||||||
|
del <file_name> - remove file with specified filename (delete access)\n
|
||||||
|
rename <file_name> --new <new_file_name> - rename file (delete access)\n
|
||||||
|
user <ToxID> <rights> - new rights (example: rwdm) for user (masters only)\n
|
||||||
|
status <new_status> - new status message (masters only)\n
|
||||||
|
name <new_name> - new name (masters only)\n
|
||||||
|
message <ToxID> <message_text> - send message to friend (masters only)\n
|
||||||
|
message --all <message_text> - send message to all friends (masters only)
|
||||||
|
Users with write access can send files to bot.
|
||||||
|
""".encode('utf-8'))
|
||||||
|
elif message == 'rights':
|
||||||
|
self.send_message(friend_num, 'Read: {}\nWrite: {}\nDelete: {}\nMaster: {}'
|
||||||
|
.format('Yes' if id in settings['read'] else 'No',
|
||||||
|
'Yes' if id in settings['write'] else 'No',
|
||||||
|
'Yes' if id in settings['delete'] else 'No',
|
||||||
|
'Yes, sir!' if id in settings['master'] else 'No'))
|
||||||
|
elif message.startswith('del '):
|
||||||
|
if id in settings['delete']:
|
||||||
|
path = settings['folder'] + '/' + message[4:]
|
||||||
|
if os.path.exists(path):
|
||||||
|
os.remove(path)
|
||||||
|
self.send_message(friend_num, 'File was successfully deleted')
|
||||||
|
else:
|
||||||
|
self.send_message(friend_num, 'Wrong file name'.encode('utf-8'))
|
||||||
|
else:
|
||||||
|
self.send_message(friend_num, 'Not enough rights'.encode('utf-8'))
|
||||||
|
elif message.startswith('user '):
|
||||||
|
if id not in settings['master']:
|
||||||
|
self.send_message(friend_num, 'Not enough rights'.encode('utf-8'))
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
rights = message.split(' ')[2]
|
||||||
|
except:
|
||||||
|
rights = ''
|
||||||
|
id = message.split(' ')[1][:TOX_PUBLIC_KEY_SIZE * 2]
|
||||||
|
if id in settings['read']:
|
||||||
|
settings['read'].remove(id)
|
||||||
|
if id in settings['write']:
|
||||||
|
settings['write'].remove(id)
|
||||||
|
if id in settings['delete']:
|
||||||
|
settings['delete'].remove(id)
|
||||||
|
|
||||||
|
if 'r' in rights:
|
||||||
|
settings['read'].append(id)
|
||||||
|
if 'w' in rights:
|
||||||
|
settings['write'].append(id)
|
||||||
|
if 'd' in rights:
|
||||||
|
settings['delete'].append(id)
|
||||||
|
if 'm' in rights:
|
||||||
|
settings['master'].append(id)
|
||||||
|
settings.save()
|
||||||
|
self.send_message(friend_num, 'Updated'.encode('utf-8'))
|
||||||
|
|
||||||
|
elif message.startswith('status '):
|
||||||
|
if id not in settings['master']:
|
||||||
|
self.send_message(friend_num, 'Not enough rights'.encode('utf-8'))
|
||||||
|
else:
|
||||||
|
self.set_status_message(message[7:])
|
||||||
|
elif message.startswith('name '):
|
||||||
|
if id not in settings['master']:
|
||||||
|
self.send_message(friend_num, 'Not enough rights'.encode('utf-8'))
|
||||||
|
else:
|
||||||
|
self.set_name(message[5:])
|
||||||
|
elif message.startswith('share '):
|
||||||
|
if id in settings['read']:
|
||||||
|
if '--all' not in message:
|
||||||
|
fl = ' '.join(message.split(' ')[2:])
|
||||||
|
try:
|
||||||
|
num = self._tox.friend_by_public_key(message.split(' ')[1][:TOX_PUBLIC_KEY_SIZE * 2])
|
||||||
|
print num
|
||||||
|
self.send_file(settings['folder'] + '/' + fl, num)
|
||||||
|
except Exception as ex:
|
||||||
|
print ex
|
||||||
|
self.send_message(friend_num, 'Friend not found'.encode('utf-8'))
|
||||||
|
else:
|
||||||
|
fl = ' '.join(message.split(' ')[2:])
|
||||||
|
for num in self._tox.self_get_friend_list():
|
||||||
|
if self._tox.friend_get_connection_status(num):
|
||||||
|
self.send_file(settings['folder'] + '/' + fl, num)
|
||||||
|
else:
|
||||||
|
self.send_message(friend_num, 'Not enough rights'.encode('utf-8'))
|
||||||
|
elif message.startswith('rename '):
|
||||||
|
ind = message.index(' --new ')
|
||||||
|
old = message[7:ind]
|
||||||
|
new = message[ind + 7:]
|
||||||
|
if id in settings['delete']:
|
||||||
|
if os.path.exists(settings['folder'] + '/' + old):
|
||||||
|
os.rename(settings['folder'] + '/' + old, settings['folder'] + '/' + new)
|
||||||
|
self.send_message(friend_num, 'Renamed'.encode('utf-8'))
|
||||||
|
else:
|
||||||
|
self.send_message(friend_num, 'File not found'.encode('utf-8'))
|
||||||
|
else:
|
||||||
|
self.send_message(friend_num, 'Not enough rights'.encode('utf-8'))
|
||||||
|
elif message == 'id':
|
||||||
|
if id in settings['read']:
|
||||||
|
tox_id = self._tox.self_get_address()
|
||||||
|
self.send_message(friend_num, tox_id.encode('utf-8'))
|
||||||
|
else:
|
||||||
|
self.send_message(friend_num, 'Not enough rights'.encode('utf-8'))
|
||||||
|
elif message.startswith('size '):
|
||||||
|
path = unicode(settings['folder'] + '/' + message[5:])
|
||||||
|
if id not in settings['read']:
|
||||||
|
self.send_message(friend_num, 'Not enough rights'.encode('utf-8'))
|
||||||
|
elif not os.path.exists(path):
|
||||||
|
self.send_message(friend_num, 'File not found'.encode('utf-8'))
|
||||||
|
else:
|
||||||
|
bytes_size = os.path.getsize(path)
|
||||||
|
if bytes_size < 1024:
|
||||||
|
size = u'{} B'.format(bytes_size)
|
||||||
|
elif bytes_size < 1024 * 1024:
|
||||||
|
size = u'{} KB'.format(bytes_size / 1024)
|
||||||
|
else:
|
||||||
|
size = u'{} MB'.format(bytes_size / 1024 * 1024)
|
||||||
|
s = u'size: {} ({} bytes)'.format(size, bytes_size)
|
||||||
|
self.send_message(friend_num, s.encode('utf-8'))
|
||||||
|
elif message.startswith('message '):
|
||||||
|
if id not in settings['master']:
|
||||||
|
self.send_message(friend_num, 'Not enough rights'.encode('utf-8'))
|
||||||
|
elif '--all' not in message:
|
||||||
|
tox_id = message.split(' ')[1][:TOX_PUBLIC_KEY_SIZE * 2]
|
||||||
|
s = ' '.join(message.split(' ')[2:])
|
||||||
|
num = self._tox.friend_by_public_key(tox_id)
|
||||||
|
try:
|
||||||
|
self.send_message(num, s.encode('utf-8'))
|
||||||
|
except:
|
||||||
|
self.send_message(friend_num, 'Friend is not online'.encode('utf-8'))
|
||||||
|
else:
|
||||||
|
s = ' '.join(message.split(' ')[2:])
|
||||||
|
for num in self._tox.self_get_friend_list():
|
||||||
|
if self._tox.friend_get_connection_status(num):
|
||||||
|
self.send_message(num, s.encode('utf-8'))
|
||||||
|
elif message == 'stats':
|
||||||
|
if id not in settings['read']:
|
||||||
|
self.send_message(friend_num, 'Not enough rights'.encode('utf-8'))
|
||||||
|
else:
|
||||||
|
s = ''
|
||||||
|
for f in os.listdir(settings['folder']):
|
||||||
|
f = unicode(f)
|
||||||
|
if os.path.isfile(os.path.join(settings['folder'], f)):
|
||||||
|
s += u'{} ({} downloads)\n'.format(f,
|
||||||
|
self._downloads[os.path.join(settings['folder'], f)])
|
||||||
|
if not s:
|
||||||
|
s = 'Nothing found'
|
||||||
|
else:
|
||||||
|
s += u'Downloads count: {}'.format(sum(self._downloads.values()))
|
||||||
|
self.send_message(friend_num, s.encode('utf-8'), TOX_MESSAGE_TYPE['NORMAL'])
|
||||||
|
else:
|
||||||
|
self.send_message(friend_num, 'Wrong command'.encode('utf-8'))
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Friend requests
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def process_friend_request(self, tox_id, message):
|
||||||
|
"""
|
||||||
|
Accept or ignore friend request
|
||||||
|
:param tox_id: tox id of contact
|
||||||
|
:param message: message
|
||||||
|
"""
|
||||||
|
self._tox.friend_add_norequest(tox_id) # num - friend number
|
||||||
|
settings = Settings.get_instance()
|
||||||
|
if 'r' in settings['auto_rights'] and tox_id not in settings['read']:
|
||||||
|
settings['read'].append(tox_id)
|
||||||
|
if 'w' in settings['auto_rights'] and tox_id not in settings['write']:
|
||||||
|
settings['write'].append(tox_id)
|
||||||
|
if 'd' in settings['auto_rights'] and tox_id not in settings['delete']:
|
||||||
|
settings['delete'].append(tox_id)
|
||||||
|
if 'm' in settings['auto_rights'] and tox_id not in settings['master']:
|
||||||
|
settings['master'].append(tox_id)
|
||||||
|
settings.save()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# File transfers support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def incoming_file_transfer(self, friend_number, file_number, size, file_name):
|
||||||
|
"""
|
||||||
|
New transfer
|
||||||
|
:param friend_number: number of friend who sent file
|
||||||
|
:param file_number: file number
|
||||||
|
:param size: file size in bytes
|
||||||
|
:param file_name: file name without path
|
||||||
|
"""
|
||||||
|
id = self._tox.friend_get_public_key(friend_number)
|
||||||
|
settings = Settings.get_instance()
|
||||||
|
if id in settings['write']:
|
||||||
|
path = settings['folder']
|
||||||
|
new_file_name, i = file_name, 1
|
||||||
|
while os.path.isfile(path + '/' + new_file_name): # file with same name already exists
|
||||||
|
if '.' in file_name: # has extension
|
||||||
|
d = file_name.rindex('.')
|
||||||
|
else: # no extension
|
||||||
|
d = len(file_name)
|
||||||
|
new_file_name = file_name[:d] + ' ({})'.format(i) + file_name[d:]
|
||||||
|
i += 1
|
||||||
|
self.accept_transfer(path + '/' + new_file_name, friend_number, file_number, size)
|
||||||
|
else:
|
||||||
|
self.cancel_transfer(friend_number, file_number, False)
|
||||||
|
|
||||||
|
def cancel_transfer(self, friend_number, file_number, already_cancelled=False):
|
||||||
|
"""
|
||||||
|
Stop transfer
|
||||||
|
:param friend_number: number of friend
|
||||||
|
:param file_number: file number
|
||||||
|
:param already_cancelled: was cancelled by friend
|
||||||
|
"""
|
||||||
|
if (friend_number, file_number) in self._file_transfers:
|
||||||
|
tr = self._file_transfers[(friend_number, file_number)]
|
||||||
|
if not already_cancelled:
|
||||||
|
tr.cancel()
|
||||||
|
else:
|
||||||
|
tr.cancelled()
|
||||||
|
del self._file_transfers[(friend_number, file_number)]
|
||||||
|
else:
|
||||||
|
self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL'])
|
||||||
|
|
||||||
|
def accept_transfer(self, path, friend_number, file_number, size):
|
||||||
|
"""
|
||||||
|
:param path: path for saving
|
||||||
|
:param friend_number: friend number
|
||||||
|
:param file_number: file number
|
||||||
|
:param size: file size
|
||||||
|
"""
|
||||||
|
rt = ReceiveTransfer(path, self._tox, friend_number, size, file_number)
|
||||||
|
self._file_transfers[(friend_number, file_number)] = rt
|
||||||
|
self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME'])
|
||||||
|
|
||||||
|
def send_file(self, path, friend_number):
|
||||||
|
"""
|
||||||
|
Send file to current active friend
|
||||||
|
:param path: file path
|
||||||
|
"""
|
||||||
|
self._downloads[path] += 1
|
||||||
|
st = SendTransfer(path, self._tox, friend_number)
|
||||||
|
self._file_transfers[(friend_number, st.get_file_number())] = st
|
||||||
|
|
||||||
|
def incoming_chunk(self, friend_number, file_number, position, data):
|
||||||
|
if (friend_number, file_number) in self._file_transfers:
|
||||||
|
transfer = self._file_transfers[(friend_number, file_number)]
|
||||||
|
transfer.write_chunk(position, data)
|
||||||
|
if transfer.state:
|
||||||
|
del self._file_transfers[(friend_number, file_number)]
|
||||||
|
|
||||||
|
def outgoing_chunk(self, friend_number, file_number, position, size):
|
||||||
|
if (friend_number, file_number) in self._file_transfers:
|
||||||
|
transfer = self._file_transfers[(friend_number, file_number)]
|
||||||
|
transfer.send_chunk(position, size)
|
||||||
|
if transfer.state:
|
||||||
|
del self._file_transfers[(friend_number, file_number)]
|
||||||
|
|
||||||
|
|
||||||
|
def tox_factory(data=None, settings=None):
|
||||||
|
"""
|
||||||
|
:param data: user data from .tox file. None = no saved data, create new profile
|
||||||
|
:param settings: current profile settings. None = default settings will be used
|
||||||
|
:return: new tox instance
|
||||||
|
"""
|
||||||
|
if settings is None:
|
||||||
|
settings = {
|
||||||
|
'ipv6_enabled': True,
|
||||||
|
'udp_enabled': True,
|
||||||
|
'proxy_type': 0,
|
||||||
|
'proxy_host': '0',
|
||||||
|
'proxy_port': 0,
|
||||||
|
'start_port': 0,
|
||||||
|
'end_port': 0,
|
||||||
|
'tcp_port': 0
|
||||||
|
}
|
||||||
|
tox_options = Tox.options_new()
|
||||||
|
tox_options.contents.udp_enabled = settings['udp_enabled']
|
||||||
|
tox_options.contents.proxy_type = settings['proxy_type']
|
||||||
|
tox_options.contents.proxy_host = settings['proxy_host']
|
||||||
|
tox_options.contents.proxy_port = settings['proxy_port']
|
||||||
|
tox_options.contents.start_port = settings['start_port']
|
||||||
|
tox_options.contents.end_port = settings['end_port']
|
||||||
|
tox_options.contents.tcp_port = settings['tcp_port']
|
||||||
|
if data: # load existing profile
|
||||||
|
tox_options.contents.savedata_type = TOX_SAVEDATA_TYPE['TOX_SAVE']
|
||||||
|
tox_options.contents.savedata_data = c_char_p(data)
|
||||||
|
tox_options.contents.savedata_length = len(data)
|
||||||
|
else: # create new profile
|
||||||
|
tox_options.contents.savedata_type = TOX_SAVEDATA_TYPE['NONE']
|
||||||
|
tox_options.contents.savedata_data = None
|
||||||
|
tox_options.contents.savedata_length = 0
|
||||||
|
return Tox(tox_options)
|
120
callbacks.py
Normal file
120
callbacks.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
from settings import Settings
|
||||||
|
from bot import Bot
|
||||||
|
from toxcore_enums_and_consts import *
|
||||||
|
from tox import bin_to_string
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Callbacks - current user
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def self_connection_status():
|
||||||
|
"""
|
||||||
|
Current user changed connection status (offline, UDP, TCP)
|
||||||
|
"""
|
||||||
|
def wrapped(tox, connection, user_data):
|
||||||
|
print 'Connection status: ', str(connection)
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Callbacks - friends
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def friend_connection_status(tox, friend_num, new_status, user_data):
|
||||||
|
"""
|
||||||
|
Check friend's connection status (offline, udp, tcp)
|
||||||
|
"""
|
||||||
|
print "Friend #{} connected! Friend's status: {}".format(friend_num, new_status)
|
||||||
|
|
||||||
|
|
||||||
|
def friend_message():
|
||||||
|
"""
|
||||||
|
New message from friend
|
||||||
|
"""
|
||||||
|
def wrapped(tox, friend_number, message_type, message, size, user_data):
|
||||||
|
print message.decode('utf-8')
|
||||||
|
Bot.get_instance().new_message(friend_number, message.decode('utf-8'))
|
||||||
|
# parse message
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
|
def friend_request(tox, public_key, message, message_size, user_data):
|
||||||
|
"""
|
||||||
|
Called when user get new friend request
|
||||||
|
"""
|
||||||
|
profile = Bot.get_instance()
|
||||||
|
tox_id = bin_to_string(public_key, TOX_PUBLIC_KEY_SIZE)
|
||||||
|
profile.process_friend_request(tox_id, message.decode('utf-8'))
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Callbacks - file transfers
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def tox_file_recv(tox_link):
|
||||||
|
"""
|
||||||
|
New incoming file
|
||||||
|
"""
|
||||||
|
def wrapped(tox, friend_number, file_number, file_type, size, file_name, file_name_size, user_data):
|
||||||
|
profile = Bot.get_instance()
|
||||||
|
if file_type == TOX_FILE_KIND['DATA']:
|
||||||
|
print 'file'
|
||||||
|
file_name = unicode(file_name[:file_name_size].decode('utf-8'))
|
||||||
|
profile.incoming_file_transfer(friend_number, file_number, size, file_name)
|
||||||
|
else: # AVATAR
|
||||||
|
tox_link.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL'])
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
|
def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, user_data):
|
||||||
|
"""
|
||||||
|
Incoming chunk
|
||||||
|
"""
|
||||||
|
Bot.get_instance().incoming_chunk(
|
||||||
|
friend_number,
|
||||||
|
file_number,
|
||||||
|
position,
|
||||||
|
chunk[:length] if length else None)
|
||||||
|
|
||||||
|
|
||||||
|
def file_chunk_request(tox, friend_number, file_number, position, size, user_data):
|
||||||
|
"""
|
||||||
|
Outgoing chunk
|
||||||
|
"""
|
||||||
|
Bot.get_instance().outgoing_chunk(
|
||||||
|
friend_number,
|
||||||
|
file_number,
|
||||||
|
position,
|
||||||
|
size)
|
||||||
|
|
||||||
|
|
||||||
|
def file_recv_control(tox, friend_number, file_number, file_control, user_data):
|
||||||
|
"""
|
||||||
|
Friend cancelled, paused or resumed file transfer
|
||||||
|
"""
|
||||||
|
if file_control == TOX_FILE_CONTROL['CANCEL']:
|
||||||
|
Bot.get_instance().cancel_transfer(friend_number, file_number, True)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Callbacks - initialization
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def init_callbacks(tox):
|
||||||
|
"""
|
||||||
|
Initialization of all callbacks.
|
||||||
|
:param tox: tox instance
|
||||||
|
"""
|
||||||
|
tox.callback_self_connection_status(self_connection_status(), 0)
|
||||||
|
|
||||||
|
tox.callback_friend_message(friend_message(), 0)
|
||||||
|
tox.callback_friend_connection_status(friend_connection_status, 0)
|
||||||
|
tox.callback_friend_request(friend_request, 0)
|
||||||
|
|
||||||
|
tox.callback_file_recv(tox_file_recv(tox), 0)
|
||||||
|
tox.callback_file_recv_chunk(file_recv_chunk, 0)
|
||||||
|
tox.callback_file_chunk_request(file_chunk_request, 0)
|
||||||
|
tox.callback_file_recv_control(file_recv_control, 0)
|
125
file_transfers.py
Normal file
125
file_transfers.py
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
from toxcore_enums_and_consts import TOX_FILE_KIND, TOX_FILE_CONTROL
|
||||||
|
from os.path import basename, getsize
|
||||||
|
from os import remove
|
||||||
|
from time import time
|
||||||
|
from tox import Tox
|
||||||
|
|
||||||
|
|
||||||
|
TOX_FILE_TRANSFER_STATE = {
|
||||||
|
'RUNNING': 0,
|
||||||
|
'PAUSED': 1,
|
||||||
|
'CANCELED': 2,
|
||||||
|
'FINISHED': 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FileTransfer(object):
|
||||||
|
"""
|
||||||
|
Superclass for file transfers
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, path, tox, friend_number, size, file_number=None):
|
||||||
|
self._path = path
|
||||||
|
self._tox = tox
|
||||||
|
self._friend_number = friend_number
|
||||||
|
self.state = TOX_FILE_TRANSFER_STATE['RUNNING']
|
||||||
|
self._file_number = file_number
|
||||||
|
self._creation_time = time()
|
||||||
|
self._size = float(size)
|
||||||
|
self._done = 0
|
||||||
|
|
||||||
|
def set_tox(self, tox):
|
||||||
|
self._tox = tox
|
||||||
|
|
||||||
|
def get_file_number(self):
|
||||||
|
return self._file_number
|
||||||
|
|
||||||
|
def get_friend_number(self):
|
||||||
|
return self._friend_number
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
self.send_control(TOX_FILE_CONTROL['CANCEL'])
|
||||||
|
if hasattr(self, '_file'):
|
||||||
|
self._file.close()
|
||||||
|
|
||||||
|
def cancelled(self):
|
||||||
|
if hasattr(self, '_file'):
|
||||||
|
self._file.close()
|
||||||
|
|
||||||
|
def send_control(self, control):
|
||||||
|
if self._tox.file_control(self._friend_number, self._file_number, control):
|
||||||
|
self.state = control
|
||||||
|
|
||||||
|
def get_file_id(self):
|
||||||
|
return self._tox.file_get_file_id(self._friend_number, self._file_number)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Send file
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
class SendTransfer(FileTransfer):
|
||||||
|
|
||||||
|
def __init__(self, path, tox, friend_number, kind=TOX_FILE_KIND['DATA'], file_id=None):
|
||||||
|
if path is not None:
|
||||||
|
self._file = open(path, 'rb')
|
||||||
|
size = getsize(path)
|
||||||
|
else:
|
||||||
|
size = 0
|
||||||
|
super(SendTransfer, self).__init__(path, tox, friend_number, size)
|
||||||
|
self._file_number = tox.file_send(friend_number, kind, size, file_id,
|
||||||
|
basename(path).encode('utf-8') if path else '')
|
||||||
|
|
||||||
|
def send_chunk(self, position, size):
|
||||||
|
"""
|
||||||
|
Send chunk
|
||||||
|
:param position: start position in file
|
||||||
|
:param size: chunk max size
|
||||||
|
"""
|
||||||
|
if size:
|
||||||
|
self._file.seek(position)
|
||||||
|
data = self._file.read(size)
|
||||||
|
self._tox.file_send_chunk(self._friend_number, self._file_number, position, data)
|
||||||
|
self._done += size
|
||||||
|
else:
|
||||||
|
self._file.close()
|
||||||
|
self.state = TOX_FILE_TRANSFER_STATE['FINISHED']
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Receive file
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
class ReceiveTransfer(FileTransfer):
|
||||||
|
|
||||||
|
def __init__(self, path, tox, friend_number, size, file_number):
|
||||||
|
super(ReceiveTransfer, self).__init__(path, tox, friend_number, size, file_number)
|
||||||
|
self._file = open(self._path, 'wb')
|
||||||
|
self._file.truncate(0)
|
||||||
|
self._file_size = 0
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
super(ReceiveTransfer, self).cancel()
|
||||||
|
remove(self._path)
|
||||||
|
|
||||||
|
def write_chunk(self, position, data):
|
||||||
|
"""
|
||||||
|
Incoming chunk
|
||||||
|
:param position: position in file to save data
|
||||||
|
:param data: raw data (string)
|
||||||
|
"""
|
||||||
|
if data is None:
|
||||||
|
self._file.close()
|
||||||
|
self.state = TOX_FILE_TRANSFER_STATE['FINISHED']
|
||||||
|
else:
|
||||||
|
data = ''.join(chr(x) for x in data)
|
||||||
|
if self._file_size < position:
|
||||||
|
self._file.seek(0, 2)
|
||||||
|
self._file.write('\0' * (position - self._file_size))
|
||||||
|
self._file.seek(position)
|
||||||
|
self._file.write(data)
|
||||||
|
self._file.flush()
|
||||||
|
l = len(data)
|
||||||
|
if position + l > self._file_size:
|
||||||
|
self._file_size = position + l
|
||||||
|
self._done += l
|
45
main.py
Normal file
45
main.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
from bootstrap import node_generator
|
||||||
|
from bot import *
|
||||||
|
from callbacks import init_callbacks
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
class FileBot(object):
|
||||||
|
|
||||||
|
def __init__(self, path):
|
||||||
|
super(FileBot, self).__init__()
|
||||||
|
self.tox = None
|
||||||
|
self.stop = False
|
||||||
|
self.profile = None
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def main(self):
|
||||||
|
self.tox = tox_factory(ProfileHelper.open_profile(self.path))
|
||||||
|
init_callbacks(self.tox)
|
||||||
|
# bootstrap
|
||||||
|
for data in node_generator():
|
||||||
|
self.tox.bootstrap(*data)
|
||||||
|
settings = Settings()
|
||||||
|
self.profile = Bot(self.tox)
|
||||||
|
print 'Iterate'
|
||||||
|
try:
|
||||||
|
while not self.stop:
|
||||||
|
self.tox.iterate()
|
||||||
|
time.sleep(self.tox.iteration_interval() / 1000.0)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print ''
|
||||||
|
settings.save()
|
||||||
|
data = self.tox.get_savedata()
|
||||||
|
ProfileHelper.save_profile(data)
|
||||||
|
del self.tox
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
path = sys.argv[1]
|
||||||
|
bot = FileBot(path)
|
||||||
|
bot.main()
|
||||||
|
else:
|
||||||
|
raise IOError('Path to save file not found')
|
||||||
|
|
1
settings.json
Normal file
1
settings.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"write": ["56A1ADE4B65B86BCD51CC73E2CD4E542179F47959FE3E0E21B4B0ACDADE51855D34D34D37CB5"], "auto_rights": "r", "master": [], "read": ["56A1ADE4B65B86BCD51CC73E2CD4E542179F47959FE3E0E21B4B0ACDADE51855D34D34D37CB5"], "folder": "/home/tox_user/tox/shared", "delete": ["56A1ADE4B65B86BCD51CC73E2CD4E542179F47959FE3E0E21B4B0ACDADE51855D34D34D37CB5"]}
|
63
settings.py
Normal file
63
settings.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
from platform import system
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import locale
|
||||||
|
from util import Singleton, curr_directory
|
||||||
|
from toxcore_enums_and_consts import *
|
||||||
|
|
||||||
|
|
||||||
|
class Settings(Singleton, dict):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.path = curr_directory() + '/settings.json'
|
||||||
|
if os.path.isfile(self.path):
|
||||||
|
with open(self.path) as fl:
|
||||||
|
data = fl.read()
|
||||||
|
super(self.__class__, self).__init__(json.loads(data))
|
||||||
|
else:
|
||||||
|
super(self.__class__, self).__init__(Settings.get_default_settings())
|
||||||
|
self.save()
|
||||||
|
self['read'] = map(lambda x: x[:TOX_PUBLIC_KEY_SIZE * 2], self['read'])
|
||||||
|
self['write'] = map(lambda x: x[:TOX_PUBLIC_KEY_SIZE * 2], self['write'])
|
||||||
|
self['delete'] = map(lambda x: x[:TOX_PUBLIC_KEY_SIZE * 2], self['delete'])
|
||||||
|
self['master'] = map(lambda x: x[:TOX_PUBLIC_KEY_SIZE * 2], self['master'])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_default_settings():
|
||||||
|
return {
|
||||||
|
'read': [],
|
||||||
|
'write': [],
|
||||||
|
'delete': [],
|
||||||
|
'master': [],
|
||||||
|
'folder': curr_directory(),
|
||||||
|
'auto_rights': 'r'
|
||||||
|
}
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
print 'Saving'
|
||||||
|
text = json.dumps(self)
|
||||||
|
with open(self.path, 'w') as fl:
|
||||||
|
fl.write(text)
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileHelper(object):
|
||||||
|
"""
|
||||||
|
Class with static methods for search, load and save profiles
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def open_profile(path):
|
||||||
|
path = path.decode(locale.getpreferredencoding())
|
||||||
|
ProfileHelper._path = path
|
||||||
|
with open(ProfileHelper._path, 'rb') as fl:
|
||||||
|
data = fl.read()
|
||||||
|
if data:
|
||||||
|
return data
|
||||||
|
else:
|
||||||
|
raise IOError('Save file has zero size!')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def save_profile(data):
|
||||||
|
with open(ProfileHelper._path, 'wb') as fl:
|
||||||
|
fl.write(data)
|
||||||
|
|
209
toxcore_enums_and_consts.py
Normal file
209
toxcore_enums_and_consts.py
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
TOX_USER_STATUS = {
|
||||||
|
'NONE': 0,
|
||||||
|
'AWAY': 1,
|
||||||
|
'BUSY': 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_MESSAGE_TYPE = {
|
||||||
|
'NORMAL': 0,
|
||||||
|
'ACTION': 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_PROXY_TYPE = {
|
||||||
|
'NONE': 0,
|
||||||
|
'HTTP': 1,
|
||||||
|
'SOCKS5': 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_SAVEDATA_TYPE = {
|
||||||
|
'NONE': 0,
|
||||||
|
'TOX_SAVE': 1,
|
||||||
|
'SECRET_KEY': 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_OPTIONS_NEW = {
|
||||||
|
'OK': 0,
|
||||||
|
'MALLOC': 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_NEW = {
|
||||||
|
'OK': 0,
|
||||||
|
'NULL': 1,
|
||||||
|
'MALLOC': 2,
|
||||||
|
'PORT_ALLOC': 3,
|
||||||
|
'PROXY_BAD_TYPE': 4,
|
||||||
|
'PROXY_BAD_HOST': 5,
|
||||||
|
'PROXY_BAD_PORT': 6,
|
||||||
|
'PROXY_NOT_FOUND': 7,
|
||||||
|
'LOAD_ENCRYPTED': 8,
|
||||||
|
'LOAD_BAD_FORMAT': 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_BOOTSTRAP = {
|
||||||
|
'OK': 0,
|
||||||
|
'NULL': 1,
|
||||||
|
'BAD_HOST': 2,
|
||||||
|
'BAD_PORT': 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_CONNECTION = {
|
||||||
|
'NONE': 0,
|
||||||
|
'TCP': 1,
|
||||||
|
'UDP': 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_SET_INFO = {
|
||||||
|
'OK': 0,
|
||||||
|
'NULL': 1,
|
||||||
|
'TOO_LONG': 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_FRIEND_ADD = {
|
||||||
|
'OK': 0,
|
||||||
|
'NULL': 1,
|
||||||
|
'TOO_LONG': 2,
|
||||||
|
'NO_MESSAGE': 3,
|
||||||
|
'OWN_KEY': 4,
|
||||||
|
'ALREADY_SENT': 5,
|
||||||
|
'BAD_CHECKSUM': 6,
|
||||||
|
'SET_NEW_NOSPAM': 7,
|
||||||
|
'MALLOC': 8,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_FRIEND_DELETE = {
|
||||||
|
'OK': 0,
|
||||||
|
'FRIEND_NOT_FOUND': 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_FRIEND_BY_PUBLIC_KEY = {
|
||||||
|
'OK': 0,
|
||||||
|
'NULL': 1,
|
||||||
|
'NOT_FOUND': 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_FRIEND_GET_PUBLIC_KEY = {
|
||||||
|
'OK': 0,
|
||||||
|
'FRIEND_NOT_FOUND': 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_FRIEND_GET_LAST_ONLINE = {
|
||||||
|
'OK': 0,
|
||||||
|
'FRIEND_NOT_FOUND': 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_FRIEND_QUERY = {
|
||||||
|
'OK': 0,
|
||||||
|
'NULL': 1,
|
||||||
|
'FRIEND_NOT_FOUND': 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_SET_TYPING = {
|
||||||
|
'OK': 0,
|
||||||
|
'FRIEND_NOT_FOUND': 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_FRIEND_SEND_MESSAGE = {
|
||||||
|
'OK': 0,
|
||||||
|
'NULL': 1,
|
||||||
|
'FRIEND_NOT_FOUND': 2,
|
||||||
|
'FRIEND_NOT_CONNECTED': 3,
|
||||||
|
'SENDQ': 4,
|
||||||
|
'TOO_LONG': 5,
|
||||||
|
'EMPTY': 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_FILE_KIND = {
|
||||||
|
'DATA': 0,
|
||||||
|
'AVATAR': 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_FILE_CONTROL = {
|
||||||
|
'RESUME': 0,
|
||||||
|
'PAUSE': 1,
|
||||||
|
'CANCEL': 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_FILE_CONTROL = {
|
||||||
|
'OK': 0,
|
||||||
|
'FRIEND_NOT_FOUND': 1,
|
||||||
|
'FRIEND_NOT_CONNECTED': 2,
|
||||||
|
'NOT_FOUND': 3,
|
||||||
|
'NOT_PAUSED': 4,
|
||||||
|
'DENIED': 5,
|
||||||
|
'ALREADY_PAUSED': 6,
|
||||||
|
'SENDQ': 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_FILE_SEEK = {
|
||||||
|
'OK': 0,
|
||||||
|
'FRIEND_NOT_FOUND': 1,
|
||||||
|
'FRIEND_NOT_CONNECTED': 2,
|
||||||
|
'NOT_FOUND': 3,
|
||||||
|
'DENIED': 4,
|
||||||
|
'INVALID_POSITION': 5,
|
||||||
|
'SENDQ': 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_FILE_GET = {
|
||||||
|
'OK': 0,
|
||||||
|
'NULL': 1,
|
||||||
|
'FRIEND_NOT_FOUND': 2,
|
||||||
|
'NOT_FOUND': 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_FILE_SEND = {
|
||||||
|
'OK': 0,
|
||||||
|
'NULL': 1,
|
||||||
|
'FRIEND_NOT_FOUND': 2,
|
||||||
|
'FRIEND_NOT_CONNECTED': 3,
|
||||||
|
'NAME_TOO_LONG': 4,
|
||||||
|
'TOO_MANY': 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_FILE_SEND_CHUNK = {
|
||||||
|
'OK': 0,
|
||||||
|
'NULL': 1,
|
||||||
|
'FRIEND_NOT_FOUND': 2,
|
||||||
|
'FRIEND_NOT_CONNECTED': 3,
|
||||||
|
'NOT_FOUND': 4,
|
||||||
|
'NOT_TRANSFERRING': 5,
|
||||||
|
'INVALID_LENGTH': 6,
|
||||||
|
'SENDQ': 7,
|
||||||
|
'WRONG_POSITION': 8,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_FRIEND_CUSTOM_PACKET = {
|
||||||
|
'OK': 0,
|
||||||
|
'NULL': 1,
|
||||||
|
'FRIEND_NOT_FOUND': 2,
|
||||||
|
'FRIEND_NOT_CONNECTED': 3,
|
||||||
|
'INVALID': 4,
|
||||||
|
'EMPTY': 5,
|
||||||
|
'TOO_LONG': 6,
|
||||||
|
'SENDQ': 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_GET_PORT = {
|
||||||
|
'OK': 0,
|
||||||
|
'NOT_BOUND': 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_PUBLIC_KEY_SIZE = 32
|
||||||
|
|
||||||
|
TOX_ADDRESS_SIZE = TOX_PUBLIC_KEY_SIZE + 6
|
||||||
|
|
||||||
|
TOX_MAX_FRIEND_REQUEST_LENGTH = 1016
|
||||||
|
|
||||||
|
TOX_MAX_MESSAGE_LENGTH = 1372
|
||||||
|
|
||||||
|
TOX_MAX_NAME_LENGTH = 128
|
||||||
|
|
||||||
|
TOX_MAX_STATUS_MESSAGE_LENGTH = 1007
|
||||||
|
|
||||||
|
TOX_SECRET_KEY_SIZE = 32
|
||||||
|
|
||||||
|
TOX_FILE_ID_LENGTH = 32
|
||||||
|
|
||||||
|
TOX_HASH_LENGTH = 32
|
||||||
|
|
||||||
|
TOX_MAX_CUSTOM_PACKET_SIZE = 1373
|
22
util.py
Normal file
22
util.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def log(data):
|
||||||
|
with open(curr_directory() + '/logs.log', 'a') as fl:
|
||||||
|
fl.write(str(data) + '\n')
|
||||||
|
|
||||||
|
|
||||||
|
def curr_directory():
|
||||||
|
return os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
class Singleton(object):
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
if not hasattr(cls, '_instance'):
|
||||||
|
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_instance(cls):
|
||||||
|
return cls._instance
|
Loading…
Reference in New Issue
Block a user