import sys import socket import string import select import re import pickle from tox import Tox, ToxAV from time import sleep from os.path import exists from threading import Thread SERVER = ["54.199.139.199", 33445, "7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029"] GROUP_BOT = '56A1ADE4B65B86BCD51CC73E2CD4E542179F47959FE3E0E21B4B0ACDADE5185520B3E6FC5D64' IRC_HOST = "irc.freenode.net" IRC_PORT = 6667 NAME = NICK = IDENT = REALNAME = "SyncBot" CHANNEL = '#tox-ontopic' MEMORY_DB = 'memory.pickle' class AV(ToxAV): def __init__(self, core, width, height): super(AV, self).__init__(core, width, height) self.core = self.get_tox() self.daemon = True self.stop = True self.call_type = self.TypeAudio def on_invite(self): self.call_type = self.get_peer_transmission_type(0) print("Incoming %s call from %s ..." % ( "video" if self.call_type == self.TypeVideo else "audio", self.core.get_name(self.get_peer_id(0)))) self.answer(self.call_type) print("Answered, in call...") def on_start(self): self.call_type = self.get_peer_transmission_type(0) self.prepare_transmission(True) self.stop = False self.a_thread = Thread(target=self.audio_transmission) self.a_thread.daemon = True self.a_thread.start() if self.call_type == self.TypeVideo: self.v_thread = Thread(target=self.video_transmission) self.v_thread.daemon = True self.v_thread.start() def on_end(self): self.stop = True self.kill_transmission() self.a_thread.join() if self.call_type == self.TypeVideo: self.v_thread.join() print 'Call ended' def on_peer_timeout(self): self.stop_call() def audio_transmission(self): print("Starting audio transmission...") while not self.stop: try: ret = self.recv_audio() if ret: sys.stdout.write('.') sys.stdout.flush() self.send_audio(ret["size"], ret["data"]) except Exception as e: print(e) sleep(0.001) def video_transmission(self): print("Starting video transmission...") while not self.stop: try: vret = self.recv_video() if vret: sys.stdout.write('*') sys.stdout.flush() self.send_video(vret['data']) except Exception as e: print(e) sleep(0.001) class SyncBot(Tox): def __init__(self): if exists('data'): self.load_from_file('data') self.av = AV(self, 480, 320) self.connect() self.set_name("SyncBot") self.set_status_message("Send me a message with the word 'invite'") print('ID: %s' % self.get_address()) self.readbuffer = "" self.tox_group_id = None self.irc_init() self.memory = {} if exists(MEMORY_DB): with open(MEMORY_DB, 'r') as f: self.memory = pickle.load(f) def irc_init(self): self.irc = socket.socket() self.irc.connect((IRC_HOST, IRC_PORT)) self.irc.send("NICK %s\r\n" % NICK) self.irc.send("USER %s %s bla :%s\r\n" % (IDENT, IRC_HOST, REALNAME)) def connect(self): print('connecting...') self.bootstrap_from_address(SERVER[0], 1, SERVER[1], SERVER[2]) def ensure_exe(self, func, args): count = 0 THRESHOLD = 50 while True: try: return func(*args) except: assert count < THRESHOLD count += 1 for i in range(10): self.do() sleep(0.02) def loop(self): checked = False self.joined = False self.request = False try: while True: status = self.isconnected() if not checked and status: print('Connected to DHT.') checked = True try: self.bid = self.get_friend_id(GROUP_BOT) except: self.ensure_exe(self.add_friend, (GROUP_BOT, "Hi")) self.bid = self.get_friend_id(GROUP_BOT) if checked and not status: print('Disconnected from DHT.') self.connect() checked = False readable, _, _ = select.select([self.irc], [], [], 0.01) if readable: self.readbuffer += self.irc.recv(4096) lines = self.readbuffer.split('\n') self.readbuffer = lines.pop() for line in lines: rx = re.match(r':(.*?)!.*? PRIVMSG %s :(.*?)\r' % CHANNEL, line, re.S) if rx: print('IRC> %s: %s' % rx.groups()) msg = '[%s]: %s' % rx.groups() content = rx.group(2) if content[1:].startswith('ACTION '): action = '[%s]: %s' % (rx.group(1), rx.group(2)[8:-1]) self.ensure_exe(self.group_action_send, (self.tox_group_id, action)) elif self.tox_group_id != None: self.ensure_exe(self.group_message_send, (self.tox_group_id, msg)) if content.startswith('^'): self.handle_command(content) l = line.rstrip().split() if l[0] == "PING": self.irc_send("PONG %s\r\n" % l[1]) if l[1] == "376": self.irc.send("JOIN %s\r\n" % CHANNEL) self.do() except KeyboardInterrupt: self.save_to_file('data') def irc_send(self, msg): success = False while not success: try: self.irc.send(msg) success = True break except socket.error: self.irc_init() sleep(1) def on_connection_status(self, friendId, status): if not self.request and not self.joined \ and friendId == self.bid and status: print('Groupbot online, trying to join group chat.') self.request = True self.ensure_exe(self.send_message, (self.bid, 'invite')) def on_group_invite(self, friendid, pk): if not self.joined: self.joined = True self.tox_group_id = self.join_groupchat(friendid, pk) print('Joined groupchat.') def on_group_message(self, groupnumber, friendgroupnumber, message): name = self.group_peername(groupnumber, friendgroupnumber) if len(name) and name != NAME: print('TOX> %s: %s' % (name, message)) if message.startswith('>'): message = '\x0309%s\x03' % message self.irc_send('PRIVMSG %s :[%s]: %s\r\n' % (CHANNEL, name, message)) if message.startswith('^'): self.handle_command(message) def on_group_action(self, groupnumber, friendgroupnumber, action): name = self.group_peername(groupnumber, friendgroupnumber) if len(name) and name != NAME: print('TOX> %s: %s' % (name, action)) if action.startswith('>'): action = '\x0309%s\x03' % action self.irc_send('PRIVMSG %s :\x01ACTION [%s]: %s\x01\r\n' % (CHANNEL, name, action)) def on_friend_request(self, pk, message): print('Friend request from %s: %s' % (pk, message)) self.add_friend_norequest(pk) print('Accepted.') def on_friend_message(self, friendid, message): if message == 'invite': if not self.tox_group_id is None: print('Inviting %s' % self.get_name(friendid)) self.invite_friend(friendid, self.tox_group_id) return else: message = 'Waiting for GroupBot, please try again in 1 min.' self.ensure_exe(self.send_message, (friendid, message)) def send_both(self, content): self.ensure_exe(self.group_message_send, (self.tox_group_id, content)) self.irc_send('PRIVMSG %s :%s\r\n' % (CHANNEL, content)) def handle_command(self, cmd): cmd = cmd[1:] if cmd in ['syncbot', 'echobot']: self.send_both(self.get_address()) elif cmd == 'resync': sys.exit(0) elif cmd.startswith('remember '): args = cmd[9:].split(' ') subject = args[0] desc = ' '.join(args[1:]) self.memory[subject] = desc with open(MEMORY_DB, 'w') as f: pickle.dump(self.memory, f) self.send_both('Remembering ^%s: %s' % (subject, desc)) elif self.memory.has_key(cmd): self.send_both(self.memory[cmd]) t = SyncBot() t.loop()