Пример #1
0
def slack_thread():
    print "in spawned slack thread"
    sc = SlackClient(SLACK_TOKEN)

    # Join all channels
    chans = json.loads(sc.api_call("channels.list"))
    chans = chans['channels']
    for chan in chans:
        sc.api_call("channels.join", name=chan['name'])

    if sc.rtm_connect():
        while True:
            msgs = sc.rtm_read()
            print "Got msgs == " + str(msgs)
            for msg in msgs:
                if msg.get('ok') is not None:
                    print "Server acknowledged our message: " + str(msg)
                elif msg.get('type') is not None:
                    if msg['type'] == 'hello':
                        print "Server said hello: " + str(msg)
                    elif msg['type'] == 'channel_created':
                        print "Channel created: " + str(msg)
                        print sc.api_call("channels.join", name=msg['channel']['name'])
                    elif msg['type'] == 'message':
                        print "Got a message: " + str(msg)
                        sc.rtm_send_message(msg['channel'], str(msg))
                    else:
                        print "Got RTM message with unknown type field: " + str(msg)
                else:
                    print "Unknown message format in RTM: " + str(msg)
                    
            gevent.sleep(1)
    else:
        print "Connection Failed, invalid token?"
Пример #2
0
class SlackThread(threading.Thread):
    def __init__(self, queue, config):
        super(SlackThread, self).__init__()
        self.daemon = True
        self.queue = queue
        self._config = config
        self.conn = None

    def run(self):
        try:
            print 'Connecting to slack'
            self.conn = SlackClient(self._config['token'])
            if not self.conn.rtm_connect():
                return

            self.parseLoginData(self.conn.server.login_data)

            print 'Connected to slack'
            self.conn.server.websocket.sock.setblocking(True)
            while True:
                for event in self.conn.rtm_read():
                    self.queue.put({'type': 'slack.event', 'data': event})
        except Exception as e:
            print 'SLACK RUNLOOP ERROR: %s' % e

        self.conn = None

    def parseLoginData(self, loginData):
        for user in loginData.get('users', []):
            if user.get('deleted', False):
                continue
            if user.get('is_bot', False):
                continue
            self.queue.put({'type': 'slack.join', 'data': {'id': user['id'], 'name': user['name']}})
Пример #3
0
class SlackROS():
    # Must have __init__(self) function for a class, similar to a C++ class constructor.
    def __init__(self):
        # Get the ~private namespace parameters from command line or launch file.
        self.token = rospy.get_param('~token', 'xoxp-123456789')
        self.channel = rospy.get_param('~channel', 'G1234567Q')
        self.username = rospy.get_param('~username', 'ros-bot')
	
        # Create a publisher for our custom message.
        pub = rospy.Publisher('from_slack_to_ros', String, queue_size=10)
	rospy.Subscriber("from_ros_to_slack", String, self.callback)

	# Create the slack client
	self.sc = SlackClient(self.token)

	if self.sc.rtm_connect():

            # Main while loop.
            while not rospy.is_shutdown():
                for reply in self.sc.rtm_read():
                    if "type" in reply and "user" in reply:
                        #print reply
                        if reply["type"] == "message" and reply["channel"] == self.channel:
                            pub.publish(reply["text"])
                
	        # Sleep for a while before publishing new messages. Division is so rate != period.
                rospy.sleep(2.0)

    def callback(self, data):
	self.sc.api_call(
    	    "chat.postMessage", channel=self.channel, text=data.data,
    	    username=self.username, icon_emoji=':robot_face:'
	)
Пример #4
0
def marcopolo_bot():
    # Insert API token from Slack
    # My token was disabled as soon as I uploaded it to Git, another token must be generated 
    token = 'xoxb-55268990450-WzfkM0A5ufihTTElZ8k6sC33'
    # Creating the slackclient object/instance 
    sc = SlackClient(token)
    # Connect to Slack
    if sc.rtm_connect():
        
        while True:
            # Read latest messages as long as connected to Slack
            rtm_responses = sc.rtm_read()
            # Check every instance of potential response and check for text response 
            for rtm_response in rtm_responses:
                if 'text' in rtm_response:
                    # If response is found as 'marco', call API function to respond with 'polo'
                    if rtm_response['text'] == 'marco':
                        sc.api_call(
                            "chat.postMessage", channel="marco-polo", text="polo",
                            username='******', icon_emoji=':robot_face:'
                        )
            
            time.sleep(1)

    else:
        quit()
Пример #5
0
    def main(self):
        token = os.environ.get("SLACK_TOKEN")

        slack_client = SlackClient(token)

        if slack_client.rtm_connect():

            while True:
                new_evts = slack_client.rtm_read()

                for evt in new_evts:
                    #print evt

                    if 'type' in evt:

                        if str(evt["type"]) == "message" and "text" in evt:
                            # this is where the logic for the human input text is placed.
                            # we also get more information from the JSON
                            keyword = True
                            channel = evt['channel']
                            response = evt['text']
                            print response

                    elif 'reply_to' in evt:

                        #this is where the logic for the chat bot is placed.
                        slack_client.rtm_send_message('This is where the messages will go', 'Hello World')


        else:
            print "Failed."
Пример #6
0
def bot():
    try:
        slack_client = SlackClient(token=config["slack"]["token"])
        slack_client.rtm_connect()

        bot_info = json.loads(slack_client.api_call("auth.test").decode("utf-8"))
        last_ping = 0

        cache_emoji_list(slack_client)

        while True:
            last_ping = autoping(slack_client, last_ping)

            process_queued_responses(slack_client)

            for event in slack_client.rtm_read():
                print(event)
                event_type = event.get("type")

                if event_type == "message":
                    process_message_event(slack_client, bot_info, event)

                time.sleep(0.1)
    except KeyboardInterrupt:
        sys.exit(0)
Пример #7
0
class RtmBot(object):
    def __init__(self, token):
        self.last_ping = 0
        self.token = token
        self.bot_plugins = []
        self.slack_client = None
        self.exit = False
    def connect(self):
        """Convenience method that creates Server instance"""
        self.slack_client = SlackClient(self.token)
        self.slack_client.rtm_connect()
    def start(self):
        self.connect()
        self.load_plugins()
        while True and not self.exit:
            for reply in self.slack_client.rtm_read():
                self.input(reply)
            self.crons()
            self.output()
            self.autoping()
            time.sleep(.1)
    def autoping(self):
        #hardcode the interval to 3 seconds
        now = int(time.time())
        if now > self.last_ping + 3:
            self.slack_client.server.ping()
            self.last_ping = now
    def input(self, data):
        if "type" in data:
            function_name = "process_" + data["type"]
            dbg("got {}".format(function_name))
            for plugin in self.bot_plugins:
                plugin.register_jobs()
                plugin.do(function_name, data)
    def output(self):
        for plugin in self.bot_plugins:
            if plugin.name == 'robbie':
                self.exit = plugin.exit
            limiter = False
            for output in plugin.do_output():
                channel = self.slack_client.server.channels.find(output[0])
                if channel != None and output[1] != None:
                    if limiter == True:
                        time.sleep(.1)
                        limiter = False
                    message = output[1].encode('utf-8','ignore')
                    channel.send_message("{}".format(message))
                    limiter = True
    def crons(self):
        for plugin in self.bot_plugins:
            plugin.do_jobs()
    def load_plugins(self):
        for plugin in glob.glob(directory+'/plugins/*'):
            sys.path.insert(0, plugin)
            sys.path.insert(0, directory+'/plugins/')
        for plugin in glob.glob(directory+'/plugins/*.py') + glob.glob(directory+'/plugins/*/*.py'):
            logging.info(plugin)
            name = plugin.split('/')[-1][:-3]
#            try:
            self.bot_plugins.append(Plugin(name))
Пример #8
0
class TachikomaFrontend(pykka.ThreadingActor, CoreListener):
	def new_slack_client(self):
		self.sc = SlackClient(self.slackToken)
		if not self.sc.rtm_connect():
			raise Exception("Bad Slack token?")
		logger.info("New Slack client started")

	def __init__(self, config, core):
		super(TachikomaFrontend, self).__init__()
		self.daemon = True
		self.slackToken = config['tachikoma']['slack_token'],
		self.core = core
		self.new_slack_client()
		thread.start_new_thread(self.doSlack, ())

	def doSlackRead(self, last_track_told):
		try:
			items = self.sc.rtm_read()
		except Exception, e:
			logger.info("Exception from Slack: %r", e)
			time.sleep(1)
			self.new_slack_client()
			return last_track_told
		logger.debug("info %r", items)
		if items != []:
			try:
				current_track = self.core.playback.get_current_track().get(3)
			except pykka.Timeout, e:
				logger.warning("Failure to get current track: %r", e)
				current_track = None
			return self.doSlackLoop(last_track_told, current_track, items)
Пример #9
0
class Robot(object):
    def __init__(self):
        self.client = SlackClient(SLACK_TOKEN)
#        self.brain = RedisBrain()
        self.apps, self.docs = self.load_apps()

    def load_apps(self):
        docs = ['='*14, '생협봇  사용방법\n(원하시는 기능이 있을경우 건의하시면 추가해드립니다!)', '='*14]
        apps = {}

        for name in APPS:
            app = import_module('apps.%s' % name)
            if name != 'system':
                docs.append(
                '   !%s: %s' % (', '.join(app.run.commands), app.run.__doc__)
                )
            for command in app.run.commands:
                apps[command] = app

        return apps, docs

    def handle_messages(self, messages):
        for channel, text, user in messages:
            command, payloads = self.extract_command(text)
            if not command:
                continue

            app = self.apps.get(command, None)
            if not app:
                continue
            pool.apply_async(func=app.run, args=(self, channel, payloads, user))

    def extract_messages(self, events):
        messages = []
        for e in events:
            user = e.get('user','')
            channel = e.get('channel', '')
            text = e.get('text', '')
            if channel and text:
                messages.append((channel, text, user))
        return messages

    def extract_command(self, text):
        if CMD_PREFIX != text[0]:
            return (None, None)

        tokens = text.split(' ', 1)
        if 1 < len(tokens):
            return tokens[0][1:], tokens[1]
        else:
            return (text[1:], '')

    def run(self):
        if self.client.rtm_connect():
            while True:
                events = self.client.rtm_read()
                if events:
                    messages = self.extract_messages(events)
                    self.handle_messages(messages)
                gevent.sleep(0.3)
Пример #10
0
class SlackMediator(object):
    def __init__(self, token):
        self.token = token
        self.slack_client = SlackClient(self.token)
        self.time_keeper = _TimeKeeper()

    def time_keeping(self):
        self.time_keeper.reset()
        return iter(self.time_keeper)

    def wait(self):
        self.time_keeper.wait()

    def read(self):
        return self.slack_client.rtm_read()

    def send(self, output, wait_itr):
        channel_id, message = output
        channel = self.find_channel(channel_id)
        if channel is not None and message is not None:
            next(wait_itr)
            channel.send_message(message)

    def ping(self):
        return self.slack_client.server.ping()

    def find_channel(self, name):
        return self.slack_client.server.channels.find(name)

    def connect(self):
        """Convenience method that creates Server instance"""
        logger.info("connect to slack ..")
        self.slack_client.rtm_connect()
Пример #11
0
def main(args):
	global sc
	
	for i in range(NUM_WORKERS):
		t = threading.Thread(target=worker)
		t.daemon = True
		t.start()

	for n in range(NUM_TRY):
		sc = SlackClient(TOKEN)
		if sc.rtm_connect():
			while True:
				try:
					records = sc.rtm_read()
				except:
					print "接続が切断されました。再接続します。試行回数: %d" % (n + 1)
					break
				for record in records:
					if "file" in record:
						fileinfo = record["file"]["id"]
						filedata = sc.api_call("files.info", file=fileinfo)
						if fileinfo not in memo:
							q.put(filedata["file"])
							memo.append(fileinfo)
				time.sleep(WAIT_TIME)
		else:
			print "接続に失敗しました。TOKENが間違っていませんか?"
			time.sleep(60)
Пример #12
0
def slack():
	'''Slack rtm reader started in seprate thread'''
	logs.write("In slack function in new thread", 'working')
	sc = SlackClient(token)
	if sc.rtm_connect():
		logs.write("Connected to rtm socket", 'success')
    	while True:
    		time.sleep(0.1)
    		#Get message from rtm socket
        	message=sc.rtm_read()
        	#If the message isn't empty
        	if message!=[]:
        		#If the message is text as opposed to a notification. Eventually plan to have other kinds of messages in a backend communications channel.
        		if message[0].keys()[0]=='text':
        			command=message[0].values()[0]
        			logs.write(command,'working')
        			#The commands are json or plain text. If it isn't a json backend command, interpret it as a "normal" command
        			try:
        				command=json.loads(command)
        			except ValueError:
        				command=[{'type':'command'},{'devices':'all'},{'action':"{0}".format(command)}]
        			#Json slack commands or management can eventually be formatted like so: [{"type":"management/command",{"devices":"all/mobile/desktop/network/device name"},{"action":"message content"}]
        			#Not sure if I want to do that in the backend or command channel or what really, but I'm definitely working with it. 
        			commandtype=command[0]
        			devices=command[1]
        			action=command[2]
        			#Replace thisdevicename with whatever you want to name yours in the W.I.L.L slack network (obviously)
        			if devices.values()[0]=='all' or devices.values()[0]=="thisdevicename":
	        			logs.write("Checking local W.I.L.L server", 'trying')
	        			#Hit W.I.L.L with the command. This is also where you could add exceptions or easter eggs
	        			answer=requests.get('http://127.0.0.1:5000/?context=command&command={0}'.format(action.values()[0])).text
	        			print sc.api_call( "chat.postMessage", channel="#w_i_l_l", text="{0}".format(answer), username='******')
	else:
		logs.write("Connection Failed, invalid token?", 'error')
Пример #13
0
def slack():
	logs.write("In slack function in new thread", 'working')
	sc = SlackClient(token)
	if sc.rtm_connect():
		logs.write("Connected to rtm socket", 'success')
    	while True:
    		time.sleep(0.1)
        	message=sc.rtm_read()
        	if message!=[]:
        		if message[0].keys()[0]=='text':
        			command=message[0].values()[0]
        			logs.write(command,'working')
        			try:
        				command=json.loads(command)
        			except ValueError:
        				command=[{'type':'command'},{'devices':'all'},{'action':"{0}".format(command)}]
        			commandtype=command[0]
        			devices=command[1]
        			action=command[2]
        			if devices.values()[0]=='all' or devices.values()[0]=="XPS":
	        			logs.write("Checking local W.I.L.L server", 'trying')
	        			answer=requests.get('http://127.0.0.1:5000/?context=command&command={0}'.format(action.values()[0])).text
	        			print sc.api_call( "chat.postMessage", channel="#w_i_l_l", text="{0}".format(answer), username='******')
	else:
		logs.write("Connection Failed, invalid token?", 'error')
Пример #14
0
class RtmBot(object):
    def __init__(self, token):
        self.last_ping = 0
        self.token = token
        self.bot_plugins = []
        self.slack_client = None
    def connect(self):
        """Convenience method that creates Server instance"""
        self.slack_client = SlackClient(self.token)
        self.slack_client.rtm_connect()
    def start(self):
        self.connect()
        self.load_plugins()
        while True:
            for reply in self.slack_client.rtm_read():
                self.input(reply)
            self.crons()
            self.output()
            self.autoping()
            time.sleep(.1)
    def autoping(self):
        #hardcode the interval to 3 seconds
        now = int(time.time())
        if now > self.last_ping + 3:
            self.slack_client.server.ping()
            self.last_ping = now
    def input(self, data):
        if "type" in data:
            function_name = "process_" + data["type"]
            dbg("got {}".format(function_name))
            for plugin in self.bot_plugins:
                plugin.register_jobs()
                plugin.do(function_name, data)
    def output(self):
        for plugin in self.bot_plugins:
            limiter = False
            for output in plugin.do_output():
                channel = self.slack_client.server.channels.find(output[0])
                if channel is not None and output[1] is not None:
                    if limiter is True:
                        time.sleep(.1)
                        limiter = False
                    message = output[1].encode('utf-8')
                    channel.send_message("{}".format(message.decode('utf-8')))
                    limiter = True
    def crons(self):
        for plugin in self.bot_plugins:
            plugin.do_jobs()
    def load_plugins(self):
        plugin_dir = os.path.join(directory, 'plugins')
        for plugin in glob.glob(os.path.join(plugin_dir, '*')):
            sys.path.insert(0, plugin)
            sys.path.insert(0, plugin_dir)
        for plugin in glob.glob(os.path.join(plugin_dir, '*.py')) + glob.glob(os.path.join(plugin_dir, '*', '*.py')):
            logging.info(plugin)
            name = plugin.split(os.sep)[-1][:-3]
            try:
                self.bot_plugins.append(Plugin(name))
            except:
                print("error loading plugin {}".format(name))
Пример #15
0
def main():
    sc = SlackClient(BOT_TOKEN)
    if sc.rtm_connect():
        # print sc.server.channels
        bot_id = sc.server.users.find(bot).id
        while True:
            data = sc.rtm_read()
            if data != []:
                print data[0]
                if "type" in data[0] and data[0]["type"] == "message" and "subtype" not in data[0]:
                    if data[0]["user"] != bot_id:
                        text =  data[0]["text"]
                        channel_id = data[0]["channel"]
                        channel = sc.server.channels.find(channel_id)
                        if phrase in text:
                            try:
                                test = text.replace(phrase, "")
                                result = Parser().parse(test)
                                sc.rtm_send_message(channel.name,"Rolling a '%s' and got a result of %s"%(test,result.sum))
                                #TODO: Support message level saving such that a user could track history of roll
                            except Exception as e:
                                sc.rtm_send_message(channel.name,"ROLL FAILED: %s"%e.message)

            time.sleep(1)
    else:
        print "Connection Failed, invalid token?"
Пример #16
0
def main():
    sc = SlackClient(SLACK_TOKEN)
    rt = RecognitionTracker()
    if sc.rtm_connect():
        while True:
            messages = sc.rtm_read()
            for message in messages:
                message_to_write = None

                # It only watches the channels it's in, but to be extra
                # careful here.
                channel = message.get("channel")
                if not channel or channel not in CHANNEL_INFO.keys():
                    continue

                # The first time you get here after 10AM on a new day, print
                # out some random results.
                current_date = (datetime.datetime.now() -
                                datetime.timedelta(hours=17)).date()
                if current_date != rt.current_date:
                    # Write the daily message to every channel
                    for channel_key in CHANNEL_INFO.iterkeys():
                        possible_message_to_write = rt.get_daily(channel_key)
                        if possible_message_to_write:
                            sc.api_call(
                                "chat.postMessage",
                                channel=channel_key,
                                username=CHANNEL_INFO[channel_key]['name'],
                                icon_emoji=CHANNEL_INFO[channel_key]['emoji'],
                                attachments=possible_message_to_write
                            )

                    rt.current_date = current_date

                command_type = get_command_type(message)
                if command_type == "thanks":
                    message_to_write = rt.give_thanks(message['text'],
                                                      message['user'],
                                                      channel)

                elif command_type == "summary":
                    message_to_write = rt.get_summary(channel)

                elif command_type == "help":
                    message_to_write = rt.get_help(channel)

                elif command_type == "today":
                    message_to_write = rt.get_daily(channel,
                                                    override=True)

                if message_to_write:
                    sc.api_call("chat.postMessage", channel=channel,
                                username=CHANNEL_INFO[channel]['name'],
                                icon_emoji=CHANNEL_INFO[channel]['emoji'],
                                attachments=message_to_write)

                time.sleep(1)
    else:
        print "Oh noes, the sadness commences!"
Пример #17
0
class RtmBot(object):
    def __init__(self):
        self.last_ping = 0
        self.token = settings.SLACK_TOKEN
        self.bot_plugins = []
        self.slack_client = None

    def connect(self):
        """Convenience method that creates Server instance"""
        self.slack_client = SlackClient(self.token)
        self.slack_client.rtm_connect()

    def start(self):
        self.connect()
        self.load_plugins()
        while True:
            for reply in self.slack_client.rtm_read():
                self.input(reply)
            self.crons()
            self.output()
            self.autoping()
            time.sleep(.1)

    def autoping(self):
        # hardcode the interval to 3 seconds
        now = int(time.time())
        if now > self.last_ping + 3:
            self.slack_client.server.ping()
            self.last_ping = now

    def input(self, data):
        if "type" in data:
            function_name = "process_" + data["type"]
            if function_name != "process_pong":
                dbg("got {}".format(function_name))
            for plugin in self.bot_plugins:
                plugin.register_jobs()
                plugin.do(function_name, data)

    def output(self):
        for plugin in self.bot_plugins:
            limiter = False
            for output in plugin.do_output():
                channel = self.slack_client.server.channels.find(output[0])
                if channel is not None and output[1] is not None:
                    if limiter:
                        time.sleep(.1)
                        limiter = False
                    message = output[1].encode('ascii', 'ignore')
                    channel.send_message("{}".format(message))
                    limiter = True

    def crons(self):
        for plugin in self.bot_plugins:
            plugin.do_jobs()

    def load_plugins(self):
        for plugin in settings.RTM_PLUGINS:
            self.bot_plugins.append(Plugin(plugin))
Пример #18
0
def main():
	# read the api key into variable
	with open('key.txt', 'r') as key_file:
		bot_token = key_file.read().replace('\n', '')

	# init bot token and officers from google sheets
	sc = SlackClient(bot_token)



	# TODO: Dynamically find the bot's id using users.list
	# could cause problem if this changes for some reason
	bot_id = "U0H7GEEJW"

	# counts up after every sleep(1)
	# so we can poll when counter reachs 60 or 1 min
	counter = 0

	# connect to the bots feed
	if sc.rtm_connect():
		while True:
			# read events from peterbot's feed
			try:
				events = sc.rtm_read()
			# in the event that it throws an error just set it
			# to an empty list and continue
			except Exception, e:
				# print to add to log
				sys.stderr.write(e)
				events = []

			for e in events:
				user_input = ""
				message = ""

				# format the input text
				if e.get("text"):
					user_input = e.get("text").lower().strip()
					
					# return a message based on the user's input
					message = "Currently Under Maintainence (Lab is Open as of 10:21am)"

				# if there is a message to send, then send it
				# will not respond if received from bot message to prevent
				# looping conversation with itself
				if message and e.get("user") != bot_id:
					chan_id = e.get("channel")
					sc.api_call("chat.postMessage", as_user="******", channel=chan_id, text=message)

			# delay
			time.sleep(1)
			counter += 1

			# run script to build up statistics
			if counter >= 60:
				counter = 0
Пример #19
0
class RtmBot(object):
    def __init__(self, token):
        self.token = token
        self.bot_plugins = []
        self.slack_client = None

    def connect(self):
        ''' Creates server instance '''
        self.slack_client = SlackClient(self.token)
        self.slack_client.rtm_connect()

    def start(self):
        self.connect()
        self.load_plugins()
        while True:
            for reply in self.slack_client.rtm_read():
                self.input(reply)
            self.crons()
            self.output()
            time.sleep(.1)

    def input(self, data):
        if "type" in data:
            function_name = "process_" + data["type"]
            dbg("got {}".format(function_name))
            for plugin in self.bot_plugins:
                print plugin, 'registering jobs'
                plugin.register_jobs()
                plugin.do(function_name, data)

    def output(self):
        for plugin in self.bot_plugins:
            limiter = False
            for output in plugin.do_output():
                channel = self.slack_client.server.channels.find(output[0])
                if channel != None and output[1] != None:
                    if limiter == True:
                        time.sleep(1)
                        limiter = False
                    message = output[1].encode('ascii','ignore')
                    channel.send_message("{}".format(message))
                    limiter = True

    def crons(self):
        for plugin in self.bot_plugins:
            plugin.do_jobs()

    def load_plugins(self):
        for plugin in glob.glob(directory+'/plugins/*'):
            sys.path.insert(0, plugin)
            sys.path.insert(0, directory+'/plugins/')
        for plugin in glob.glob(directory+'/plugins/*.py') + glob.glob(directory+'/plugins/*/*.py'):
            logging.info(plugin)
            name = plugin.split('/')[-1][:-3]
#            try:
            self.bot_plugins.append(Plugin(name))
Пример #20
0
class PeckBot(object):
    def __init__(self, token, timeout):
        print('[INFO] Initializing bot...')
        self.token = token
        self.client = SlackClient(token)
        self.timeout = timeout
        self.userid = 'U04U7K4HC'
        self.responses = [
            ('^!reddit ([a-zA-Z0-9]+)', RedditBehavior()),
            ('^!chat (.*)$', CleverBotBehavior()),
            ('^!doc$', DocBehavior()),
            ('^!quote ?(.*)$', QuoteBehavior()),
            ('^!trivia ?(.*)$', TriviaBehavior()),
            ('(.+)', NonsensSpewingBehavior()),
            ('.+', NonsensTrainingBehavior())
        ]
        print('[INFO] Init done.')

    def connect(self):
        return self.client.rtm_connect()

    def send_msg(self, msg, channel):
        self.client.rtm_send_message(channel, msg)
        self.pause()

    def pause(self):
        time.sleep(self.timeout)

    def run(self):
        print('[INFO] PeckBot is running')
        while True:
            events = self.client.rtm_read()
            for event in events:
                if 'type' in event and event['type'] == 'message':
                    if 'user' in event and event['user'] != self.userid:
                        self.respond(event)
            self.pause()

    def respond(self, event):
        for resp in self.responses:
            regex, behavior = resp
            matches = re.findall(regex, event['text'])
            try:
                channel_name = json.loads(self.client.api_call('channels.info', channel=event['channel']))['channel']['name']
            except KeyError:
                channel_name = event['channel']
            for match in matches:
                print('[INFO] Triggered {0} on #{1}'.format(behavior.name, channel_name))
                try:
                    behavior.execute(self, match, event)
                except Exception as e:
                    print('[ERROR] {0} failed: {1}'.format(behavior.name, e))

            if len(matches) > 0:
                break
Пример #21
0
class SlackHandle(threading.Thread):

    # bot user id 
    __BOT_ID = os.environ.get('BOT_ID')
    # slack bot token generated from the slack app and integration settings
    __SLACK_BOT_TOKEN = os.environ.get('SLACK_BOT_TOKEN')

    def __init__(self, command_queue = None):
        super(SlackHandle, self).__init__()
        self.__command_queue = command_queue
        self.stop_request = threading.Event()
        if self.__BOT_ID:
            self.__AT_BOT = "<@" + self.__BOT_ID + ">"
        else:
            raise Exception('Please define Bot Id in environment.')

        if self.__SLACK_BOT_TOKEN:
            self.__slack_client = SlackClient(self.__SLACK_BOT_TOKEN)
        else:
            raise Exception('Please define Slack Bot Token in environment.')
    
    def run(self):
        # try to connect to slack via rtm
        if self.__slack_client.rtm_connect():
            print('Slack bot started and connected')
            while not self.stop_request.isSet():
                # fetch changes and parse rtm json
                command, channel = self.__parse_slack_output(self.__slack_client.rtm_read())
                if command and channel:
                    commandQ = {'command' : command, 'reciever' : { 'handle' : self, 'params' : { 'channel' : channel } } }
                    # print(commandQ)
                    # Queue the commend to be processed by command listner and a command handler execute the command
                    self.__command_queue.put(commandQ)
                time.sleep(1)
        else:
            # there was an error connecting to slack
            print('Slack bot could not connect.')
            raise Exception('Slack bot could not connect.')
    
    def join(self, timeout = None):
        self.stop_request.set()
        super(SlackHandle, self).join(timeout)
    
    # Parse the slack rtm output and fetch the text and channel info from the json
    def __parse_slack_output(self, slack_rtm_output):
        if slack_rtm_output and len(slack_rtm_output) > 0:
            for output in slack_rtm_output:
                if output and 'text' in output and self.__AT_BOT in output['text']:
                    return output['text'].split(self.__AT_BOT)[1].strip().lower(), output['channel']
        return None, None
    
    # method is called by reply processor to send message back to user 
    def handle(self, message =  None, params = {}):
        if message and len(message) > 0 and 'channel' in params:
            self.__slack_client.api_call("chat.postMessage", channel= params['channel'], text=message, as_user=True)
Пример #22
0
class SlackBot(object):
    def __init__(self):
        self.config = config
        self.api    = Slacker(self.config.token)
        self.client = SlackClient(self.config.token)
        self.plugins = []
        self.user_map    = {}
        self.channel_map = {}
        self.load_plugins()

    def run(self):
        self.update_maps()
        self.client.rtm_connect()
        while True:
            for event in self.client.rtm_read():
                self.combine_maps(event)
                for plugin in self.plugins:
                    plugin.process_event(event)
            time.sleep(1)

    # in subclass, post with name/icon
    def post_message(self, channel, message):
        self.api.chat.post_message(channel, message)

    def update_maps(self):
        for user in self.api.users.list().body['members']:
            self.user_map[user['id']] = user
        for channel in self.api.channels.list().body['channels']:
            self.channel_map[channel['id']] = channel
        for group in self.api.groups.list().body['groups']:
            self.channel_map[group['id']] = group

    def combine_maps(self, event):
        user_id = event.get('user')
        if user_id is not None:
            user = self.user_map.get(user_id)
            if user is not None:
                event['user_dict'] = user
        channel_id = event.get('channel')
        if channel_id is not None:
            channel = self.channel_map.get(channel_id)
            if channel is not None:
                event['channel_dict'] = channel

    # for override
    def plugin_classes(self):
        return [
            EventTypePlugin,
            PrintPlugin,
            EchoPlugin,
        ]

    def load_plugins(self):
        for klass in self.plugin_classes():
            self.plugins.append(klass(self))
Пример #23
0
class Slack(Channel):
    def __init__(self, token):
        self.client = SlackClient(token)
        self.rooms = {}
        self.reconnect()
        self.ignore = self.client.server.users.find(self.client.server.username).id

    def reconnect(self):
        return self.client.rtm_connect()

    def join(self, roomname):
        channel = self.client.server.channels.find(roomname)
        if not channel:
            return
        room = self.rooms[channel.id] = SlackRoom(self, channel)
        return room

    def alive(self):
        if not self.client.server.websocket.connected:
            self.reconnect()
        return self.client.server.websocket.connected

    def fetch_messages(self):
        while True:
            msgs = self.client.rtm_read()
            if not len(msgs):
                break
            for msg in msgs:
                if not msg.get('type'):
                    continue
                if msg['type'] == 'hello':
                    continue
                if msg['type'] == 'presence_change':
                    continue
                if msg['type'] == 'reconnect_url':
                    continue
                if msg['type'] == 'user_typing':
                    continue
                if msg['type'] == 'message':
                    room = self.rooms.get(msg['channel'])
                    if not room:
                        continue
                    #print 'DEBUG_SLACK_RECV_MSG', msg
                    user = msg.get('user') or msg.get('username') or \
                           msg.get('comment', {}).get('user') or \
                           msg.get('bot_id') or '_'
                    if user == self.ignore:
                        continue
                    room.append_message(user, msg.get('text', ''), msg)
                else:
                    print 'UNKNOWN_SLACK_RECV_MSG', msg

    def close(self):
        pass
Пример #24
0
def main():
    token = "token"
    sc = SlackClient(token)
    if sc.rtm_connect():
        while True:
            print(sc.rtm_read())
            time.sleep(1)
            channel = sc.server.channels.find('channel')
            channel.send_message('test')
    else:
        print("Connection failed, invalid token?")
Пример #25
0
class Bot(object):

    def __init__(self, token, bot_user):
        self.last_ping = 0
        self.token = token
        self.slack_client = None
        self.pub = pub
        self.slack = Slacker(token)
        self.bot_user = bot_user

    def connect(self):
        self.slack_client = SlackClient(self.token)
        return self.slack_client.rtm_connect()

    def run(self):
        # connect the slack_client and create a websocket connection
        isConnected = self.connect()
        if isConnected:
            while True:
                # while True we keep reading from slack every 0.3 secs for messages
                # for each payload in the array of payloads sent we dispatch a message
                # which is the type of the payload and the payload itself
                # any listener gets updated and can perform any action based on the payload
                for payload in self.slack_client.rtm_read():
                    print(payload)
                    if 'subtype' in payload:
                        print('we got a msg from a bot please lets see if we wanna handle bot-bot convo')
                    else:
                        self.dispatchMessage(payload['type'],payload)

                time.sleep(1)
        else:
            # should throw an exception here
            print("Connection closed")

    # register a callback to listen to changes
    def registerListener(self, callback, obj_type):
        self.pub.subscribe(callback, obj_type)

    # dispatches changes
    def dispatchMessage(self, obj_type, payload):
        self.pub.sendMessage(obj_type, payload=payload)

    def sendChannelMsg(self, msg_dict):
        channel = msg_dict['channel']
        msg = msg_dict['text']
        attachments = msg_dict['attachments']
        self.slack.chat.post_message(channel=channel, text=msg, username=self.bot_user, 
                                        attachments=attachments, icon_url="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSzEdaAJwSCfB5K5F6FrASKwAk7ukLdWkWddVuy0ew3_iDqcU_0")


    def listenForMsg(self, payload):
        print("just got a message")
        self.sendChannelMsg(payload)
Пример #26
0
def main():
    sc = SlackClient(token)

    if sc.rtm_connect():
        channel = sc.server.channels.find("squares")
        while True:
            response = parse_input(sc.rtm_read())
            for msg in response or []:
                channel.send_message(msg)
            time.sleep(1)
    else:
        print("Connection Failed, invalid token?")
Пример #27
0
class Jarvis(object):
    def __init__(self, directory, token):
        self.last_ping = 0

        self.slack_client = SlackClient(token)
        self.slack_client.rtm_connect()

        for plugin in glob.glob(directory + '/plugins/*'):
            sys.path.insert(0, plugin)
            sys.path.insert(0, directory + '/plugins/')

        self.plugins = []
        for plugin in glob.glob(directory + '/plugins/*.py') + \
                glob.glob(directory + '/plugins/*/*.py'):
            name = plugin.split('/')[-1][:-3]
            self.plugins.append(Plugin(name))

        logger.debug('Done initializing.')

    def input(self, data):
        # TODO: fix naive 'text fragment' in data['text']
        if 'type' in data and 'text' in data:
            data['text'] = data['text'].lower()
            if not data['text'].startswith('jarvis'):
                return

            function_name = 'process_' + data['type']
            for plugin in self.plugins:
                plugin.input(function_name, data)

    def keepalive(self):
        now = int(time.time())
        if now > self.last_ping + 3:
            self.slack_client.server.ping()
            self.last_ping = now

    def output(self):
        for plugin in self.plugins:
            for output in plugin.output():
                channel = self.slack_client.server.channels.find(output[0])
                if channel and output[1]:
                    message = output[1].encode('ascii', 'ignore')
                    channel.send_message('{}'.format(message))

    def run(self):
        while True:
            for reply in self.slack_client.rtm_read():
                self.input(reply)

            self.output()

            self.keepalive()
            time.sleep(.1)
Пример #28
0
class Bot(object):
    def __init__(self, config_file):
        config = json.load(open(config_file))
        self.chatbot = ChatBot(
            'Robot Overload',
            io_adapter='chatterbot.adapters.io.NoOutputAdapter',
            database=config['database'])
        self.chatbot.train('chatterbot.corpus.english')
        self.chatbot.train('chatterbot.corpus.english.greetings')
        self.chatbot.train('chatterbot.corpus.english.conversations')

        self.token = config['token']
        self.bot_id = config['bot_id']
        self.slack_client = None

    def connect(self):
        self.slack_client = SlackClient(self.token)
        self.slack_client.rtm_connect()
        self.last_ping = 0

    def start(self):
        self.connect()
        while True:
            for reply in self.slack_client.rtm_read():
                self.input(reply)
            self.autoping()
            time.sleep(.1)

    def input(self, data):
        text = data.get('text', '')
        if data.get('type') == 'message' and self.bot_id in text:
            channel = data['channel']
            text = text.replace(self.bot_id, '')
            response =  self.chatbot.get_response(text)
            self.output([channel, response])

    def output(self, output):
        limiter = False
        channel = self.slack_client.server.channels.find(output[0])
        if channel != None and output[1] != None:
            if limiter == True:
                time.sleep(.1)
                limiter = False
            message = output[1].encode('ascii','ignore')
            channel.send_message("{}".format(message))
            limiter = True

    def autoping(self):
        now = int(time.time())
        if now > self.last_ping + 3:
            self.slack_client.server.ping()
            self.last_ping = now
Пример #29
0
 def handle(self, *args, **options):
     team = Team.objects.first()
     client = SlackClient(team.bot_access_token)
     if client.rtm_connect():
         while True:
             events = client.rtm_read()
             print("%s----%s" % (team, events))
             for event in events:
                 if 'type' in event and event['type'] == 'message' and event['text'] == 'hi':
                     client.rtm_send_message(event['channel'], "hello world")
                 elif 'type' in event and event['type'] == 'message' and event['text'] == 'who':
                     client.rtm_send_message(event['channel'], "I'am Groot")
             time.sleep(1)
Пример #30
0
class Gendo(_PackageBoundObject):
    def __init__(self, import_name, slack_token, channel):
        _PackageBoundObject.__init__(self, import_name)
        self.listeners = []
        self.client = SlackClient(slack_token)
        self.channel = channel
        self.sleep = 0.5

    def listen_for(self, rule, **options):
        def decorator(f):
            self.add_listener(rule, f, **options)
            return f
        return decorator

    def run(self):
        if self.client.rtm_connect():
            while True:
                time.sleep(self.sleep)
                try:
                    data = self.client.rtm_read()
                    if data and data[0].get('type') == 'message':
                        user = data[0].get('user')
                        message = data[0].get('text')
                        self.respond(user, message)
                except (KeyboardInterrupt, SystemExit):
                    print "Shutting down..."
                    break

    def respond(self, user, message):
        if not message:
            return
        for phrase, view_func, options in self.listeners:
            if phrase in message.lower():
                response = view_func(user, message, **options)
                if response:
                    self.speak(response)

    def add_listener(self, rule, view_func=None, **options):
        self.listeners.append((rule, view_func, options))

    def speak(self, message):
        self.client.api_call("chat.postMessage", as_user="******",
                             channel=self.channel, text=message)

    def get_user_info(self, user_id):
        user = self.client.api_call('users.info', user=user_id)
        return json.loads(user)

    def get_user_name(self, user_id):
        user = self.get_user_info(user_id)
        return user.get('user', {}).get('name')
Пример #31
0
        cursor.executemany(
            'INSERT INTO messages VALUES(?, ?, ?, ?)',
            [(event['text'], event['user'], event['channel'], event['ts'])])
        conn.commit()

    logger.debug("--------------------------")


# Loop
if sc.rtm_connect(auto_reconnect=True):
    update_users()
    update_channels()
    logger.info('Archive bot online. Messages will now be recorded...')
    while sc.server.connected is True:
        try:
            for event in sc.rtm_read():
                if event['type'] == 'message':
                    handle_message(event)
                    if 'subtype' in event and event['subtype'] in [
                            'group_leave'
                    ]:
                        update_channels()
                elif event['type'] in [
                        'group_joined', 'member_joined_channel',
                        'channel_created', 'group_left'
                ]:
                    update_channels()
        except WebSocketConnectionClosedException:
            sc.rtm_connect()
        except:
            logger.error(traceback.format_exc())
Пример #32
0
if __name__ == '__main__':
    card_trigger = "!card"
    card_offset = len(card_trigger) + 1
    rule_trigger = "!rule"
    rule_offset = len(rule_trigger) + 1
    refresh_trigger = "!refresh"
    slack_token = os.environ.get('SLACK_BOT_TOKEN')
    sc = SlackClient(slack_token)
    while True:
        try:
            if sc.rtm_connect():
                populate_cards()
                print 'Successfully connected'
                while True:
                    msgs = sc.rtm_read()
                    for msg in msgs:
                        response = None
                        txt = msg.get('text')
                        if msg.get('text'
                                   ) is not None and card_trigger in msg.get(
                                       'text'):
                            response = handle_card(txt)
                        if txt is not None and rule_trigger in msg.get('text'):
                            response = handle_rule(txt)
                        if txt is not None and refresh_trigger in msg.get(
                                'text'):
                            populate_cards()
                            sc.api_call('chat.postMessage',
                                        channel=msg.get('channel'),
                                        text="Refreshed DB")
Пример #33
0
import time
from slackclient import SlackClient

token = "your token here"  # found at https://api.slack.com/web#authentication
sc = SlackClient(token)
if sc.rtm_connect():
    while True:
        result = sc.rtm_read()
        #print result
        for item in result:
            print item
            if (item["type"] == "message") and (item["user"]
                                                == "U1AT0FNF6"):  #rhi
                newMessage = "*" + item["text"] + "*"
                print sc.api_call("chat.postMessage",
                                  channel=item["channel"],
                                  text=newMessage,
                                  as_user="******")
        time.sleep(1)
else:
    print "Connection Failed, invalid token?"
Пример #34
0
                    " 秒",
                    ts=timeStamp,
                    as_user='******')
        time.sleep(2.0)

    sc.api_call("chat.postMessage",
                channel=_targetChannel,
                text="Time up!!",
                ts=timeStamp,
                as_user='******')


if sc.rtm_connect():
    while True:
        time.sleep(1)
        stream = sc.rtm_read()
        print(stream)
        if len(stream) > 0 and 'type' in stream[0] and 'text' in stream[0]:
            inputSplit = stream[0]['text'].split()
            print(inputSplit)
            if stream[0]['type'] == 'message' and inputSplit[
                    0] == '<@' + BOTID + '>':
                targetChannel = stream[0]['channel']
                if inputSplit[1].isdigit():
                    finishTime = int(inputSplit[1])
                    print('timer start')
                    timer(targetChannel, finishTime)
                else:
                    post = sc.api_call('chat.postMessage',
                                       channel=targetChannel,
                                       text='書き方 \n`@timer 秒数`',
Пример #35
0
    """
    item_id = utils.find_id(text, keywords.TASK_ITEM)
    if item_id is None:
        return error_reply(channel)
    item = oboy.list_by_id(item_id)
    answer = prompt(channel, response.delete_confirm(item))
    if answer in keywords.ANSWER_POSITIVE:
        oboy.delete_item(item_id)
        bot.rtm_send_message(channel, response.delete_success())
    else:
        bot.rtm_send_message(channel, response.delete_regret())


def error_reply(channel):
    """Sends an error message to the user asking it to try
    again.

    :param channel: The channel ID of which to respond to.
    :type channel: str
    """
    bot.rtm_send_message(channel, response.error_retry())


if __name__ == "__main__":
    if bot.rtm_connect():
        while True:
            event = bot.rtm_read()
            if utils.is_user_message(event, BOT_ID):
                process_entry(event)
            time.sleep(1)
Пример #36
0
                          text=message,
                          username=hashed_user.hexdigest()[:10])


def parse_slack_output(slack_rtm_output):
    """
        The Slack Real Time Messaging API is an events firehose.
        this parsing function returns None unless a message is
        directed at the Bot, based on its ID.
    """
    output_list = slack_rtm_output
    if output_list and len(output_list) > 0:
        for output in output_list:
            if output and 'channel' in output and 'text' in output:
                print output['channel']
                if output['channel'][0] == "D":
                    return output['text'], output['user']
    return None, None


if __name__ == "__main__":
    READ_WEBSOCKET_DELAY = 1  # 1 second delay between reading from firehose
    if slack_client.rtm_connect():
        while True:
            message, user = parse_slack_output(slack_client.rtm_read())
            if message and user:
                anon_message(message, user)
            time.sleep(READ_WEBSOCKET_DELAY)
    else:
        print("Connection failed. Invalid Slack token or bot ID?")
Пример #37
0
def create_default_message(attachments):
    attachments.append({
        "title": "Too dumb",
        "text":
        "I'm not smart enough to understand that. Try something with lunch",
        "fallback": "That's not a command"
    })


if __name__ == "__main__":
    bot_id = get_bot_id()
    if bot_id is not None:
        if slack_client.rtm_connect():
            print("lunchbot connected and running!")
            while True:
                try:
                    command, channel, user = parse_slack_output(
                        slack_client.rtm_read(), bot_id)
                # F**k it, gotta catch 'em all
                except Exception as e:
                    print(
                        "Caught an exception while trying to read:\nReconnecting...",
                        e)
                    slack_client.rtm_connect()
                if command and channel and user:
                    handle_command(command, channel, user)
                time.sleep(1)
        else:
            print("Connection failed. Invalid Slack token or bot ID?")
Пример #38
0
class SlackBot:
    """
    Slack bot base class. Includes lots of useful functionality that bots often require, such as messaging, connection management, and interfacing with APIs.

    This class is compatible with the [January 2017 release of the Slack API](https://api.slack.com/changelog).

    This class is intended to be subclassed, with the `on_step` and `on_message` methods overridden to do more useful things.
    """
    def __init__(self, token, logger=None):
        assert isinstance(token,
                          str), "`token` must be a valid Slack API token"
        assert logger is None or not isinstance(
            logger,
            logging.Logger), "`logger` must be `None` or a logging function"

        self.client = SlackClient(token)
        if logger is None:
            self.logger = logging.getLogger(self.__class__.__name__)
        else:
            self.logger = logger

        # cached versions of methods
        self.get_user_info_by_id_cached = lru_cache(maxsize=256)(
            self.get_user_info_by_id)

        # incoming message fields
        self.unprocessed_incoming_messages = deque(
        )  # store unprocessed messages to allow message peeking

        # outgoing message fields
        self.max_message_id = 1  # every message sent over RTM needs a unique positive integer ID - this should technically be handled by the Slack library, but that's broken as of now
        self.last_say_time = 0  # store last message send timestamp to rate limit sending
        self.bot_user_id = None  # ID of this bot user

    def on_step(self):
        self.logger.info("step handler called")

    def on_message(self, message_dict):
        self.logger.info(
            "message handler called with message {}".format(message_dict))

    def start_loop(self):
        while True:
            try:
                self.start()  # start the main loop
            except KeyboardInterrupt:
                break
            except Exception:
                self.logger.error("main loop threw exception:\n{}".format(
                    traceback.format_exc()))
                self.logger.info("restarting in 5 seconds...")
                time.sleep(5)
        self.logger.info("shutting down...")

    def retrieve_unprocessed_incoming_messages(self):
        result = list(
            self.unprocessed_incoming_messages) + self.client.rtm_read()
        self.unprocessed_incoming_messages.clear()
        return result

    def peek_unprocessed_incoming_messages(self):
        self.unprocessed_incoming_messages.extend(self.client.rtm_read())
        return list(self.unprocessed_incoming_messages)

    def peek_new_messages(self):
        new_messages = self.client.rtm_read()
        self.unprocessed_incoming_messages.extend(new_messages)
        return list(new_messages)

    def start(self):
        # connect to the Slack Realtime Messaging API
        self.logger.info("connecting to Slack realtime messaging API...")
        if not self.client.rtm_connect():
            raise ConnectionError(
                "Could not connect to Slack realtime messaging API (possibly a bad token or network issue)"
            )
        self.logger.info("connected to Slack realtime messaging API")

        # obtain the bot credentials
        authentication = self.client.api_call("auth.test")
        assert authentication["ok"], "Could not authenticate with Slack API"
        self.bot_user_id = authentication["user_id"]

        last_ping = time.monotonic()
        while True:
            # call all the step callbacks
            try:
                self.on_step()
            except Exception:
                self.logger.error(
                    "step processing threw exception:\n{}".format(
                        traceback.format_exc()))

            # call all the message callbacks for each newly received message
            for message_dict in self.retrieve_unprocessed_incoming_messages():
                try:
                    self.on_message(message_dict)
                except KeyboardInterrupt:
                    raise
                except Exception:
                    self.logger.error(
                        "message processing threw exception:\n{}\n\nmessage contents:\n{}"
                        .format(traceback.format_exc(), message_dict))

            # ping the server periodically to make sure our connection is kept alive
            if time.monotonic() - last_ping > 5:
                self.client.server.ping()
                last_ping = time.monotonic()

            # delay to avoid checking the socket too often
            time.sleep(0.01)

    def say(self, sendable_text, *, channel_id, thread_id=None):
        """Say `sendable_text` in the channel with ID `channel_id`, returning the message ID (unique within each `SlackBot` instance)."""
        assert self.get_channel_name_by_id(
            channel_id
        ) is not None, "`channel_id` must be a valid channel ID rather than \"{}\"".format(
            channel_id)
        assert isinstance(
            thread_id, str
        ) or thread_id is None, "`thread_id` must be a valid Slack timestamp or None, rather than \"{}\"".format(
            thread_id)
        assert isinstance(
            sendable_text,
            str), "`text` must be a string rather than \"{}\"".format(
                sendable_text)

        # rate limit sending to 1 per second, since that's the Slack API limit
        current_time = time.monotonic()
        if current_time - self.last_say_time < 1:
            time.sleep(max(0, 1 - (current_time - self.last_say_time)))
            self.last_say_time += 1
        else:
            self.last_say_time = current_time

        self.logger.info("sending message to channel {}: {}".format(
            self.get_channel_name_by_id(channel_id), sendable_text))

        # the correct method to use here is `rtm_send_message`, but it's technically broken since it doesn't send the message ID so we're going to do this properly ourselves
        # the message ID allows us to correlate messages with message responses, letting us ensure that messages are actually delivered properly
        # see the "Sending messages" heading at https://api.slack.com/rtm for more details
        message_id = self.max_message_id
        self.max_message_id += 1
        if thread_id is not None:  # message in a thread
            self.client.server.send_to_websocket({
                "id": message_id,
                "type": "message",
                "channel": channel_id,
                "text": sendable_text,
                "thread_ts": thread_id,
            })
        else:  # top-level message
            self.client.server.send_to_websocket({
                "id": message_id,
                "type": "message",
                "channel": channel_id,
                "text": sendable_text,
            })
        return message_id

    def say_complete(self,
                     sendable_text,
                     *,
                     channel_id,
                     thread_id=None,
                     timeout=5):
        """Say `sendable_text` in the channel with ID `channel_id`, waiting for the message to finish sending (raising a `TimeoutError` if this takes more than `timeout` seconds), returning the message timestamp."""
        assert float(
            timeout
        ) > 0, "`timeout` must be a positive number rather than \"{}\"".format(
            timeout)
        message_id = self.say(sendable_text,
                              channel_id=channel_id,
                              thread_id=thread_id)
        message_timestamp = None
        start_time = time.monotonic()
        while message_timestamp is None and time.monotonic(
        ) - start_time < timeout:
            # peek at new messages to see if the response is written
            for message_dict in self.peek_new_messages():
                if "ok" in message_dict and message_dict.get(
                        "reply_to"
                ) == message_id:  # received reply for the sent message
                    if not message_dict["ok"]:
                        raise ValueError("Message sending error: {}".format(
                            message_dict.get("error", {}).get("msg")))
                    assert isinstance(
                        message_dict.get("ts"),
                        str), "Invalid message timestamp: {}".format(
                            message_dict.get("ts"))
                    message_timestamp = message_dict["ts"]
                    break
            else:
                time.sleep(0.01)
        if message_timestamp is None:
            raise TimeoutError("Message sending timed out")
        return message_timestamp

    def react(self, channel_id, timestamp, emoticon):
        """React with `emoticon` to the message with timestamp `timestamp` in channel with ID `channel_id`."""
        assert self.get_channel_name_by_id(
            channel_id
        ) is not None, "`channel_id` must be a valid channel ID rather than \"{}\"".format(
            channel_id)
        assert isinstance(
            timestamp,
            str), "`timestamp` must be a string rather than \"{}\"".format(
                timestamp)
        assert isinstance(
            emoticon,
            str), "`emoticon` must be a string rather than \"{}\"".format(
                emoticon)
        emoticon = emoticon.strip(":")
        self.logger.info(
            "adding reaction :{}: to message with timestamp {} in channel {}".
            format(emoticon, timestamp,
                   self.get_channel_name_by_id(channel_id)))
        response = self.client.api_call("reactions.add",
                                        name=emoticon,
                                        channel=channel_id,
                                        timestamp=timestamp)
        assert response.get("ok"), "Reaction addition failed: error {}".format(
            response.get("error"))

    def unreact(self, channel_id, timestamp, emoticon):
        """React with `emoticon` to the message with timestamp `timestamp` in channel with ID `channel_id`."""
        assert self.get_channel_name_by_id(
            channel_id
        ) is not None, "`channel_id` must be a valid channel ID rather than \"{}\"".format(
            channel_id)
        assert isinstance(
            timestamp,
            str), "`timestamp` must be a string rather than \"{}\"".format(
                sendable_text)
        assert isinstance(
            emoticon,
            str), "`emoticon` must be a string rather than \"{}\"".format(
                sendable_text)
        emoticon = emoticon.strip(":")
        self.logger.info(
            "removing reaction :{}: to message with timestamp {} in channel {}"
            .format(emoticon, timestamp,
                    self.get_channel_name_by_id(channel_id)))
        response = self.client.api_call("reactions.remove",
                                        name=emoticon,
                                        channel=channel_id,
                                        timestamp=timestamp)
        assert response.get("ok"), "Reaction removal failed: error {}".format(
            response.get("error"))

    def get_channel_name_by_id(self, channel_id):
        """Returns the name of the channel with ID `channel_id`, or `None` if there are no channels with that ID. Channels include public channels, direct messages with other users, and private groups."""
        assert isinstance(
            channel_id, str
        ), "`channel_id` must be a valid channel ID rather than \"{}\"".format(
            channel_id)
        for entry in self.client.server.channels:
            if entry.id == channel_id: return entry.name
        return None

    def get_channel_id_by_name(self, channel_name):
        """Returns the ID of the channel with name `channel_name`, or `None` if there are no channels with that name. Channels include public channels, direct messages with other users, and private groups."""
        assert isinstance(
            channel_name, str
        ), "`channel_name` must be a valid channel name rather than \"{}\"".format(
            channel_name)

        channel_name = channel_name.strip().lstrip("#")

        # check for channel reference (these are formatted like `<#CHANNEL_ID>` or `<#CHANNEL_ID|CHANNEL_NAME>`)
        match = re.match(r"<#(\w+)(?:\|[^>]+)?>$", channel_name)
        if match: return match.group(1)

        # search by channel name
        for entry in self.client.server.channels:
            if entry.name == channel_name: return entry.id

        return None

    def get_user_name_by_id(self, user_id):
        """Returns the username of the user with ID `user_id`, or `None` if there are no users with that ID."""
        assert isinstance(
            user_id, str
        ), "`user_id` must be a valid user ID rather than \"{}\"".format(
            user_id)
        for key, entry in self.client.server.users.items():
            if entry.id == user_id: return entry.name
        return None

    def get_user_id_by_name(self, user_name):
        """Returns the ID of the user with username `user_name`, or `None` if there are no users with that username."""
        assert isinstance(
            user_name, str
        ), "`user_name` must be a valid username rather than \"{}\"".format(
            user_name)

        user_name = user_name.strip().lstrip("@")

        # check for user reference (these are formatted like `<@USER_ID>` or `<@USER_ID|USER_NAME>`)
        match = re.match(r"^<@(\w+)(?:\|[^>]+)?>$", user_name)
        if match: return match.group(1)

        # search by user name
        for key, entry in self.client.server.users.items():
            if entry.name == user_name: return entry.id

        # search by user real name
        for key, entry in self.client.server.users.items():
            if entry.real_name == user_name: return entry.id

        return None

    def get_direct_message_channel_id_by_user_id(self, user_id):
        """Returns the channel ID of the direct message with the user with ID `user_id`, or `None` if the ID is invalid."""
        listing = self.client.api_call("im.list")["ims"]
        for entry in listing:
            if entry["user"] == user_id: return entry["id"]
        return None

    def get_user_info_by_id(self, user_id):
        """Returns a [metadata dictionary](https://api.slack.com/types/user) about the user with ID `user_id`."""
        assert self.get_user_name_by_id(
            user_id
        ) is not None, "`user_id` must exist and be a valid user ID rather than \"{}\"".format(
            user_id)
        self.logger.info("retrieving user info for user {}".format(
            self.get_user_name_by_id(user_id)))
        response = self.client.api_call("users.info", user=user_id)
        assert response.get("ok"), "User info request failed: error {}".format(
            response.get("error"))
        assert isinstance(response.get("user"), dict) and "id" in response[
            "user"], "User info response malformed: {}".format(
                response.get("user"))
        return response["user"]

    def get_user_is_bot(self, user_id):
        """Returns `True` if the user with ID `user_id` is a bot user, `False` otherwise."""
        if user_id == "USLACKBOT":
            return True  # for some reason, Slack doesn't consider Slackbot a real bot
        user_info = self.get_user_info_by_id_cached(user_id)
        return user_info.get("is_bot", False)

    def server_text_to_sendable_text(self, server_text):
        """Returns `server_text`, a string in Slack server message format, converted into a string in Slack sendable message format."""
        assert isinstance(
            server_text,
            str), "`server_text` must be a string rather than \"{}\"".format(
                server_text)
        text_without_special_sequences = re.sub(r"<[^<>]*>", "", server_text)
        assert "<" not in text_without_special_sequences and ">" not in text_without_special_sequences, "Invalid special sequence in server text \"{}\", perhaps some text needs to be escaped"

        # process link references
        def process_special_sequence(match):
            original, body = match.group(0), match.group(1).split("|")[0]
            if body.startswith("#"):
                return original  # channel reference, should send unchanged
            if body.startswith("@"):
                return original  # user reference, should send unchanged
            if body.startswith("!"):
                return original  # special command, should send unchanged
            return body  # link, should remove angle brackets and label in order to allow it to linkify

        return re.sub(r"<(.*?)>", process_special_sequence, server_text)

    def text_to_sendable_text(self, text):
        """Returns `text`, a plain text string, converted into a string in Slack sendable message format."""
        assert isinstance(
            text,
            str), "`text` must be a string rather than \"{}\"".format(text)
        return text.replace("&", "&amp;").replace("<",
                                                  "&lt;").replace(">", "&gt;")

    def sendable_text_to_text(self, sendable_text):
        """Returns `sendable_text`, a string in Slack sendable message format, converted into a plain text string. The transformation can lose some information for escape sequences, such as link labels."""
        assert isinstance(
            sendable_text,
            str), "`sendable_text` must be a string rather than \"{}\"".format(
                sendable_text)
        text_without_special_sequences = re.sub(r"<[^<>]*>", "", sendable_text)
        assert "<" not in text_without_special_sequences and ">" not in text_without_special_sequences, "Invalid special sequence in sendable text \"{}\", perhaps some text needs to be escaped"

        # process link references
        def process_special_sequence(match):
            original, body = match.group(0), match.group(1).split("|")[0]
            if body.startswith("#"):  # channel reference
                channel_name = self.get_channel_name_by_id(body[1:])
                if channel_name is None: return original
                return "#" + channel_name
            if body.startswith("@"):  # user reference
                user_name = self.get_user_name_by_id(body[1:])
                if user_name is None: return original
                return "@" + user_name
            if body.startswith("!"):  # special command
                if body == "!channel": return "@channel"
                if body == "!group": return "@group"
                if body == "!everyone": return "@everyone"
            return original

        raw_text = re.sub(r"<(.*?)>", process_special_sequence, sendable_text)

        return raw_text.replace("&lt;",
                                "<").replace("&gt;",
                                             ">").replace("&amp;", "&")

    def administrator_console(self, namespace):
        """Start an interactive administrator Python console with namespace `namespace`."""
        import threading
        import readline  # this makes arrow keys work for input()
        import code

        def start_console():
            code.interact("##########################################\n" +
                          "#   Botty Administrator Python Console   #\n" +
                          "##########################################\n",
                          local=namespace)

        console_thread = threading.Thread(
            target=start_console, daemon=True
        )  # thread dies when main thread (the only non-daemon thread) exits
        console_thread.start()
Пример #39
0
class SlackBackend(ErrBot):

    room_types = "public_channel,private_channel"

    @staticmethod
    def _unpickle_identifier(identifier_str):
        return SlackBackend.__build_identifier(identifier_str)

    @staticmethod
    def _pickle_identifier(identifier):
        return SlackBackend._unpickle_identifier, (str(identifier),)

    def _register_identifiers_pickling(self):
        """
        Register identifiers pickling.

        As Slack needs live objects in its identifiers, we need to override their pickling behavior.
        But for the unpickling to work we need to use bot.build_identifier, hence the bot parameter here.
        But then we also need bot for the unpickling so we save it here at module level.
        """
        SlackBackend.__build_identifier = self.build_identifier
        for cls in (SlackPerson, SlackRoomOccupant, SlackRoom):
            copyreg.pickle(
                cls, SlackBackend._pickle_identifier, SlackBackend._unpickle_identifier
            )

    def __init__(self, config):
        super().__init__(config)
        identity = config.BOT_IDENTITY
        self.token = identity.get("token", None)
        self.proxies = identity.get("proxies", None)
        if not self.token:
            log.fatal(
                'You need to set your token (found under "Bot Integration" on Slack) in '
                "the BOT_IDENTITY setting in your configuration. Without this token I "
                "cannot connect to Slack."
            )
            sys.exit(1)
        self.sc = None  # Will be initialized in serve_once
        compact = config.COMPACT_OUTPUT if hasattr(config, "COMPACT_OUTPUT") else False
        self.md = slack_markdown_converter(compact)
        self._register_identifiers_pickling()

    def set_message_size_limit(self, limit=4096, hard_limit=40000):
        """
        Slack supports upto 40000 characters per message, Errbot maintains 4096 by default.
        """
        super().set_message_size_limit(limit, hard_limit)

    def api_call(self, method, data=None, raise_errors=True):
        """
        Make an API call to the Slack API and return response data.

        This is a thin wrapper around `SlackClient.server.api_call`.

        :param method:
            The API method to invoke (see https://api.slack.com/methods/).
        :param raise_errors:
            Whether to raise :class:`~SlackAPIResponseError` if the API
            returns an error
        :param data:
            A dictionary with data to pass along in the API request.
        :returns:
            A dictionary containing the (JSON-decoded) API response
        :raises:
            :class:`~SlackAPIResponseError` if raise_errors is True and the
            API responds with `{"ok": false}`
        """
        if data is None:
            data = {}
        response = self.sc.api_call(method, **data)
        if not isinstance(response, collections.Mapping):
            # Compatibility with SlackClient < 1.0.0
            response = json.loads(response.decode("utf-8"))

        if raise_errors and not response["ok"]:
            raise SlackAPIResponseError(
                f"Slack API call to {method} failed: {response['error']}",
                error=response["error"],
            )
        return response

    def update_alternate_prefixes(self):
        """Converts BOT_ALT_PREFIXES to use the slack ID instead of name

        Slack only acknowledges direct callouts `@username` in chat if referred
        by using the ID of that user.
        """
        # convert BOT_ALT_PREFIXES to a list
        try:
            bot_prefixes = self.bot_config.BOT_ALT_PREFIXES.split(",")
        except AttributeError:
            bot_prefixes = list(self.bot_config.BOT_ALT_PREFIXES)

        converted_prefixes = []
        for prefix in bot_prefixes:
            try:
                converted_prefixes.append(f"<@{self.username_to_userid(prefix)}>")
            except Exception as e:
                log.error(
                    'Failed to look up Slack userid for alternate prefix "%s": %s',
                    prefix,
                    e,
                )

        self.bot_alt_prefixes = tuple(
            x.lower() for x in self.bot_config.BOT_ALT_PREFIXES
        )
        log.debug("Converted bot_alt_prefixes: %s", self.bot_config.BOT_ALT_PREFIXES)

    def serve_once(self):
        self.sc = SlackClient(self.token, proxies=self.proxies)

        log.info("Verifying authentication token")
        self.auth = self.api_call("auth.test", raise_errors=False)
        if not self.auth["ok"]:
            raise SlackAPIResponseError(
                error=f"Couldn't authenticate with Slack. Server said: {self.auth['error']}"
            )
        log.debug("Token accepted")
        self.bot_identifier = SlackPerson(self.sc, self.auth["user_id"])

        log.info("Connecting to Slack real-time-messaging API")
        if self.sc.rtm_connect():
            log.info("Connected")
            # Block on reads instead of using the busy loop suggested in slackclient docs
            # https://github.com/slackapi/python-slackclient/issues/46#issuecomment-165674808
            self.sc.server.websocket.sock.setblocking(True)
            self.reset_reconnection_count()

            # Inject bot identity to alternative prefixes
            self.update_alternate_prefixes()

            try:
                while True:
                    for message in self.sc.rtm_read():
                        self._dispatch_slack_message(message)
            except KeyboardInterrupt:
                log.info("Interrupt received, shutting down..")
                return True
            except Exception:
                log.exception("Error reading from RTM stream:")
            finally:
                log.debug("Triggering disconnect callback")
                self.disconnect_callback()
        else:
            raise Exception("Connection failed, invalid token ?")

    def _dispatch_slack_message(self, message):
        """
        Process an incoming message from slack.

        """
        if "type" not in message:
            log.debug("Ignoring non-event message: %s.", message)
            return

        event_type = message["type"]

        event_handlers = {
            "hello": self._hello_event_handler,
            "presence_change": self._presence_change_event_handler,
            "message": self._message_event_handler,
            "member_joined_channel": self._member_joined_channel_event_handler,
            "reaction_added": self._reaction_event_handler,
            "reaction_removed": self._reaction_event_handler,
        }

        event_handler = event_handlers.get(event_type)

        if event_handler is None:
            log.debug(
                "No event handler available for %s, ignoring this event", event_type
            )
            return
        try:
            log.debug("Processing slack event: %s", message)
            event_handler(message)
        except Exception:
            log.exception(f"{event_type} event handler raised an exception")

    def _hello_event_handler(self, event):
        """Event handler for the 'hello' event"""
        self.connect_callback()
        self.callback_presence(Presence(identifier=self.bot_identifier, status=ONLINE))

    def _presence_change_event_handler(self, event):
        """Event handler for the 'presence_change' event"""

        idd = SlackPerson(self.sc, event["user"])
        presence = event["presence"]
        # According to https://api.slack.com/docs/presence, presence can
        # only be one of 'active' and 'away'
        if presence == "active":
            status = ONLINE
        elif presence == "away":
            status = AWAY
        else:
            log.error(
                f"It appears the Slack API changed, I received an unknown presence type {presence}."
            )
            status = ONLINE
        self.callback_presence(Presence(identifier=idd, status=status))

    def _message_event_handler(self, event):
        """Event handler for the 'message' event"""
        channel = event["channel"]
        if channel[0] not in "CGD":
            log.warning("Unknown message type! Unable to handle %s", channel)
            return

        subtype = event.get("subtype", None)

        if subtype in ("message_deleted", "channel_topic", "message_replied"):
            log.debug("Message of type %s, ignoring this event", subtype)
            return

        if subtype == "message_changed" and "attachments" in event["message"]:
            # If you paste a link into Slack, it does a call-out to grab details
            # from it so it can display this in the chatroom. These show up as
            # message_changed events with an 'attachments' key in the embedded
            # message. We should completely ignore these events otherwise we
            # could end up processing bot commands twice (user issues a command
            # containing a link, it gets processed, then Slack triggers the
            # message_changed event and we end up processing it again as a new
            # message. This is not what we want).
            log.debug(
                "Ignoring message_changed event with attachments, likely caused "
                "by Slack auto-expanding a link"
            )
            return

        if "message" in event:
            text = event["message"].get("text", "")
            user = event["message"].get("user", event.get("bot_id"))
        else:
            text = event.get("text", "")
            user = event.get("user", event.get("bot_id"))

        text, mentioned = self.process_mentions(text)

        text = self.sanitize_uris(text)

        log.debug("Saw an event: %s", pprint.pformat(event))
        log.debug("Escaped IDs event text: %s", text)

        msg = Message(
            text,
            extras={
                "attachments": event.get("attachments"),
                "slack_event": event,
            },
        )

        if channel.startswith("D"):
            if subtype == "bot_message":
                msg.frm = SlackBot(
                    self.sc,
                    bot_id=event.get("bot_id"),
                    bot_username=event.get("username", ""),
                )
            else:
                msg.frm = SlackPerson(self.sc, user, event["channel"])
            msg.to = SlackPerson(
                self.sc,
                self.username_to_userid(self.sc.server.username),
                event["channel"],
            )
            channel_link_name = event["channel"]
        else:
            if subtype == "bot_message":
                msg.frm = SlackRoomBot(
                    self.sc,
                    bot_id=event.get("bot_id"),
                    bot_username=event.get("username", ""),
                    channelid=event["channel"],
                    bot=self,
                )
            else:
                msg.frm = SlackRoomOccupant(self.sc, user, event["channel"], bot=self)
            msg.to = SlackRoom(channelid=event["channel"], bot=self)
            channel_link_name = msg.to.name

        msg.extras["url"] = (
            f"https://{self.sc.server.domain}.slack.com/archives/"
            f'{channel_link_name}/p{self._ts_for_message(msg).replace(".", "")}'
        )

        self.callback_message(msg)

        if mentioned:
            self.callback_mention(msg, mentioned)

    def _member_joined_channel_event_handler(self, event):
        """Event handler for the 'member_joined_channel' event"""
        user = SlackPerson(self.sc, event["user"])
        if user == self.bot_identifier:
            user = self.bot_identifier
        self.callback_room_joined(SlackRoom(channelid=event["channel"], bot=self), user)

    def _reaction_event_handler(self, event):
        """Event handler for the 'reaction_added'
        and 'reaction_removed' events"""

        user = SlackPerson(self.sc, event["user"])
        item_user = None
        if event["item_user"]:
            item_user = SlackPerson(self.sc, event["item_user"])

        action = REACTION_ADDED
        if event["type"] == "reaction_removed":
            action = REACTION_REMOVED

        reaction = Reaction(
            reactor=user,
            reacted_to_owner=item_user,
            action=action,
            timestamp=event["event_ts"],
            reaction_name=event["reaction"],
            reacted_to=event["item"],
        )

        self.callback_reaction(reaction)

    def userid_to_username(self, id_):
        """Convert a Slack user ID to their user name"""
        user = self.sc.server.users.get(id_)
        if user is None:
            raise UserDoesNotExistError(f"Cannot find user with ID {id_}.")
        return user.name

    def username_to_userid(self, name):
        """Convert a Slack user name to their user ID"""
        name = name.lstrip("@")
        user = self.sc.server.users.find(name)
        if user is None:
            raise UserDoesNotExistError(f"Cannot find user {name}.")
        return user.id

    def channelid_to_channelname(self, id_):
        """Convert a Slack channel ID to its channel name"""
        channel = [channel for channel in self.sc.server.channels if channel.id == id_]
        if not channel:
            raise RoomDoesNotExistError(f"No channel with ID {id_} exists.")
        return channel[0].name

    def channelname_to_channelid(self, name):
        """Convert a Slack channel name to its channel ID"""
        name = name.lstrip("#")
        channel = [
            channel for channel in self.sc.server.channels if channel.name == name
        ]
        if not channel:
            raise RoomDoesNotExistError(f"No channel named {name} exists")
        return channel[0].id

    def channels(self, exclude_archived=True, joined_only=False, types=room_types):
        """
        Get all channels and groups and return information about them.

        :param exclude_archived:
            Exclude archived channels/groups
        :param joined_only:
            Filter out channels the bot hasn't joined
        :returns:
            A list of channel (https://api.slack.com/types/channel)
            and group (https://api.slack.com/types/group) types.

        See also:
          * https://api.slack.com/methods/conversations.list
        """
        response = self.api_call(
            "conversations.list",
            data={"exclude_archived": exclude_archived, "types": types},
        )
        channels = [
            channel
            for channel in response["channels"]
            if channel["is_member"] or not joined_only
        ]

        # There is no need to list groups anymore.  Groups are now identified as 'private_channel'
        # type using the conversations.list api method.
        # response = self.api_call('groups.list', data={'exclude_archived': exclude_archived})
        # No need to filter for 'is_member' in this next call (it doesn't
        # (even exist) because leaving a group means you have to get invited
        # back again by somebody else.
        # groups = [group for group in response['groups']]

        return channels

    @lru_cache(1024)
    def get_im_channel(self, id_):
        """Open a direct message channel to a user"""
        try:
            response = self.api_call("conversations.open", data={"users": id_})
            return response["channel"]["id"]
        except SlackAPIResponseError as e:
            if e.error == "cannot_dm_bot":
                log.info("Tried to DM a bot.")
                return None
            else:
                raise e

    def _prepare_message(self, msg):  # or card
        """
        Translates the common part of messaging for Slack.
        :param msg: the message you want to extract the Slack concept from.
        :return: a tuple to user human readable, the channel id
        """
        if msg.is_group:
            to_channel_id = msg.to.id
            to_humanreadable = (
                msg.to.name
                if msg.to.name
                else self.channelid_to_channelname(to_channel_id)
            )
        else:
            to_humanreadable = msg.to.username
            to_channel_id = msg.to.channelid
            if to_channel_id.startswith("C"):
                log.debug(
                    "This is a divert to private message, sending it directly to the user."
                )
                to_channel_id = self.get_im_channel(
                    self.username_to_userid(msg.to.username)
                )
        return to_humanreadable, to_channel_id

    def send_message(self, msg):
        super().send_message(msg)

        if msg.parent is not None:
            # we are asked to reply to a specify thread.
            try:
                msg.extras["thread_ts"] = self._ts_for_message(msg.parent)
            except KeyError:
                # Gives to the user a more interesting explanation if we cannot find a ts from the parent.
                log.exception(
                    "The provided parent message is not a Slack message "
                    "or does not contain a Slack timestamp."
                )

        to_humanreadable = "<unknown>"
        try:
            if msg.is_group:
                to_channel_id = msg.to.id
                to_humanreadable = (
                    msg.to.name
                    if msg.to.name
                    else self.channelid_to_channelname(to_channel_id)
                )
            else:
                to_humanreadable = msg.to.username
                if isinstance(
                    msg.to, RoomOccupant
                ):  # private to a room occupant -> this is a divert to private !
                    log.debug(
                        "This is a divert to private message, sending it directly to the user."
                    )
                    to_channel_id = self.get_im_channel(
                        self.username_to_userid(msg.to.username)
                    )
                else:
                    to_channel_id = msg.to.channelid

            msgtype = "direct" if msg.is_direct else "channel"
            log.debug(
                "Sending %s message to %s (%s).",
                msgtype,
                to_humanreadable,
                to_channel_id,
            )
            body = self.md.convert(msg.body)
            log.debug("Message size: %d.", len(body))

            parts = self.prepare_message_body(body, self.message_size_limit)

            timestamps = []
            for part in parts:
                data = {
                    "channel": to_channel_id,
                    "text": part,
                    "unfurl_media": "true",
                    "link_names": "1",
                    "as_user": "******",
                }

                # Keep the thread_ts to answer to the same thread.
                if "thread_ts" in msg.extras:
                    data["thread_ts"] = msg.extras["thread_ts"]

                result = self.api_call("chat.postMessage", data=data)
                timestamps.append(result["ts"])

            msg.extras["ts"] = timestamps
        except Exception:
            log.exception(
                f"An exception occurred while trying to send the following message "
                f"to {to_humanreadable}: {msg.body}."
            )

    def _slack_upload(self, stream: Stream) -> None:
        """
        Performs an upload defined in a stream
        :param stream: Stream object
        :return: None
        """
        try:
            stream.accept()
            resp = self.api_call(
                "files.upload",
                data={
                    "channels": stream.identifier.channelid,
                    "filename": stream.name,
                    "file": stream,
                },
            )
            if "ok" in resp and resp["ok"]:
                stream.success()
            else:
                stream.error()
        except Exception:
            log.exception(
                f"Upload of {stream.name} to {stream.identifier.channelname} failed."
            )

    def send_stream_request(
        self,
        user: Identifier,
        fsource: BinaryIO,
        name: str = None,
        size: int = None,
        stream_type: str = None,
    ) -> Stream:
        """
        Starts a file transfer. For Slack, the size and stream_type are unsupported

        :param user: is the identifier of the person you want to send it to.
        :param fsource: is a file object you want to send.
        :param name: is an optional filename for it.
        :param size: not supported in Slack backend
        :param stream_type: not supported in Slack backend

        :return Stream: object on which you can monitor the progress of it.
        """
        stream = Stream(user, fsource, name, size, stream_type)
        log.debug(
            "Requesting upload of %s to %s (size hint: %d, stream type: %s).",
            name,
            user.channelname,
            size,
            stream_type,
        )
        self.thread_pool.apply_async(self._slack_upload, (stream,))
        return stream

    def send_card(self, card: Card):
        if isinstance(card.to, RoomOccupant):
            card.to = card.to.room
        to_humanreadable, to_channel_id = self._prepare_message(card)
        attachment = {}
        if card.summary:
            attachment["pretext"] = card.summary
        if card.title:
            attachment["title"] = card.title
        if card.link:
            attachment["title_link"] = card.link
        if card.image:
            attachment["image_url"] = card.image
        if card.thumbnail:
            attachment["thumb_url"] = card.thumbnail

        if card.color:
            attachment["color"] = (
                COLORS[card.color] if card.color in COLORS else card.color
            )

        if card.fields:
            attachment["fields"] = [
                {"title": key, "value": value, "short": True}
                for key, value in card.fields
            ]

        parts = self.prepare_message_body(card.body, self.message_size_limit)
        part_count = len(parts)
        footer = attachment.get("footer", "")
        for i in range(part_count):
            if part_count > 1:
                attachment["footer"] = f"{footer} [{i + 1}/{part_count}]"
            attachment["text"] = parts[i]
            data = {
                "channel": to_channel_id,
                "attachments": json.dumps([attachment]),
                "link_names": "1",
                "as_user": "******",
            }
            try:
                log.debug("Sending data:\n%s", data)
                self.api_call("chat.postMessage", data=data)
            except Exception:
                log.exception(
                    f"An exception occurred while trying to send a card to {to_humanreadable}.[{card}]"
                )

    def __hash__(self):
        return 0  # this is a singleton anyway

    def change_presence(self, status: str = ONLINE, message: str = "") -> None:
        self.api_call(
            "users.setPresence",
            data={"presence": "auto" if status == ONLINE else "away"},
        )

    @staticmethod
    def prepare_message_body(body, size_limit):
        """
        Returns the parts of a message chunked and ready for sending.

        This is a staticmethod for easier testing.

        Args:
            body (str)
            size_limit (int): chunk the body into sizes capped at this maximum

        Returns:
            [str]

        """
        fixed_format = body.startswith("```")  # hack to fix the formatting
        parts = list(split_string_after(body, size_limit))

        if len(parts) == 1:
            # If we've got an open fixed block, close it out
            if parts[0].count("```") % 2 != 0:
                parts[0] += "\n```\n"
        else:
            for i, part in enumerate(parts):
                starts_with_code = part.startswith("```")

                # If we're continuing a fixed block from the last part
                if fixed_format and not starts_with_code:
                    parts[i] = "```\n" + part

                # If we've got an open fixed block, close it out
                if part.count("```") % 2 != 0:
                    parts[i] += "\n```\n"

        return parts

    @staticmethod
    def extract_identifiers_from_string(text):
        """
        Parse a string for Slack user/channel IDs.

        Supports strings with the following formats::

            <#C12345>
            <@U12345>
            <@U12345|user>
            @user
            #channel/user
            #channel

        Returns the tuple (username, userid, channelname, channelid).
        Some elements may come back as None.
        """
        exception_message = (
            "Unparseable slack identifier, should be of the format `<#C12345>`, `<@U12345>`, "
            "`<@U12345|user>`, `@user`, `#channel/user` or `#channel`. (Got `%s`)"
        )
        text = text.strip()

        if text == "":
            raise ValueError(exception_message % "")

        channelname = None
        username = None
        channelid = None
        userid = None

        if text[0] == "<" and text[-1] == ">":
            exception_message = (
                "Unparseable slack ID, should start with U, B, C, G, D or W (got `%s`)"
            )
            text = text[2:-1]
            if text == "":
                raise ValueError(exception_message % "")
            if text[0] in ("U", "B", "W"):
                if "|" in text:
                    userid, username = text.split("|")
                else:
                    userid = text
            elif text[0] in ("C", "G", "D"):
                channelid = text
            else:
                raise ValueError(exception_message % text)
        elif text[0] == "@":
            username = text[1:]
        elif text[0] == "#":
            plainrep = text[1:]
            if "/" in text:
                channelname, username = plainrep.split("/", 1)
            else:
                channelname = plainrep
        else:
            raise ValueError(exception_message % text)

        return username, userid, channelname, channelid

    def build_identifier(self, txtrep):
        """
        Build a :class:`SlackIdentifier` from the given string txtrep.

        Supports strings with the formats accepted by
        :func:`~extract_identifiers_from_string`.
        """
        log.debug("building an identifier from %s.", txtrep)
        username, userid, channelname, channelid = self.extract_identifiers_from_string(
            txtrep
        )

        if userid is None and username is not None:
            userid = self.username_to_userid(username)
        if channelid is None and channelname is not None:
            channelid = self.channelname_to_channelid(channelname)
        if userid is not None and channelid is not None:
            return SlackRoomOccupant(self.sc, userid, channelid, bot=self)
        if userid is not None:
            return SlackPerson(self.sc, userid, self.get_im_channel(userid))
        if channelid is not None:
            return SlackRoom(channelid=channelid, bot=self)

        raise Exception(
            "You found a bug. I expected at least one of userid, channelid, username or channelname "
            "to be resolved but none of them were. This shouldn't happen so, please file a bug."
        )

    def is_from_self(self, msg: Message) -> bool:
        return self.bot_identifier.userid == msg.frm.userid

    def build_reply(self, msg, text=None, private=False, threaded=False):
        response = self.build_message(text)

        if "thread_ts" in msg.extras["slack_event"]:
            # If we reply to a threaded message, keep it in the thread.
            response.extras["thread_ts"] = msg.extras["slack_event"]["thread_ts"]
        elif threaded:
            # otherwise check if we should start a new thread
            response.parent = msg

        response.frm = self.bot_identifier
        if private:
            response.to = msg.frm
        else:
            response.to = msg.frm.room if isinstance(msg.frm, RoomOccupant) else msg.frm
        return response

    def add_reaction(self, msg: Message, reaction: str) -> None:
        """
        Add the specified reaction to the Message if you haven't already.
        :param msg: A Message.
        :param reaction: A str giving an emoji, without colons before and after.
        :raises: ValueError if the emoji doesn't exist.
        """
        return self._react("reactions.add", msg, reaction)

    def remove_reaction(self, msg: Message, reaction: str) -> None:
        """
        Remove the specified reaction from the Message if it is currently there.
        :param msg: A Message.
        :param reaction: A str giving an emoji, without colons before and after.
        :raises: ValueError if the emoji doesn't exist.
        """
        return self._react("reactions.remove", msg, reaction)

    def _react(self, method: str, msg: Message, reaction: str) -> None:
        try:
            # this logic is from send_message
            if msg.is_group:
                to_channel_id = msg.to.id
            else:
                to_channel_id = msg.to.channelid

            ts = self._ts_for_message(msg)

            self.api_call(
                method,
                data={"channel": to_channel_id, "timestamp": ts, "name": reaction},
            )
        except SlackAPIResponseError as e:
            if e.error == "invalid_name":
                raise ValueError(e.error, "No such emoji", reaction)
            elif e.error in ("no_reaction", "already_reacted"):
                # This is common if a message was edited after you reacted to it, and you reacted to it again.
                # Chances are you don't care about this. If you do, call api_call() directly.
                pass
            else:
                raise SlackAPIResponseError(error=e.error)

    def _ts_for_message(self, msg):
        try:
            return msg.extras["slack_event"]["message"]["ts"]
        except KeyError:
            return msg.extras["slack_event"]["ts"]

    def shutdown(self):
        super().shutdown()

    @property
    def mode(self):
        return "slack"

    def query_room(self, room):
        """ Room can either be a name or a channelid """
        if room.startswith("C") or room.startswith("G"):
            return SlackRoom(channelid=room, bot=self)

        m = SLACK_CLIENT_CHANNEL_HYPERLINK.match(room)
        if m is not None:
            return SlackRoom(channelid=m.groupdict()["id"], bot=self)

        return SlackRoom(name=room, bot=self)

    def rooms(self):
        """
        Return a list of rooms the bot is currently in.

        :returns:
            A list of :class:`~SlackRoom` instances.
        """
        channels = self.channels(
            joined_only=True,
            exclude_archived=True,
        )
        return [SlackRoom(channelid=channel["id"], bot=self) for channel in channels]

    def prefix_groupchat_reply(self, message, identifier):
        super().prefix_groupchat_reply(message, identifier)
        message.body = f"@{identifier.nick}: {message.body}"

    @staticmethod
    def sanitize_uris(text):
        """
        Sanitizes URI's present within a slack message. e.g.
        <mailto:[email protected]|[email protected]>,
        <http://example.org|example.org>
        <http://example.org>

        :returns:
            string
        """
        text = re.sub(r"<([^|>]+)\|([^|>]+)>", r"\2", text)
        text = re.sub(r"<(http([^>]+))>", r"\1", text)

        return text

    def process_mentions(self, text):
        """
        Process mentions in a given string
        :returns:
            A formatted string of the original message
            and a list of :class:`~SlackPerson` instances.
        """
        mentioned = []

        m = re.findall("<@[^>]*>*", text)

        for word in m:
            try:
                identifier = self.build_identifier(word)
            except Exception as e:
                log.debug(
                    "Tried to build an identifier from '%s' but got exception: %s",
                    word,
                    e,
                )
                continue

            # We only track mentions of persons.
            if isinstance(identifier, SlackPerson):
                log.debug("Someone mentioned")
                mentioned.append(identifier)
                text = text.replace(word, str(identifier))

        return text, mentioned
Пример #40
0
class RtmBot(object):
    def __init__(self, token):
        self.last_ping = 0
        self.token = token
        self.bot_plugins = []
        self.slack_client = None
    def connect(self):
        """Convenience method that creates Server instance"""
        self.slack_client = SlackClient(self.token)
        self.slack_client.rtm_connect()
    def start(self):
        self.connect()
        self.load_plugins()
        while True:
            for reply in self.slack_client.rtm_read():
                self.input(reply)
            self.crons()
            self.output()
            self.autoping()
            time.sleep(.1)
    def autoping(self):
        #hardcode the interval to 3 seconds
        now = int(time.time())
        if now > self.last_ping + 3:
            self.slack_client.server.ping()
            self.last_ping = now
    def input(self, data):
        if "type" in data:
            function_name = "process_" + data["type"]
            logging.debug("got {}".format(function_name))
            for plugin in self.bot_plugins:
                plugin.register_jobs()
                plugin.do(function_name, data)
    def output(self):
        for plugin in self.bot_plugins:
            limiter = False
            for output in plugin.do_output():
                channel = self.slack_client.server.channels.find(output[0])
                if channel != None and output[1] != None:
                    if limiter == True:
                        time.sleep(.1)
                        limiter = False
                    message = output[1].encode('ascii','ignore')
                    channel.send_message("{}".format(message))
                    limiter = True
    def crons(self):
        for plugin in self.bot_plugins:
            plugin.do_jobs()
    def load_plugins(self):
        import plugins

        for plugin in glob.glob(os.path.join(plugins.__path__[0], '*')):
            sys.path.insert(0, plugin)
            sys.path.insert(0, os.path.join(plugins.__path__[0]))
        for plugin in glob.glob(os.path.join(plugins.__path__[0], '*.py')) + glob.glob(os.path.join(plugins.__path__[0], '*','*.py')):
            logging.info(plugin)
            name = os.path.split(plugin)[-1][:-3]
            try:
                self.bot_plugins.append(Plugin(name))
            except:
                print "error loading plugin %s" % name
Пример #41
0
    check = 0
    transfer = 0
    add = 0
    remove = 0
    _help = 0

    sender_id = ""
    message_give_id = ""
    message_take_id = ""

    sender_name = ""
    message_give_name = ""
    message_take_name = ""
    #-----------------------------------------------------
    #Get Message and Such
    for message in slack_client.rtm_read():
        if 'text' in message:
            print(f'Message received:\n{json.dumps(message, indent=2)}')
            message_text = message['text']
            message_text = message_text.lower()
            sender_id = message['user']
            sender_id = sender_id.lower()
            message_text = message_text.replace('me', f'<@{sender_id}>')

            #-----------------------------------------------------
            #Parse Message actions and ID's
            if re.match(r'.*(help).*', message_text, re.IGNORECASE):
                _help = 1

            if re.match(r'.*(check|who).*', message_text, re.IGNORECASE):
                check = 1
Пример #42
0
                print(output)
                return output['text'].split(AT_BOT)[1].strip().lower(), \
                       output['channel']
    return None, None


if __name__ == "__main__":
    envset.env()
    # starterbot's ID as an environment variable
    BOT_ID = os.environ.get("BOT_ID")
    token = os.environ.get("SLACK_BOT_TOKEN")

    # constants
    AT_BOT = "<@" + BOT_ID + ">"
    EXAMPLE_COMMAND = "do"

    # instantiate Slack & Twilio clients
    slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))

    READ_WEBSOCKET_DELAY = 1 # 1 second delay between reading from firehose
    if slack_client.rtm_connect():
        print("StarterBot connected and running!")
        while True:
            # print(slack_client.rtm_read())
            command, channel = parse_slack_output(slack_client.rtm_read())
            # print(command, channel)
            if command and channel:
                handle_command(command, channel)
            time.sleep(READ_WEBSOCKET_DELAY)
    else:
        print("Connection failed. Invalid Slack token or bot ID?")
Пример #43
0
                    for user in users:
                        if user.get('id') == output['user']:
                            user_name = user.get('real_name')
                        if user.get('real_name') == user_n:
                            user_id1 = user.get('id')
                            user_name1 = user.get('real_name')

                    cursor = connection.cursor()
                    cursor.execute("INSERT INTO slack_bot_app_message (user_id, user_name, text, team_id, ts) "
                                   "VALUES (%s, %s, %s, %s, %s)",
                            (user_id1, user_name1, output['text'], 1, output['event_ts']))
                    connection.commit()

    return None, None, None, None, None, None


if __name__ == "__main__":
    slack_client = SlackClient("xoxb-284823465173-QKs8E9L0PQStyJLc3kTjRxQc")

    READ_WEBSOCKET_DELAY = 1
    if slack_client.rtm_connect():
        print("StarterBot connected and running!")
        while True:
            command, channel, user, thread, ts, team = parse_slack_output(slack_client.rtm_read())
            if command and channel and user and thread == 0:
                handle_command(command, user, team)
            if command and channel and user and thread == 1:
                direct_command(command, user, ts)
            time.sleep(READ_WEBSOCKET_DELAY)
    else:
        print("Connection failed. Invalid Slack token or bot ID?")
Пример #44
0
				"value": "yes"
				},{
				"name": "no",
				"text": "No",
				"type": "button",
				"value": "no"
				}
			]
		}]
	slack_client.api_call("chat.postMessage", channel=channel, text=response, as_user=True, attachments= attachments)



if __name__ == '__main__':
	READ_WEBSOCKET_DELAY = 0.5 #per second
	if slack_client.rtm_connect():
		print(BOT_NAME + " connected and running!")
		while True:
			rtm = slack_client.rtm_read()

			if rtm:
				print rtm

			command, channel = parse_slack_output(rtm)

			if command and channel:
				handle_command(command, channel)

			time.sleep(READ_WEBSOCKET_DELAY)
	else:
		print(BOT_NAME+ " not connected!")
Пример #45
0
        The Slack Real Time Messaging API is an events firehose.
        this parsing function returns None unless a message is
        directed at the Bot, based on its ID.
    """
    output_list = slack_rtm_output
    if output_list and len(output_list) > 0:
        for output in output_list:
            if output and 'text' in output and 'user' in output:
                recieveMessageFromUser(IDToUser[output['user']],
                                       output['text'])

    return


if __name__ == "__main__":
    if not slack_client.rtm_connect():
        print("Connection failed. Invalid Slack token or bot ID?")
        exit()

    IDToUser = getSlackUsers()

    for id, user in IDToUser.iteritems():
        if user.name == "ashley.coleman":  # Remove for release
            sendMessageToUser(user)

    READ_WEBSOCKET_DELAY = 1  # 1 second delay between reading from firehose
    print("Bot connected and running!")
    while True:
        parseSlackMessage(slack_client.rtm_read(), IDToUser)
        time.sleep(READ_WEBSOCKET_DELAY)
Пример #46
0
class AA5ROBot:
    """
    A Slack bot for the AARO Slack site.
    """
    def __init__(self):
        # Get the Bot token from the environment.  Raises RunTimeError if the
        # value isn't set because the bot can't run without a token configured.
        slack_bot_token = os.environ.get('SLACK_BOT_TOKEN')
        if slack_bot_token == '':
            raise RuntimeError(
                'SLACK_BOT_TOKEN must be set in the environment.')

        # Create the main SlackClient instance for the bot
        self.slack_client = SlackClient(slack_bot_token)

        # start initial connection to Slack RTM
        if self.slack_client.rtm_connect(with_team_state=False):
            logger.info("AA5ROBot connected to Slack.")
            self.aa5robot_id = self.slack_client.api_call(
                "auth.test")["user_id"]
        else:
            logger.warning("Connection to Slack RTM failed.")
            self.shutdown(1)

        # Load the bot's commands
        self.commands = command.get_commands()

        print('AA5RObot initialized.')

    def start(self):
        reconnects = 0

        # Process events from Slack RTM until ctrl-c
        logger.info('Processing events from Slack...')
        while self.slack_client.server.connected is True:
            try:
                data, channel = self.parse_bot_commands(
                    self.slack_client.rtm_read())
                if data:
                    self.handle_command(data, channel)
                time.sleep(RTM_READ_DELAY)
            except KeyboardInterrupt:
                self.shutdown()

        # If execution gets here, the connection to the server was interrupted.
        # Attempt up to MAX_RECONNECT_ATTEMPTS tries to reconnect to Slack.
        while reconnects < MAX_RECONNECT_ATTEMPTS:
            print(
                'AA5RObot lost connection to Slack.  Attempting reconnect, try {}'
                .format(reconnects + 1))
            if self.slack_client.rtm_connect(with_team_state=False):
                print("AA5ROBot reconnected to Slack.")
                self.aa5robot_id = slack_client.api_call(
                    "auth.test")["user_id"]
                self.start()
            else:
                reconnects += 1
                time.sleep(RECONNECT_WAIT_TIME)

        # Bot was unable to reconnect, so end the process.
        print('Unable to reconnect to Slack.  Exiting.')
        self.shutdown(1)

    def shutdown(self, exit_code=0):
        """
        Execute cleanup tasks before exiting the process.
        """
        # call shutdown method on all command instances
        for instance in self.commands:
            instance[1].shutdown()

        # end the process
        print("AA5ROBot exiting.")
        sys.exit(exit_code)

    def parse_bot_commands(self, slack_events):
        """
        Parses a list of events coming from the Slack RTM API to find bot commands.
        If a bot command is found, this function returns a tuple of command and channel.
        If a command is not found, then this function returns None, None.
        """
        for event in slack_events:
            try:
                if event["type"] == "message" and not "subtype" in event:
                    user_id, message = self.parse_direct_mention(event["text"])
                    if user_id == self.aa5robot_id:
                        return message, event["channel"]
            except KeyError:
                return None, None

        return None, None

    def parse_direct_mention(self, message_text):
        """
        Finds a direct mention (a mention that is at the beginning) in message text
        and returns the user ID which was mentioned. If there is no direct mention, returns None.
        """
        matches = re.search("^<@(|[WU].+?)>(.*)", message_text)
        # the first group contains the username, the second group contains the remaining message
        return (matches.group(1),
                matches.group(2).strip()) if matches else (None, None)

    def handle_command(self, data, channel):
        """
            Executes a bot command.
        """
        logger.debug('channel: {}, data: {}'.format(channel, data))

        # get command string
        try:
            command_str = data.split()[0].lower()
        except IndexError:
            self.send_message(
                channel,
                "Not sure what you mean.  Tell me 'help' for more info.")
            return

        if command_str == '':
            self.send_message(
                channel,
                "Not sure what you mean.  Tell me 'help' for more info.")
            return

        if command_str == 'help' or command_str == '?':
            self.handle_help(channel)
            return

        command_strings = [i[0] for i in self.commands]
        if command_str in command_strings:
            logger.info("Executing command '{}'.".format(command_str))
            method, response = self.commands[command_strings.index(
                command_str)][1].do_command(data)

            if method == command.MessageTypes.RTM_MESSAGE:
                self.send_message(channel, response)

            if method == command.MessageTypes.API_CALL:
                self.chat_post_message(channel, response)

        else:
            self.send_message(
                channel,
                "Not sure what you mean.  Tell me 'help' for more info.")
            return

    def handle_help(self, channel):
        """
        Sends the bot's help message to Slack.
        """
        self.send_message(channel, "Help not implemented yet!")

    def send_message(self, channel, response):
        """
        Send a text-only response via the RTM API.
        """
        self.slack_client.rtm_send_message(channel, response)

    def chat_post_message(self, channel, response):
        """
        Send a chat.postMessage API call.
        """
        self.slack_client.api_call("chat.postMessage",
                                   channel=channel,
                                   as_user=True,
                                   attachments=json.dumps(response))
Пример #47
0
class SlackBot(object):
    def __init__(self, token, reply_func, only_speaking_to_me=True):
        self.last_ping = 0
        self.token = token
        self.reply_func = reply_func
        self.slack_client = None
        self.user_id = None
        self.only_speaking_to_me = only_speaking_to_me

    def _connect(self):
        self.slack_client = SlackClient(self.token)
        self.user_id = self.slack_client.api_call('auth.test')['user_id']
        self.slack_client.rtm_connect()
        time.sleep(1)
        self.slack_client.rtm_read()

    def start(self):
        while True:
            self._connect()
            try:
                while True:
                    outbox = []
                    for event in self.slack_client.rtm_read():
                        # print event
                        outbox += self._process_event(event)
                    self._send_messages(outbox)
                    self._autoping()
                    time.sleep(0.1)
            except Exception as e:
                traceback.print_exc()
            time.sleep(60)  # What happened?; try to reconnect

    def _autoping(self):
        # hardcode the interval to 3 seconds
        now = int(time.time())
        if now > self.last_ping + 3:
            self.slack_client.server.ping()
            self.last_ping = now

    def _process_event(self, event):
        channel = event.get('channel')
        mtext = None
        callout = "<@{}>".format(self.user_id)

        if event.get('type') == 'message' and \
                channel and event.get('text') and \
                event.get('user', self.user_id) != self.user_id:
            if channel.startswith("D") or callout in event['text']:
                event['speaking_to_me'] = True
            else:
                event['speaking_to_me'] = False

            event['username'] = self.slack_client.server.users.find(
                event.get('user')).name
            # Turn "@your_bot: a bunch of words" into "a bunch of words"
            event['text_query'] = event['text'].replace(callout + ':',
                                                        ' ').replace(
                                                            callout, ' ')

            if not self.only_speaking_to_me or event['speaking_to_me']:
                mtext = self.reply_func(self.slack_client, event)

        if mtext:
            return [Message(channel=channel, text=mtext)]
        else:
            return []

    def _send_messages(self, outbox):
        limiter = False
        for message in outbox:
            channel_obj = self.slack_client.server.channels.find(
                message.channel)
            if channel_obj is not None and message.text:
                if limiter:
                    time.sleep(1)
                limiter = True
                channel_obj.send_message(u"{}".format(message.text))
                print u"Sent: " + message.text + u"  To: " + channel_obj.name
Пример #48
0
    matches = re.search(MENCION_REGEX, texto)
    return (matches.group(1), matches.group(2).strip()) if matches else (None,
                                                                         None)


def maneja_comando(comando, canal):
    """
        Ejecuta el comando si se conoce.
    """
    # Default response is help text for the user
    default_response = "No te entiendo. Prueba *ve*."

    response = comandos.maneja(comando)

    slack_client.api_call("chat.postMessage",
                          channel=canal,
                          text=response or default_response)


if __name__ == "__main__":
    if slack_client.rtm_connect(with_team_state=False):
        print("¡Vamos charlandero!")
        starterbot_id = slack_client.api_call("auth.test")["user_id"]
        while True:
            command, channel = procesa_comandos(slack_client.rtm_read())
            if command:
                maneja_comando(command, channel)
            time.sleep(RTM_READ_DELAY)
    else:
        print("Ha fallado. Lee más abajo para averiguar por qué")
class QASlackBot:
    buildparams = {}
    client = None
    my_user_name = ''
    userdict = {}
    reservedict = {}
    channel = None
    message = None
    buildparamsList = []

    def userlist(self):
        api_call = self.client.api_call("users.list")
        if api_call.get('ok'):
            # retrieve all users
            users = api_call.get('members')
            for user in users:
                self.userdict[user['id']] = user['name']
        #log.debug(self.userdict)

    def connect(self, token):
        self.client = SlackClient(token)
        self.client.rtm_connect()
        self.my_user_name = self.client.server.username
        log.debug("Connected to Slack as " + self.my_user_name)

    def listen(self):
        while True:
            try:
                input = self.client.rtm_read()
                if input:
                    for action in input:
                        log.debug(action)
                        if 'type' in action and action['type'] == "message":
                            self.process_message(action)
                else:
                    sleep(1)

                    # Check for time reserved and release when time is up
                for key in topics.keys():
                    if key in self.reservedict:
                        elapsed = datetime.now() - self.reservedict[key][1]
                        if elapsed.total_seconds() > TIMEOUT:
                            msg = "@{0} уже 8 часов занимает сервер! Освобождаю `{1}`".format(
                                self.reservedict[key][0], key)
                            log.debug(msg)
                            self.post(self.reservedict[key][2], msg)
                            del self.reservedict[key]

            except Exception as e:
                pass
                #log.error("Exception: ", e.message)

    def process_message(self, message):
        self.channel = message['channel']
        self.message = message['text']

        if self.message.lower().find(" help") == 12:
            self.help()
        elif self.message.lower().find(" status") == 12:
            self.status()

        for key in topics.keys():
            if self.message.lower().startswith(
                    "take " + key) or self.message.lower().startswith(
                        "t " +
                        key) or self.message.lower().startswith(key + " take"):
                id = message['user']
                # Hold state of who is using the stack
                if key not in self.reservedict:
                    response = self.newreservation(key, id)
                else:
                    response = self.existingReservation(key, id)
            elif key in self.reservedict and (
                    self.message.lower().startswith("free " + key)
                    or self.message.lower().startswith("f " + key)
                    or self.message.lower().startswith(key + " free")):
                response = self.releaseStack(key)

    def post(self, channel, message):
        chan = self.client.server.channels.find(channel)
        if not chan:
            raise Exception("Channel %s not found." % channel)

        return chan.send_message(message)

    def help(self):
        self.post(
            self.channel,
            "```Добро пожаловать в систему резервации тестовых серверов! \n\n Список всех серверов:\n \
qa1\n qa2\n qa3\n qa4\n stage2\n sandbox1\n prod\n- Зарезервировать сервер: 't <server>' ИЛИ 'take <server>' ИЛИ '<server> take'\n- Освободить сервер: 'f <server>' ИЛИ 'free <server>' ИЛИ '<server> free'\n- Проверить статус свободных серверов: \
'@qabot status'\n\nЛимит использования - 8 часов. Если используете дольше, забейте снова.```"
        )

    def status(self):
        if not self.reservedict.keys():
            self.post(self.channel, "Все свободно!")
        for key in self.reservedict.keys():
            response = topics[key].format(self.reservedict[key][0], key)
            self.post(self.channel, response)
            log.info(response)

    def newreservation(self, key, id):
        log.info("not there")
        self.reservedict[key] = [
            self.userdict[id], datetime.now(), self.channel
        ]
        response = topics[key].format(self.userdict[id], key)
        log.info("Posting to {0}: {1}".format(self.channel, response))
        self.post(self.channel, response)

    def existingReservation(self, key, id):
        log.info("Stack already taken")
        response = "Сервер уже занят."
        log.info("Posting to {0}: {1}".format(self.channel, response))
        self.post(self.channel, response)

    def releaseStack(self, key):
        log.info("release by user")
        response = self.reservedict[key][0] + " занял сервер " + key
        self.post(self.reservedict[key][2], response)
        del self.reservedict[key]
Пример #50
0
class RtmBot(object):
    def __init__(self, token):
        self.last_ping = 0
        self.token = token
        self.bot_plugins = []
        self.slack_client = None
        self.bot_on = True
        self.mode = HELPFUL

    def connect(self):
        """Convenience method that creates Server instance"""
        self.slack_client = SlackClient(self.token)
        self.slack_client.rtm_connect()
        logging.info(u"Connected {} to {} team at https://{}.slack.com".format(
            self.slack_client.server.username,
            self.slack_client.server.login_data['team']['name'],
            self.slack_client.server.domain))

    def start(self):
        self.connect()
        self.load_plugins()
        while True:
            for reply in self.slack_client.rtm_read():
                self.input(reply)
            self.crons()
            self.output()
            self.autoping()
            time.sleep(.1)

    def autoping(self):
        #hardcode the interval to 60 seconds
        now = int(time.time())
        if now > self.last_ping + 60:
            self.slack_client.server.ping()
            self.last_ping = now

    def isBotMention(self, message):
        botUserName = self.slack_client.server.login_data['self']['id']
        if re.search("@{}".format(botUserName), message):
            return True
        else:
            return False

    def input(self, data):
        # Make sure we're not responding to ourselves
        dbg("start input")
        if "user" in data and data[
                'user'] != self.slack_client.server.login_data['self']['id']:

            # data is of proper form
            if "type" in data:

                # Not doing anything with this event yet
                if data["type"] == "user_typing":
                    return

                function_name = "process_" + data["type"]
                dbg("got {}".format(function_name))
                dbg("data {}".format(data))

                if "text" in data:

                    if (self.mode == QUIET):
                        if (data['text'] == "lb start"
                                or data['text'] == "lemonbot start"):
                            if (self.isAdmin(data['user'])):
                                dbg("quiet mode admin")

                                self.mode = HELPFUL
                                function_name = "process_unmute"

                            else:
                                dbg("admin access restriction")
                                function_name = "process_non_admin"

                    elif self.mode == HELPFUL or self.mode == SNARKY:

                        if self.isBotMention(data['text']):
                            dbg("mention")
                            function_name = "process_mention"
                            self.bot_on = True

                        elif data['text'].lower().startswith(
                                "lemonbot") or data['text'].lower().startswith(
                                    "lb"):
                            dbg("command")
                            function_name = "process_helpful"

                            if (True in [
                                    x in data["text"] for x in
                                ["hush", "shutup", "shut up", "quiet", "stfu"]
                            ]):
                                dbg("helpful mode")
                                self.mode = HELPFUL
                                function_name = "process_mode_helpful"

                            elif ("mute" in data['text'].lower()):
                                dbg("before admin check")
                                if (self.isAdmin(data['user'])):
                                    dbg("quiet mode")
                                    self.mode = QUIET
                                    function_name = "process_mode_quiet"
                                else:
                                    dbg("quiet mode access restriction")
                                    function_name = "process_non_admin"

                            elif ("snarky" in data["text"].lower()):
                                dbg("snarky mode")
                                self.mode = SNARKY
                                function_name = "process_mode_snarky"

                        elif self.mode == SNARKY:
                            dbg("snarky")
                            function_name = "process_snarky"

                    for plugin in self.bot_plugins:
                        plugin.register_jobs()
                        plugin.do(function_name, data)

    def isAdmin(self, id):
        user = self.getUser(id)
        dbg("admin print {}".format(user["is_admin"]))
        if user['is_admin'] or user['is_owner'] == "true":
            return (True)
        else:
            return (False)

    def getUser(self, id):
        user = self.slack_client.api_call("users.info",
                                          token="{}".format(self.token),
                                          user="******".format(id))["user"]
        dbg("getUser: {}".format(user['name']))
        return (user)

    def getMembers(self):
        members = self.slack_client.api_call(
            "users.list",
            token="{}".format(self.token),
        )["members"]
        dbg("getMembers: {}".format([x["name"] for x in members]))
        return (members)

    def output(self):
        for plugin in self.bot_plugins:
            limiter = False
            for output in plugin.do_output():
                channel = self.slack_client.server.channels.find(output[0])
                if channel != None and output[1] != None:
                    if limiter == True:
                        time.sleep(.1)
                        limiter = False
                    message = output[1].encode('ascii', 'ignore')
                    if message.startswith("__typing__"):
                        user_typing_json = {
                            "type": "typing",
                            "channel": channel.id
                        }
                        logging.debug(user_typing_json)
                        self.slack_client.server.send_to_websocket(
                            user_typing_json)
                        time.sleep(output[2])
                    else:
                        channel.send_message("{}".format(message))
                        limiter = True
            for attachment in plugin.do_attachment():
                channel = self.slack_client.server.channels.find(attachment[0])
                if channel != None and attachment[1] != None:
                    attachments = []
                    if attachment != None and attachment[2] != None:
                        attachments.append(attachment[2])
                    attachments_json = json.dumps(attachments)
                    resp = self.slack_client.api_call(
                        "chat.postMessage",
                        text="{}".format(attachment[1]),
                        channel="{}".format(channel.id),
                        as_user="******",
                        attachments=attachments_json,
                    )
                    logging.debug(resp)

    def crons(self):
        for plugin in self.bot_plugins:
            plugin.do_jobs()

    def load_plugins(self):
        for plugin in glob.glob(directory + '/plugins/*'):
            sys.path.insert(0, plugin)
            sys.path.insert(0, directory + '/plugins/')
        for plugin in glob.glob(directory + '/plugins/*.py') + glob.glob(
                directory + '/plugins/*/*.py'):
            logging.info(plugin)
            name = plugin.split('/')[-1][:-3]
            #            try:
            self.bot_plugins.append(Plugin(name))
Пример #51
0
    def gender(self, message):
        print('gender mode: processing message')
        # this regex looks for the keywords at the beginning or end of the
        # text, or after or before any non-alphanumeric characters.
        gender_terms = re.compile(
            r'(\W|^)(guys|dude|his|her|hers|she|he)(\W|$)')
        if gender_terms.search(message):
            # construct responses to gendered terms here
            response = "Hello! I'm the loving language bot. We are conducting an experiment in non-gendered language. This bot will periodically suggest that you consider using alternate phrasing that is less gender-specific. This is one of those times. "
            self.reply(response)

    def conditional(self, message):
        print('Mode "conditional" not yet implemented')

    def eprime(self, message):
        print('Mode "eprime" not yet implemented')


if client.rtm_connect():
    langbot = LanguageBot(client)
    langbot.enable('gender')

    while True:
        incoming = client.rtm_read()
        if incoming:
            langbot.new_incoming(incoming)
        time.sleep(1)
else:
    print("Connection Failed")
Пример #52
0
class SmashBot():
    def __init__(self):
        self.slack_client = SlackClient(bot_config.get_slack_api_key())
        self.logger = logging.getLogger('smashbot')

        hdlr = logging.FileHandler(bot_config.get_log_path())
        formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
        hdlr.setFormatter(formatter)
        self.logger.addHandler(hdlr)
        self.logger.setLevel(logging.DEBUG)

        self.logger.debug('booting up smashbot file')

    def keepalive(self):
        while True:
            time.sleep(3)
            try:
                self.slack_client.server.ping()
            except WebSocketConnectionClosedException as e:
                self.logger.debug('Keep alive web socket exception.')
                self.slack_client.rtm_connect()

    def print_help(self, channel):
        message = 'I support the following:'
        message = message + '\n`@sul me over @them 3-2` or `@sul @them over me 3-2` - report a score'
        message = message + '\n`@sul group a` - see the current rankings of a group'
        # message = message + '\n`@sul leaderboard` - see the leaderboard, sorted by winrate'
        # message = message + '\n`@sul loserboard` - see the loserboard, sorted by winrate'
        message = message + '\n`@sul who do i play` - see who you play this week (only in dms)'
        message = message + '\n`@sul matches for week` - see all matches occuring this week in all groups'
        # message = message + '\n`@sul my total stats` - see your total wins and losses (both games and sets)'

        self.slack_client.api_call("chat.postMessage",
                                   channel=channel,
                                   text=message,
                                   as_user=True)

    ### TODO Refactor so it can support Bo3 AND Bo5 across the database
    def get_leaderboard(self, reverse_order=True):
        matches = db.get_matches()
        players = db.get_players()

        player_dict = dict()

        for player in players:
            player_dict[player.slack_id] = {
                'games_won': 0,
                'games_total': 0,
                'name': player.name
            }

        for match in matches:
            games_played = match.sets

            player_1 = player_dict[match.player_1_id]
            player_2 = player_dict[match.player_2_id]

            player_dict[match.player_1_id][
                'games_total'] = player_1['games_total'] + games_played
            player_dict[match.player_2_id][
                'games_total'] = player_2['games_total'] + games_played

            if match.player_1_id == match.winner_id:
                player_dict[
                    match.player_1_id]['games_won'] = player_1['games_won'] + 2

                if games_played == 3:
                    player_dict[match.player_2_id][
                        'games_won'] = player_2['games_won'] + 1

            elif match.player_2_id == match.winner_id:
                player_dict[
                    match.player_2_id]['games_won'] = player_2['games_won'] + 2

                if games_played == 3:
                    player_dict[match.player_1_id][
                        'games_won'] = player_1['games_won'] + 1

        winrate_dict = dict()
        for player_id, player in player_dict.items():
            if player['games_total'] == 0:
                winrate_dict[player['name']] = {
                    'games_won': 0,
                    'games_lost': 0,
                    'winrate': round(0, 2)
                }
            else:
                winrate_dict[player['name']] = {
                    'games_won':
                    player['games_won'],
                    'games_lost':
                    player['games_total'] - player['games_won'],
                    'winrate':
                    round((player['games_won'] / player['games_total']) * 100,
                          2)
                }

        sorted_winrates = collections.OrderedDict(
            sorted(winrate_dict.items(),
                   key=lambda x: x[1]['winrate'],
                   reverse=reverse_order))

        return sorted_winrates

    ### TODO Refactor so it can support Bo3 AND Bo5 across the database
    def print_leaderboard(self, channel):
        sorted_winrates = self.get_leaderboard()

        message = ""
        for player_name in list(sorted_winrates)[:10]:
            player_object = sorted_winrates[player_name]
            message = message + f"\n {player_name}: {player_object['winrate']}% ({player_object['games_won']}-{player_object['games_lost']})"

        self.slack_client.api_call("chat.postMessage",
                                   channel=channel,
                                   text=message,
                                   as_user=True)

    ### TODO Refactor so it can support Bo3 AND Bo5 across the database
    def print_loserboard(self, channel):
        sorted_winrates = self.get_leaderboard(False)

        message = ""
        for player_name in list(sorted_winrates)[:10]:
            player_object = sorted_winrates[player_name]
            message = message + f"\n {player_name}: {player_object['winrate']}% ({player_object['games_won']}-{player_object['games_lost']})"

        self.slack_client.api_call("chat.postMessage",
                                   channel=channel,
                                   text=message,
                                   as_user=True)

    def print_whole_week(self, channel, date):
        all_weekly_matches = db.get_matches_for_week(date)
        players = db.get_players()

        message = ""
        for match in all_weekly_matches:
            message = message + f"\n {get_player_name(players, match.player_1_id)} vs. {get_player_name(players, match.player_2_id)} : week: {match.week}"

        self.slack_client.api_call("chat.postMessage",
                                   channel=channel,
                                   text=message,
                                   as_user=True)

    def print_user_week(self, user_id, channel, date):
        all_weekly_matches = db.get_matches_for_week(date)
        players = db.get_players()

        user_match_dict = dict()
        for match in all_weekly_matches:
            if match.player_1_id == user_id:
                user_match_dict[get_player_name(
                    players, match.player_2_id)] = match.week
            elif match.player_2_id == user_id:
                user_match_dict[get_player_name(
                    players, match.player_1_id)] = match.week

        message = ""
        for player, week in user_match_dict.items():
            message = message + f"\n Playing: {player} | week: {week}"

        self.slack_client.api_call("chat.postMessage",
                                   channel=channel,
                                   text=message,
                                   as_user=True)

    ### TODO Refactor so it can support Bo3 AND Bo5 across the database
    def print_user_stats(self, user_id, channel):
        all_matches = db.get_matches()

        total_won_matches = 0
        total_lost_matches = 0
        total_won_sets = 0
        total_lost_sets = 0
        for match in all_matches:
            if match.winner_id is None:
                continue

            elif user_id == match.winner_id:
                total_won_matches += 1
                total_won_sets += 2

                if match.sets == 3:
                    total_lost_sets += 1

            elif (match.player_1_id == user_id or match.player_2_id
                  == user_id) and user_id != match.winner_id:
                total_lost_matches += 1
                total_lost_sets += 2

                if match.sets == 3:
                    total_won_sets += 1

        message = f"\n Matches Won: {total_won_matches} | Matches Lost: {total_lost_matches} | Sets Won: {total_won_sets} | Sets Lost: {total_lost_sets}"
        self.slack_client.api_call("chat.postMessage",
                                   channel=channel,
                                   text=message,
                                   as_user=True)

    def print_group(self, channel, group):
        try:
            season = db.get_current_season()
            all_matches = db.get_matches_for_season(season)
            all_players = db.get_players()
            group_matches = [
                m for m in all_matches if m.grouping.lower() == group.lower()
            ]

            if not len(group_matches):
                raise Exception('Not a match')

            players = gather_scores(group_matches)
            message = 'Group ' + group.upper() + ':'

            for p in players:
                message += '\n' + get_player_name(
                    all_players, p['player_id']) + ' ' + str(
                        p['m_w']) + '-' + str(p['m_l'])
                message += ' (' + str(p['s_w']) + '-' + str(p['s_l']) + ')'

            self.slack_client.api_call("chat.postMessage",
                                       channel=channel,
                                       text=message,
                                       as_user=True)
        except Exception as e:
            self.logger.debug(e)
            self.slack_client.api_call("chat.postMessage",
                                       channel=channel,
                                       text="Not a group (or I messed up).",
                                       as_user=True)

    def parse_first_slack_id(self, message):
        return message[message.index('<@') + 2:message.index('>')].upper()

    def parse_second_slack_id(self, message):
        message = message[message.index('>') + 1:]
        return self.parse_first_slack_id(message)

    def parse_score(self, message):
        dash_index = message.index('-')
        score_substring = message[dash_index - 1:dash_index + 2]

        if score_substring != "3-0" and score_substring != "3-1" and score_substring != "3-2":
            raise Exception("Malformed score")

        score_1 = int(score_substring[0])
        score_2 = int(score_substring[2])

        return score_1, score_2

    def parse_message(self, command, poster):
        isAdmin = poster == bot_config.get_commissioner_slack_id()

        if command.startswith('me over '):
            winner = poster
            loser = self.parse_first_slack_id(command)
        elif command.startswith('<@') and command.index('over me') > 0:
            winner = self.parse_first_slack_id(command)
            loser = poster
        elif isAdmin and command.startswith('<@'):
            winner = self.parse_first_slack_id(command)
            loser = self.parse_second_slack_id(command)
        else:
            self.logger.debug('Bad message format')
            return None

        if winner == loser:
            self.logger.debug('Cant play against yourself')
            return None

        try:
            score_1, score_2 = self.parse_score(command)
        except Exception as e:
            self.logger.debug('Malformed score', e)
            return None

        return {
            'winner_id': winner,
            'loser_id': loser,
            'score_total': (score_1 + score_2)
        }

    def enter_score(self, winner_id, loser_id, score_total, channel,
                    timestamp):
        try:
            if not db.update_match_by_id(winner_id, loser_id, score_total):
                self.slack_client.api_call(
                    "chat.postMessage",
                    channel=channel,
                    text='Not a match I have (or I messed up).',
                    as_user=True)
                self.slack_client.api_call("reactions.add",
                                           name="x",
                                           channel=channel,
                                           timestamp=timestamp)
                return

            self.slack_client.api_call(
                "chat.postMessage",
                channel=bot_config.get_commissioner_slack_id(),
                text='Entered into db',
                as_user=True)
            self.slack_client.api_call("reactions.add",
                                       name="white_check_mark",
                                       channel=channel,
                                       timestamp=timestamp)

        except Exception as e:
            self.slack_client.api_call(
                "chat.postMessage",
                channel=bot_config.get_commissioner_slack_id(),
                text='Failed to enter into db',
                as_user=True)
            self.slack_client.api_call("reactions.add",
                                       name="x",
                                       channel=channel,
                                       timestamp=timestamp)

            self.logger.error(e)

    def filter_invalid_messages(self, message_list):
        valid_messages = []

        for message_object in message_list:
            if message_object is None:
                continue

            if 'text' not in message_object or 'channel' not in message_object or 'user' not in message_object or 'ts' not in message_object:
                continue

            if 'bot_id' in message_object:
                continue

            message_text = message_object['text']
            if message_object['channel'][:1] == 'D':
                if message_text.startswith('<@' +
                                           bot_config.get_bot_slack_user_id() +
                                           '>'):
                    message_text = message_text[message_text.index(">") +
                                                1:].strip()

                message_object['text'] = message_text
                valid_messages.append(message_object)
                continue

            if message_object['channel'] == bot_config.get_channel_slack_id(
            ) and message_text.startswith('<@' +
                                          bot_config.get_bot_slack_user_id() +
                                          '>'):
                message_text = message_text[message_text.index(">") +
                                            1:].strip()

                message_object['text'] = message_text
                valid_messages.append(message_object)
                continue

        return valid_messages

    def handle_message(self, message_object):
        command = message_object["text"]
        channel = message_object["channel"]
        user_id = message_object["user"]
        timestamp = float(message_object["ts"])
        user_date = datetime.fromtimestamp(timestamp).date()
        """"
        if command == 'leaderboard':
            self.print_leaderboard(channel)
        elif command == 'loserboard' or command == 'troy':
            self.print_loserboard(channel)
        elif command == 'my total stats' and channel[:1] == 'D':
            self.print_user_stats(user_id, channel)
        """
        if command == 'matches for week':
            self.print_whole_week(channel, user_date)
        elif command == 'who do i play' and channel[:1] == 'D':
            self.print_user_week(user_id, channel, user_date)
        elif command == 'help':
            self.print_help(channel)
        elif command.startswith('group'):
            self.print_group(channel, command[6])
        else:
            result = None
            try:
                result = self.parse_message(command, user_id)
            except Exception as e:
                self.logger.debug(e)

            if result is None:
                format_msg = "Didn't catch that. The format is `@sul me over @them 3-2` or `@sul @them over me 3-2`."
                self.slack_client.api_call("chat.postMessage",
                                           channel=channel,
                                           text=format_msg,
                                           as_user=True)
            elif result is not None and channel[:1] == 'D':
                format_msg = "Nice try, you have to put this in the main channel"
                self.slack_client.api_call('chat.postMessage',
                                           channel=channel,
                                           text=format_msg,
                                           as_user=True)
            elif result is not None and channel == bot_config.get_channel_slack_id(
            ):
                self.enter_score(result['winner_id'], result['loser_id'],
                                 result['score_total'], channel,
                                 message_object["ts"])

                player = db.get_player_by_id(result['winner_id'])
                self.print_group(channel, player.grouping)

        return None

    def start_bot(self):
        p = Process(target=self.keepalive)
        p.start()

        if self.slack_client.rtm_connect():
            print("StarterBot connected and running!")

            while True:
                try:
                    message_list = self.slack_client.rtm_read()
                    message_list = self.filter_invalid_messages(message_list)

                    for message in message_list:
                        try:
                            self.handle_message(message)
                        except Exception as e:
                            self.logger.debug(e)
                            self.slack_client.api_call(
                                "reactions.add",
                                name="x",
                                channel=message["channel"],
                                timestamp=message["ts"])

                    time.sleep(1)
                except Exception as e:
                    self.logger.debug('Main while loop web socket exception.',
                                      e)
                    self.slack_client.rtm_connect()
        else:
            print("Connection failed. Invalid Slack token or bot ID?")
Пример #53
0
def main():
    sc = SlackClient(SLACK_BOT_TOKEN)

    error_msg = json.dumps([{
        "color":
        "#e74c3c",
        "attachment_type":
        "default",
        "text":
        "",
        "image_url":
        "http://i2.kym-cdn.com/photos/images/original/000/329/784/bd6.jpg"
    }])

    # Connect to slack
    if sc.rtm_connect():
        while True:
            # Listen for any latest events
            for slack_event in sc.rtm_read():

                message = slack_event.get("text")
                user = slack_event.get("user")
                channel = slack_event.get("channel")

                if (message and user):
                    if (SLACK_BOT_NAME in message):

                        movieName = message[13:]
                        if (len(movieName.strip()) == 0):
                            sc.api_call("chat.postMessage",
                                        channel=channel,
                                        text="",
                                        attachments=error_msg,
                                        as_user=True)
                        else:
                            try:
                                url = "http://www.omdbapi.com/?t=" + message[
                                    13:]
                                response = requests.get(url)
                                if response.status_code == 200:
                                    data = response.json()
                                    print "Calling " + url
                                    intro_msg = json.dumps([{
                                        "fallback":
                                        "There seems to be some issue with displaying the data",
                                        "title":
                                        message[13:],
                                        "color":
                                        "#50e043",
                                        "attachment_type":
                                        "default",
                                        "text":
                                        data["Plot"],
                                        "fields": [{
                                            "title": "Title",
                                            "value": data["Title"],
                                            "short": True
                                        }, {
                                            "title": "Actors",
                                            "value": data["Actors"],
                                            "short": True
                                        }, {
                                            "title": "Released",
                                            "value": data["Released"],
                                            "short": True
                                        }, {
                                            "title": "Rated",
                                            "value": data["Rated"],
                                            "short": True
                                        }, {
                                            "title":
                                            "IMDB Ratings",
                                            "value":
                                            data["Ratings"][0]["Value"],
                                            "short":
                                            True
                                        }],
                                        "image_url":
                                        data["Poster"]
                                    }])
                                    sc.api_call(
                                        "chat.postMessage",
                                        channel=channel,
                                        text="Here is some information about "
                                        + message[13:],
                                        attachments=intro_msg,
                                        as_user=True)
                            except:
                                sc.rtm_send_message(
                                    channel, "Hey " + "<@" + user + "> !" +
                                    " I couldn't find this movie")

                    else:
                        sc.rtm_send_message(channel, "")
Пример #54
0
class TaskBot:

    def __init__(self, slack_token, db_url, task_manager: TaskManager):
        print("token: {}".format(slack_token))
        print("db url: {}".format(db_url))
        self.slack_client = SlackClient(slack_token)
        self.starterbot_id = None
        self.im_channel_id = None

        client = pymongo.MongoClient(db_url, 27017)
        db = client['test_db']
        self.task_collection = db.task_list
        self.done_collection = db.done_list

        self.task_manager = task_manager
        print("current_position: {}".format(self.task_manager.position))
        self.displayed_task = None

    def parse_bot_command(self, slack_events):
        """
            Parses a list of events coming from the Slack RTM API to find bot commands.
            If a bot command is found, this function returns a tuple of command and channel.
            If its not found, then this function returns None, None.
        """
        print(slack_events)
        for event in slack_events:
            if event["type"] == "message" and not "subtype" in event:
                if event["channel"] == self.im_channel_id:
                    return event["text"], event["channel"]
                user_id, message = self.parse_direct_mention(event["text"])
                if user_id == self.starterbot_id:
                    return message, event["channel"]
        return None, None

    def parse_direct_mention(self, message_text):
        """
            Finds a direct mention (a mention that is at the beginning) in message text
            and returns the user ID which was mentioned. If there is no direct mention, returns None
        """
        matches = re.search(MENTION_REGEX, message_text)
        # the first group contains the username, the second group contains the remaining message
        return (matches.group(1), matches.group(2).strip()) if matches else (None, None)

    def show_next(self):
        self.displayed_task = self.task_manager.next(datetime.datetime.now())
        return  self.displayed_task.description

    #def done_task(self, task_name):



    def handle_command(self, command: str, channel, condition=None):
        """
            Executes bot command if the command is known
        """
        # Default response is help text for the user
        default_response = "Not sure what you mean."

        # Finds and executes the given command, filling in response
        response = ""

        # Default timestamp
        timestamp = datetime.datetime.now()

        # This is where you start to implement more commands!
        # for handler in task_handlers:
        #     if command.startswith(handler.TASK_COMMAND):
        #         handler.handle_request(command, condition)
        if command.startswith("next"):
            response = "next task is {}.".format(self.show_next())

        elif command.startswith("done"):
            commands = command.split(' ')
            if len(commands) > 1:
                if is_int(commands[1]):
                    try:
                        target_task = self.task_manager.get_task_by_index(int(commands[1]))
                        response = "{} is done! Well done!\n".format(target_task.description)
                        self.task_manager.done_task(target_task, timestamp)
                        self.task_manager.update_task_list()
                        response += self.show_next()
                    except ValueError as e:
                        response = e.args[0]

                else:
                    try:
                        self.task_manager.done_task_by_name(commands[1], timestamp)
                        self.task_manager.update_task_list()
                        response = "{} is done! Well done!\n".format(commands[1])
                        response += self.show_next()
                    except ValueError as e:
                        response = e.args[0]
            else:
                self.task_manager.done_task_by_index(0, timestamp)
                self.task_manager.update_task_list()
                response = "{} is done! Well done!\n".format(self.displayed_task.description)
                response += self.show_next()

        elif command.startswith("postpone"):
            self.task_manager.postpone(self.displayed_task)
            response = "postponed {}.\n".format(self.displayed_task.description)
            response += self.show_next()

        elif command.startswith("adddaily"):
            commands = command.split(' ')
            if len(commands) > 1:
                if len(commands) > 2 and is_int(commands[2]):
                    frequency = int(commands[2])
                else:
                    frequency = 5
                gen = {'task_type': 'Unscheduled', 'last_done': datetime.datetime(2018, 9, 16, 12, 30, 0),
                       'frequency': frequency,
                       'task': {'priority': 4, 'due': datetime.datetime(2018, 1, 10, 10, 0, 0), 'time_needed': 15,
                                'description': commands[1],
                                'time_slot': 'any', 'cancellable': False, 'position': 'home'}}
                try:
                    self.task_manager.insert_generator(gen)
                    response = "A task {} is added!".format(commands[1])

                except:
                    response = "Failed to add task. Something wrong!"

        elif command.startswith("top"):
            commands = command.split(' ')
            length = 10
            if len(commands) > 1:
                try:
                    length = int(commands[1])
                except ValueError:
                    length = 10
            tasks = self.task_manager.top(length)
            response = "task list:\n"
            for index, task in enumerate(tasks):
                response += "{} {}: {}, {}\n".format(index, task.description, task.priority, task.due.date())

        elif command.startswith("task"):
            print(command)
            dummy, args = parse_command(['task'], command)
            print(args)
            self.task_manager.add_task(args[0])

        # Sends the response back to the channel
        self.slack_client.api_call(
            "chat.postMessage",
            channel=channel,
            text=response or default_response#,
            # attachments=BUTTON_JSON['attachments']
        )

    def handle_command_test(self, command_line: str, channel, condition=None):
        """
            Executes bot command if the command is known
        """
        default_response = "Not sure what you mean."

        # Finds and executes the given command, filling in response
        response = ""

        # Default timestamp
        timestamp = datetime.datetime.now()

        # This is where you start to implement more commands!
        # for handler in task_handlers:
        #     if command.startswith(handler.TASK_COMMAND):
        #         handler.handle_request(command, condition)
        command_set = {'next': self.show_next,
                       'done': self.done_task ,
                       }
        command, arg = parse_command(command_set.keys(), command_line)


        if command.startswith("next"):
            response = "next task is {}.".format(self.show_next())

        elif command.startswith("done"):
            commands = command.split(' ')
            if len(commands) > 1:
                if is_int(commands[1]):
                    try:
                        self.task_manager.done_task_by_index(int(commands[1]), timestamp)
                        self.task_manager.update_task_list()
                        response = "{} is done! Well done!\n".format(self.task_manager.get_task_by_index(int(commands[1])).description)
                        response += self.show_next()
                    except ValueError as e:
                        response = e.args[0]

                else:
                    try:
                        self.task_manager.done_task_by_name(commands[1], timestamp)
                        self.task_manager.update_task_list()
                        response = "{} is done! Well done!\n".format(commands[1])
                        response += self.show_next()
                    except ValueError as e:
                        response = e.args[0]
            else:
                self.task_manager.done_task_by_index(0, timestamp)
                self.task_manager.update_task_list()
                response = "{} is done! Well done!\n".format(self.displayed_task.description)
                response += self.show_next()

        elif command.startswith("postpone"):
            self.task_manager.postpone(self.displayed_task)
            response = "postponed {}.\n".format(self.displayed_task.description)
            response += self.show_next()

        elif command.startswith("adddaily"):
            commands = command.split(' ')
            if len(commands) > 1:
                if len(commands) > 2 and is_int(commands[2]):
                    frequency = int(commands[2])
                else:
                    frequency = 5
                gen = {'task_type': 'Unscheduled', 'last_done': datetime.datetime(2018, 9, 16, 12, 30, 0),
                       'frequency': frequency,
                       'task': {'priority': 4, 'due': datetime.datetime(2018, 1, 10, 10, 0, 0), 'time_needed': 15,
                                'description': commands[1],
                                'time_slot': 'any', 'cancellable': False, 'position': 'home'}}
                try:
                    self.task_manager.insert_generator(gen)
                    response = "A task {} is added!".format(commands[1])

                except:
                    response = "Failed to add task. Something wrong!"

        elif command.startswith("top"):
            commands = command.split(' ')
            length = 10
            if len(commands) > 1:
                try:
                    length = int(commands[1])
                except ValueError:
                    length = 10
            tasks = self.task_manager.top(length)
            response = "task list:\n"
            for index, task in enumerate(tasks):
                response += "{} {}: {}\n".format(index, task.description, task.priority)

        elif command.startswith("task"):
            print(command)
            dummy, args = parse_command(['task'], command)
            print(args)
            self.task_manager.add_task(args[0])


    def run(self):
        if self.slack_client.rtm_connect(with_team_state=False):
            print("Starter Bot connected and running!")
            self.starterbot_id = self.slack_client.api_call("auth.test")["user_id"]
            self.im_channel_id = self.slack_client.api_call("im.list")["ims"][1]["id"]
            while True:
                raw_command, channel = self.parse_bot_command(self.slack_client.rtm_read())
                if raw_command:
                    self.handle_command(raw_command, channel)
                # morning_tasks
                # if morning_obj.is_morning(datetime.datetime.now()):
                #     tasks = task_collection.find({"time_slot": "morning"})
                #     task_list = "Good morning! let's begin moving!\n"
                #     for task in tasks:
                #         task_list += u"{}\n".format(task["description"])
                #     # Sends the response back to the channel
                #     slack_client.api_call(
                #         "chat.postMessage",
                #         channel=im_channel_id,
                #         text=task_list,
                #         )

                time.sleep(RTM_READ_DELAY)

        else:
            print("Connection failed. Exception traceback printed above.")
Пример #55
0
class Robot(object):
    def __init__(self):
        self.client = SlackClient(SLACK_TOKEN)
        self.brain = RedisBrain()
        self.apps, self.docs = self.load_apps()

    def load_apps(self):
        docs = ['=' * 14, 'Usage', '=' * 14]
        apps = {}

        for name in APPS:
            app = import_module('apps.%s' % name)
            docs.append('{0}{1}: {2}'.format(CMD_PREFIX,
                                             ', '.join(app.run.commands),
                                             app.run.__doc__))
            for command in app.run.commands:
                apps[command] = app

        return apps, docs

    def handle_message(self, message):
        channel, user, text = message

        command, payloads = self.extract_command(text)
        if not command:
            return

        app = self.apps.get(command, None)
        if not app:
            return

        try:
            pool.apply_async(func=app.run,
                             args=(self, channel, user, payloads))
        except:
            traceback.print_exc()

    def extract_messages(self, events):
        messages = []
        for event in events:
            channel = event.get('channel', '')
            user = event.get('user', '')
            text = event.get('text', '')
            if channel and user and text:
                messages.append((channel, user, text))
        return messages

    def extract_command(self, text):
        if CMD_PREFIX and CMD_PREFIX != text[0]:
            return (None, None)

        tokens = text.split(' ', 1)
        if 1 < len(tokens):
            return tokens[0][CMD_LENGTH:], tokens[1]
        else:
            return (text[CMD_LENGTH:], '')

    def rtm_connect(self):
        conn = None
        try:
            conn = self.client.rtm_connect()
        except:
            logger.error(traceback.format_exc())
        else:
            return conn

    def read_message(self):
        events = None
        try:
            events = self.client.rtm_read()
        except:
            logger.error(traceback.format_exc())
            self.rtm_connect()
        return events

    def run(self):
        if not self.rtm_connect():
            raise RuntimeError(
                'Can not connect to slack client. Check your settings.')

        while True:
            events = self.read_message()
            if events:
                messages = self.extract_messages(events)
                for message in messages:
                    self.handle_message(message)
            gevent.sleep(0.3)
Пример #56
0
    log.info("Message:  %s" % args.message)
    log.info("Username: %s" % args.username)
    slack_client.api_call("chat.postMessage",
                          as_user=False,
                          username=args.username,
                          icon_emoji=":%s:" % args.emoji,
                          channel=args.convo,
                          text=args.message)
    sys.exit(" ... exiting")

if __name__ == "__main__":
    if slack_client.rtm_connect(with_team_state=False):
        try:
            handler = Handler(config)
            while True:
                handler.handle_events(slack_client.rtm_read())
                time.sleep(RTM_READ_DELAY)
        except (KeyboardInterrupt, SystemExit) as e:
            config.pop('commands')
            config.pop('plugins')
            config.dump(os.path.join(script_path, 'slack_settings.json'))
            # persistence.close_db()

            slack_client.api_call("chat.postMessage",
                                  as_user=True,
                                  channel=config.get('slackbot.botchannel.id'),
                                  text='Bye bye ... Reason: %s' % str(e))

            sys.exit(" ... exiting")

    else:
Пример #57
0
    def _fetch_slack_blessing(self, slack_token: Text, slack_channel_id: Text,
                              model_uri: Text) -> _SlackResponse:
        """Send message via Slack channel and wait for response.

    Args:
      slack_token: The user-defined function to obtain token to send and receive
        messages.
      slack_channel_id: The id of the Slack channel to send and receive
        messages.
      model_uri: The URI of the model waiting for human review.

    Returns:
      A _SlackResponse instance.

    Raises:
      ConnectionError:
        When connection to slack server cannot be established.

    """
        sc = SlackClient(slack_token)
        msg = _NOTIFY_MODEL_REVIEW_TEMPLATE.format(model_uri)
        ts = 0
        if not sc.rtm_connect():
            msg = 'Cannot connect to slack server with given token'
            absl.logging.error(msg)
            raise ConnectionError(msg)  # pylint: disable=undefined-variable

        sc.rtm_send_message(slack_channel_id, message=msg)

        while sc.server.connected:
            payload_list = sc.rtm_read()
            if not payload_list:
                continue

            for payload in payload_list:
                if payload.get('ok') and payload.get(
                        'reply_to') == 0 and not ts:
                    ts = payload['ts']
                    continue
                if not self._is_valid_message(payload, slack_channel_id, ts):
                    continue
                if payload.get('text').lower() in _APPROVE_TEXT:
                    absl.logging.info(
                        'User %s approves the model located at %s',
                        payload.get('user'), model_uri)
                    return _SlackResponse(True, payload.get('user'),
                                          payload.get('text'),
                                          slack_channel_id, str(ts))
                elif payload.get('text').lower() in _DECLINE_TEXT:
                    absl.logging.info(
                        'User %s declines the model located at %s',
                        payload.get('user'), model_uri)
                    return _SlackResponse(False, payload.get('user'),
                                          payload.get('text'),
                                          slack_channel_id, str(ts))
                else:
                    unrecognized_text = payload.get('text')
                    absl.logging.info('Unrecognized response: %s',
                                      unrecognized_text)
                    sc.rtm_send_message(
                        slack_channel_id,
                        message=_NOTIFY_CORRECT_REPLY_TEMPLATE.format(
                            unrecognized_text),
                        thread=ts)
Пример #58
0
    if response != False and response != None:
        logger_response = response.replace('\n', ' ')[:20]
        logger.info(f"Response: {logger_response}...")
        SLCKCLNT.api_call(
            "chat.postMessage",
            channel=event['channel'],
            text=response
            or "What was that? :: Try: " + ", ".join([x
                                                      for x in CMMDS.keys()]))

    if response == None:
        SLCKCLNT.api_call("chat.postMessage",
                          channel=event['channel'],
                          text="What was that? :: Try: " +
                          ", ".join([x for x in CMMDS.keys()]))


if __name__ == "__main__":
    if SLCKCLNT.rtm_connect(with_team_state=False):
        SLCKBTD = SLCKCLNT.api_call("auth.test")["user_id"]
        logger.info(f"Bot connected {SLCKBTD}")

        while True:
            command, channel = parse_incoming(SLCKCLNT.rtm_read())
            if command:
                handle_command(command, channel)
            time.sleep(RTM_READ_DELAY)
    else:
        logger.exception("Connection Failed")
Пример #59
0
        process = subprocess.Popen(cmd.split(),
                                   shell=True,
                                   stdout=subprocess.PIPE)
        for line in process.stdout:
            response = line.decode("ascii", "replace")

            # Sends the response back to the channel
            slack_client.api_call("chat.postMessage",
                                  channel=channel,
                                  text=response or default_response)
        return
    # Sends the response back to the channel
    slack_client.api_call("chat.postMessage",
                          channel=channel,
                          text=response or default_response)


if __name__ == "__main__":
    if slack_client.rtm_connect(with_team_state=False):
        print("Starter Bot connected and running!")
        # Read bot's user ID by calling Web API method `auth.test`
        starterbot_id = slack_client.api_call("auth.test")["user_id"]
        print(starterbot_id)
        while True:
            command, channel = parse_bot_commands(slack_client.rtm_read())
            if command:
                handle_command(command, channel)
            time.sleep(RTM_READ_DELAY)
    else:
        print("Connection failed. Exception traceback printed above.")
Пример #60
0
import os
import time
import api_functions
from slackclient import SlackClient

# dogeBot's ID as an environment variable
BOT_ID = os.environ.get("BOT_ID")

# constants
AT_BOT = "<@" + BOT_ID + ">"
EXAMPLE_COMMAND = "do"

# instantiate Slack & Twilio clients
slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))

if __name__ == "__main__":
    READ_WEBSOCKET_DELAY = 1  # 1 second delay between reading from firehose
    if slack_client.rtm_connect():
        print("dogeBot connected and running!")
        while True:
            command, channel = api_functions.parse_slack_output(
                slack_client.rtm_read(), AT_BOT)
            if command and channel:
                api_functions.handle_command(command, channel, EXAMPLE_COMMAND,
                                             slack_client)
            time.sleep(READ_WEBSOCKET_DELAY)
    else:
        print("Connection failed. Invalid Slack token or bot ID?")