Exemple #1
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()
Exemple #2
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)
Exemple #3
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))
Exemple #4
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))
def init():
    if len(sys.argv) != 3:
        print 'usage: %s <sysdig-token> <slack-token>' % sys.argv[0]
        sys.exit(1)
    else:
        sdc_token = sys.argv[1]
        slack_token = sys.argv[2]

    #
    # Instantiate the SDC client and Retrieve the SDC user information to make sure we have a valid connection
    #
    sdclient = SdcClient(sdc_token)

    #
    # Make a connection to the slack API
    #
    sc = SlackClient(slack_token)
    sc.rtm_connect()

    slack_id = json.loads(sc.api_call('auth.test'))['user_id']

    #
    # Start talking!
    #
    dude = SlackBuddy(sdclient, sc, slack_id)
    dude.run()
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))
Exemple #7
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))
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))
Exemple #9
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)
Exemple #10
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
Exemple #11
0
def send_message():    
    channel = request.form['channel']
    message = request.form['message']
    access_token = request.form['access_token']

    #Ideally, the only parameters taken would be origin user + message. This can be worked on in a further iteration.
    
    sc = SlackClient(access_token)

    sc.rtm_connect()
    sc.rtm_send_message(channel=channel, message=message)
    
    return "Success"
class Converser:
    topics = {}
    client = None
    debug = False
    my_user_name = ''

    def connect(self, token):
        self.client = SlackClient(token)
        self.client.rtm_connect()
        self.my_user_name = self.client.server.username
        print ("Connected to Slack!")

    def listen(self):
        while True:
            try:
                input = self.client.rtm_read()
                if input:
                    for action in input:
                        if self.debug:
                            print(action)
                        if 'type' in action and action['type'] == "message":
                            print ("user name: ", self.my_user_name)
                            print (action['text'].lower())
                            if 'text' in action and action['text'].lower().startswith(self.my_user_name):
                                print("I was called")
                                self.process_message(action)

                else:
                    sleep(1)

            except Exception as e:
                print ("Exception: ", e.message)


    def process_message(self, message):
        for topic in self.topics.keys():
            if topic.lower() in message['text'].lower():
                response = self.topics[topic].format(**message)
                if response.startswith("sys:"):
                    response = os.popen(response[4:]).read()
                print("Posting to [%s]: %s" % (message['channel'], response))
                self.post(message['channel'], response)

    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)
Exemple #13
0
def run(sc: SlackClient, channel: str, message: str, retries: int,
        logger: logging.Logger) -> None:
    if sc.rtm_connect():
        logger.info("Connected to Slack")

        channel_id = find_channel_id(channel, sc)
        logger.debug(f"Found channel ID {channel_id} for #{channel}")

        logger.info(f"Listening for joins in #{channel}")

        retry_count = 0
        backoff = 0.5

        while True:
            try:
                # Handle dem events!
                for event in sc.rtm_read():
                    handle_event(event, channel, channel_id, message, sc, logger)

                # Reset exponential backoff retry strategy every time we
                # successfully loop. Failure would have happened in rtm_read()
                retry_count = 0

                time.sleep(0.5)

            # This is necessary to handle an error caused by a bug in Slack's
            # Python client. For more information see
            # https://github.com/slackhq/python-slackclient/issues/127
            #
            # The TimeoutError could be more elegantly resolved by making a PR
            # to the websocket-client library and letting them coerce that
            # exception to a WebSocketTimeoutException.
            except (websocket.WebSocketConnectionClosedException, TimeoutError):
                logger.error("Lost connection to Slack, reconnecting...")
                if not sc.rtm_connect():
                    logger.info("Failed to reconnect to Slack")
                    if retry_count >= retries:
                        sys.exit(bail(
                            'fatal',
                            'red',
                            "Too many failed reconnect attempts, shutting down")
                        )
                    time.sleep((backoff ** 2) / 4)
                else:
                    logger.info("Reconnected to Slack")

                retry_count += 1

    else:
        sys.exit(bail('fatal', 'red', "Couldn't connect to Slack"))
Exemple #14
0
class RtmBot(object):
    def __init__(self, token):
        self.last_ping = 0
        self.token = token
        self.slack_client = None
        self.plugins = []
        for name in plugins.__all__:
            plugin_cls = getattr(plugins, name)
            self.plugins.append(plugin_cls())

    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()
        while True:
            for reply in self.slack_client.rtm_read():
                self.input(reply)
            self.output()
            self.auto_ping()
            time.sleep(.1)

    def auto_ping(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):
        print(data)
        for plugin in self.plugins:
            plugin.do(data)

    def output(self):
        for plugin in self.plugins:
            limiter = False
            for (channel_code, message) in plugin.outputs:
                channel = self.slack_client.server.channels.find(channel_code)
                if channel is not None and message is not None:
                    if limiter is True:
                        time.sleep(.1)
                        limiter = False
                    channel.send_message(message)
                    limiter = True
            plugin.outputs = []
Exemple #15
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')
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?"
Exemple #17
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)
Exemple #18
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')
Exemple #19
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)
Exemple #20
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."
Exemple #21
0
def create_slack_client(token):
    slack_client = SlackClient(token)
    if not slack_client.rtm_connect():
        raise Exception('Cannot connect to Slack')
    clients.slack = slack_client
    tokens.slack = token
    return slack_client
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)
Exemple #23
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']}})
Exemple #24
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:'
	)
Exemple #25
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()
Exemple #26
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?"
Exemple #27
0
class StandupBot:
    def __init__(self, token):
        self.token = token
        self.slack_client = None
        self.standup = None
        self.last_ping = 0
        self.keepalive_timer = 3
        print("Standup Bot initialized.")

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

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

    def start(self):
        self.connect()

        while True:
            for response in self.slack_client.rtm_read():
                self.response_handler(response)

            self.keepalive()
            time.sleep(.5)

    def message_handler(self, channel, message):
        if "!standup" in message:
            # check to see if self.Standup is already initialized, and if so, if the standup is already started.
            if self.standup is None:
                self.slack_client.rtm_send_message(channel=channel, message="Hello <!channel>, it's time for Standup!")
                self.standup = Standup(channel, self.slack_client)
            elif self.standup is not None & self.standup.status is "INITIALIZED":
                self.slack_client.rtm_send_message([channel, "I will restart the meeting."])
                self.standup = Standup(channel, self.slack_client)
        elif "!start" in message:
            if self.standup is not None:
                self.standup.start_standup()

    def response_handler(self, response):
        print(response)
        if "type" in response and "subtype" not in response:
            if "message" in response["type"]:
                self.message_handler(channel=response["channel"], message=response["text"])
Exemple #28
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!"
Exemple #29
0
class Bot:

    def __init__(self, token):
        self.token = token
        print("Bot instantiated. My token is: "+self.token+".")

    def connect(self):
        print("Connecting with token: "+self.token+"...")

        self.client = SlackClient(self.token)
        self.client.rtm_connect()
        is_ok = self.client.api_call("api.test")
        is_ok = is_ok["ok"]

        if is_ok == True:  
            print("Connection established!")

        else:
            print("Error code : 1. Connection failure. Check access token?")
            sys.exit()
            
    def print_chat(self, msg, channel):
        self.client.rtm_send_message(channel, msg)

    def read_msg(self):
        msg = self.client.rtm_read()
        if len(msg) > 0:
            msg = msg[0]
            if "type" in msg:
                if msg["type"] == "message":
                    return msg

            return []
        else:
            return []
        
    def hello(self):
        self.client.api_call("chat.postMessage", token=self.token, channel="#general", text="Hello World", as_user=True)

    def id2name(self, id):
        users = self.client.api_call("users.list", token=self.token)
        users = users["members"]
        for user in users:
            if user["id"] == id:
                username = user["name"]
                return "@"+username
Exemple #30
0
def init():
    sdc_token = None
    try:
        sdc_token = os.environ["SYSDIG_API_TOKEN"]
    except KeyError:
        pass
    slack_token = None
    try:
        slack_token = os.environ["SLACK_TOKEN"]
    except KeyError:
        pass
    parser = argparse.ArgumentParser(description='Sysdigbot: the Sysdig Cloud Slack bot.')
    parser.add_argument('--sysdig-api-token', dest='sdc_token', required=(sdc_token is None), default=sdc_token, type=str, help='Sysdig API Token, you can use also SYSDIG_API_TOKEN environment variable to set it')
    parser.add_argument('--slack-token', dest='slack_token', required=(slack_token is None), default=slack_token, type=str, help='Slack Token, you can use also SLACK_TOKEN environment variable to set it')
    parser.add_argument('--quiet', dest='quiet', action='store_true', help='Prevents the bot from printing output on channels, which is useful to avoid any kind of channel pollution')
    parser.add_argument('--no-auto-events', dest='auto_events', action='store_false', help='By default Sysdigbot converts every message in a channel in a Sysdig Cloud event, this flag disables it')
    parser.add_argument('--log-level', dest='log_level', type=LogLevelFromString, help='Logging level, available values: debug, info, warning, error')
    args = parser.parse_args()

    logging.basicConfig(format="%(asctime)s - %(levelname)s - %(message)s", level=args.log_level)
    # requests generates too noise on information level
    logging.getLogger("requests").setLevel(logging.WARNING)
    logging.getLogger("urllib3").setLevel(logging.WARNING)
    logging.debug("Starting Sysdigbot, config=%s", repr(args))

    #
    # Instantiate the SDC client and Retrieve the SDC user information to make sure we have a valid connection
    #
    sdclient = SdcClient(args.sdc_token)

    #
    # Make a connection to the slack API
    #
    sc = SlackClient(args.slack_token)
    sc.rtm_connect()

    sinfo = sc.api_call('auth.test')
    slack_id = sinfo['user_id']

    #
    # Start talking!
    #
    dude = SlackBuddy(sdclient, sc, slack_id, args.quiet)
    dude.auto_events = args.auto_events
    dude.run()
Exemple #31
0
class ChatBackend(object):
    """Interface for listening on slack RTM api and shoot events!"""

    def __init__(self):
        self.socket_connections = 0
        self.users = {}
        self.sc = SlackClient(SLACK_API_TOKEN)
        self.connected_to_rtm = False
        self.channel_info = {}

    def send_to_slack(self, msg):
        msg = json.loads(msg)
        post_data = {
            "channel": SLACK_CHANNEL,
             "text": msg["text"],
             "username": msg["username"],
             "icon_url" : self.gravatarUrl(msg["username"])
        }
        self.sc.server.api_call("chat.postMessage", **post_data)

    def send_to_console(self, msg):
        """Send given data to the registered client.
        Automatically discards invalid connections."""
        print "Received a message through slack API: %s" % msg
        if(msg.get("subtype","") == "bot_message"):
          msg.update({
              "real_name" : "bot",
              "profile": {
                    "image_32" : msg["icons"]["image_48"]
                }
          })
        elif("ok" in self.users and self.users["ok"] and "user" in msg):
            user = next((user for user in self.users["members"] if user['id'] == msg["user"]), None)
            msg.update(user) if user is not None else ""

        socketio.emit('message', msg, namespace='/receive')

    def run(self):
        """Listens for new messages in slack, and sends them to clients."""
        self.users = json.loads(self.sc.server.api_call('users.list'))
        self.update_member_count()
        if self.sc.rtm_connect():
            print "Connected to slack RTM"
            socketio.emit('rtm_connected', namespace='/receive')
            self.connected_to_rtm = True
            while True:
                for msg in self.sc.rtm_read():
                    if msg["type"] == "message" and msg["channel"] == SLACK_CHANNEL:
                        gevent.spawn(self.send_to_console , msg)
                time.sleep(1)
                gevent.sleep()

    def start(self):
        """Maintains a slack RTM subscription in the background."""
        gevent.spawn(self.run)

    def update_member_count(self):
        self.channel_info = json.loads(self.sc.server.api_call("channels.info", **{"channel": SLACK_CHANNEL}))

    @classmethod
    def gravatarUrl(self, username):
        default_gravatar = "http://fedsonslack.com/img/fos_icon.png"
        grav_url = "https://www.gravatar.com/avatar/%s?default=%s" % (hashlib.md5(username.lower()).hexdigest() , default_gravatar)
        return grav_url
Exemple #32
0
class RtmBot(FileSystemEventHandler):
    def __init__(self, config):
        self.last_ping = 0
        self.token = config['SLACK_TOKEN']
        self.bot_plugins = []
        self.plugin_dir = directory + '/plugins'
        self.slack_client = None
        self.reload = True
        self.pool = None
        self.pool_size = config['POOL_SIZE']
        vv('Started')

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

    def on_modified(self, event):
        if not self.pool:
            return
        self.pool.wait_completion()
        self.pool = None
        self.reload = True

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

    def start(self):
        self.connect()
        # DOC(sumitjami) This code is to executed only once in the lifetime.
        # when the bot starts executing.
        if not self.pool:
            self.pool = ThreadPool(self.pool_size)
        self.load_plugins()
        while True:
            try:
                for reply in self.slack_client.rtm_read():
                    self.input(reply)
                self.crons()
            except:
                self.connected = False
                self.connect()
                self.crons()
            else:
                self.autoping()
                self.output()
            time.sleep(.1)
            if self.reload:
                self.bot_plugins = []
                if not self.pool:
                    self.pool = ThreadPool(self.pool_size)
                vv('reloading')
                self.load_plugins()

    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 data.get('user'):
                data['user_object'] = self.slack_client.server.users.find(
                    data['user'])


#            vvvv('got {}'.format(function_name))
            for plugin in self.bot_plugins:
                #                plugin.register_jobs()
                #                vvvv('doing plugin %s' % plugin.name)
                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(self.plugin_dir +
                                '/*.py') + glob.glob(self.plugin_dir +
                                                     '/*/*.py'):
            name = plugin.split('/')[-1][:-3]
            plg = None
            try:
                plg = Plugin(self, name, plgnid=len(self.bot_plugins) + 1)
            except Exception, e:
                logger.exception('error loading plugin %s' % name)
                continue
            if not plg.disabled:
                #                vv("Plugin:"+plugin)
                self.bot_plugins.append(plg)
                plg.register_jobs()
                vv('Plugind Enabled %s' % name)
            else:
                vv('Plugind Disabled %s' % name)
        self.reload = False
Exemple #33
0
class SlackBot():
    """master slack client that remains alive for the duration of the script.  subsidiary connections to SlackClient are made on each connection drop or error"""
    def __init__(self, config):

        self.config = config
        self.token = self.config['api_key']
        self.slack_client = None
        self.name = self.config['bot_name']
        self.slack_user_id = None
        self.direct_message_channels = None
        self.channel_id = None
        self.channel_name = None
        self.master = self.config['master']

        self.plugins = self.config.get('plugins', 'plugins').split('\n')
        logger.info("Plugins installed: " + str(self.plugins))

        self.last_ping = 0
        self.reconnects = 0
        self.error_count = 0
        self.run_time = 0
        self.run_time_total = 0
        self.first_time = True
        self.auth_check = True
        self.errors = []
        self.ping_frequency = 15

    def test_connection(self, verbose=True):
        """tests whether the device is connected to the internet"""

        connected = False
        retries = 0
        while connected == False:
            if verbose:
                logger.info("Testing internet connection...")

            try:
                socket.create_connection(("www.google.com", 80))
                if verbose:
                    logger.info("internet working")
                connected = True
                return True

            except (socket.gaierror, socket.error):
                logger.error("Internet connection down - retrying " +
                             str(retries))
                error = utils.ConnectionDrop(self, "internet down")
                retries += 1
                time.sleep((1 + retries))

    def generate_client(self):
        """creates an instance of SlackClient for each connection"""

        if self.test_connection():
            self.reconnects += 1
            logger.info("Generating slack_client")

            # check token is valid

            self.slack_client = SlackClient(self.token)

            if self.auth_check:
                self.auth_check = False
                if self.slack_client.api_call(
                        "auth.test", token=self.token).get('ok') == False:
                    logger.error("key not recognised")
                    sys.exit("Invalid key.. exiting")

            logger.info("Token valid - SlackClient generated " +
                        str(self.slack_client))
            logger.info("Connecting to RTM...")

            #test RTM connection

            try:
                self.slack_client.rtm_connect()
                logger.info("Connected to RTM")
                self.run_time = 0

            except Exception as e:
                logger.error("Error in RTM connection: " + str(e))
                logger.warning("Exiting script...")
                sys.exit(1)

            logger.info("Getting user & channel IDs")

            #get list of users, channels and direct message channels

            channel_list = self.slack_client.api_call("channels.list")

            self.direct_message_channels = self.slack_client.api_call(
                "im.list")

            user_list = self.slack_client.api_call("users.list")

            for channel in channel_list.get('channels'):
                if channel.get('is_member'):
                    self.channel_id = str(channel.get('id'))
                    self.channel_name = str(channel.get('name'))

            for user in user_list.get('members'):
                if user.get('name') == self.name:
                    self.slack_user_id = user.get('id')

            logger.info("Bot ID:  " + str(self.slack_user_id) +
                        " Channel ID: " + str(self.channel_id) + "/ " +
                        str(self.channel_name))

    def say(self, text_message):
        """simple function to post a message to the bot's channel"""

        try:
            self.slack_client.api_call("chat.postMessage",
                                       channel=self.channel_id,
                                       text=str(text_message),
                                       as_user=True)

        except (websocket.WebSocketConnectionClosedException,
                socket.error) as e:

            error = utils.ConnectionDrop(self, "chat connection error")

    def autoping(self):
        """pings the slack server as set by the Bot"""

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

    def load_plugin(self, name):
        """loads the plugin for the process method"""

        plugin = __import__("plugin_%s" % name)
        return plugin

    def call_plugin(self, name, message):

        plugin = self.load_plugin(name)
        plugin.plugin_main(message, self)

    def process(self):
        """checks for connection errors, reads the RTM firehose and parses messages"""

        self.run_time += 1
        self.run_time_total += 1

        try:
            messages = self.slack_client.rtm_read()
            self.error_count = 0

            if self.first_time:
                self.say("Bot ID:" + str(self.slack_user_id) + " is awake")
                self.first_time = False

            if self.errors:
                drop_period = int(time.time()) - self.errors[0].timestamp
                self.say("I was offline for " + str(drop_period) + " secs.  " +
                         str(len(self.errors)) + "errors.")
                logger.debug("Offline for " + str(drop_period) + " secs")
                self.errors = []

        except websocket.WebSocketConnectionClosedException:

            error = utils.ConnectionDrop(self, "websocket drop")
            self.generate_client()
            return

        except socket.error:

            error = utils.ConnectionDrop(self, "Socket error")
            time.sleep(5)
            self.error_count += 1

            if self.error_count > 5:
                self.generate_client()
            return

        #checks the message stream

        for message in messages:
            #print (message)

            if message['type'] == 'presence_change':
                if message['presence'] == 'active':
                    time.sleep(.5)
                    self.say(
                        lex.response('greetings') + " " + str(self.master))

            if 'text' in message:
                if message['text'].startswith(
                        "<@%s>" % self.slack_user_id
                ) or 'text' in message and message['text'].startswith(
                        "<!%s>" % 'everyone'):

                    #if user issues a command, run through through all plugins

                    message_text = message['text']
                    for plugin in self.plugins:
                        self.call_plugin(plugin, message_text)

        self.autoping()
Exemple #34
0
class Bot(System):

    MENTION_REGEX = "^<@(|[WU].+?)>(.*)"
    RTM_READ_DELAY = 1
    OUT_CHANNEL = "bot"

    def __init__(self):
        super().__init__()

        self.db = DB()
        self.utils = Utils()

        channel_list = None
        self.info_channel_id = None

        self.p.subscribe("info")
        self.log.info('start listening redis ')

        self.slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))

        if self.slack_client.rtm_connect(with_team_state=False,
                                         auto_reconnect=True):
            anselm_id = self.slack_client.api_call("auth.test")["user_id"]
            self.log.info('start listening redis ')
            channel_list = self.slack_client.api_call("channels.list")

        if channel_list and 'channels' in channel_list:
            for channel in channel_list.get('channels'):
                if channel.get('name') == 'bot':
                    self.log.info("git info channel id")
                    self.info_channel_id = channel.get('id')
                    break

    def parse_bot_commands(self, slack_events):

        for event in slack_events:
            if event["type"] == "message" and not "subtype" in event:
                user_id, message = self.parse_direct_mention(event["text"])
                if user_id == self.anselm_id:
                    return message, event["channel"]
        return None, None

    def parse_direct_mention(self, message_text):

        matches = re.search(self.MENTION_REGEX, message_text)
        return (matches.group(1),
                matches.group(2).strip()) if matches else (None, None)

    def handle_command(self, command, channel):

        ok = False

        if command.startswith('to'):
            ok = True

            todo_pressures_acc = []
            todo_n_acc = []
            lines = self.get_lines('cal_id')
            for line in lines:
                cal_id = self.aget('cal_id', line)
                doc = self.db.get_doc(cal_id)
                todo_dict = self.utils.extract_todo_pressure(doc)
                todo_pressures_acc, todo_n_acc, todo_unit = self.utils.acc_pressure(
                    todo_dict, todo_pressures_acc, todo_n_acc)

            if len(todo_pressures_acc) > 0:
                self.post(
                    channel, "There are {no} todo pressures in total:".format(
                        no=len(todo_pressures_acc)))
                for i, p in enumerate(todo_pressures_acc):
                    self.post(
                        channel, "№ {no}:  {p} {unit} (N = {n})".format(
                            no=i + 1, p=p, unit=todo_unit, n=todo_n_acc[i]))
            else:
                msg = "No calibrations selelected so far."
                self.post(channel, msg)

        if command.startswith('re'):
            ok = True

            todo_pressures_acc = []
            todo_n_acc = []
            lines = self.get_lines('cal_id')
            for line in lines:
                cal_id = self.aget('cal_id', line)
                doc = self.db.get_doc(cal_id)
                todo_dict = self.utils.extract_todo_pressure(doc)
                todo_pressures_acc, todo_n_acc, unit = self.utils.acc_pressure(
                    todo_dict, todo_pressures_acc, todo_n_acc)

            remaining_pressure_acc = []
            remaining_n_acc = []
            for line in lines:
                cal_id = self.aget('cal_id', line)
                doc = self.db.get_doc(cal_id)
                target_dict = self.utils.extract_target_pressure(doc)

                remaining_pressure, remaining_n, unit = self.utils.remaining_pressure(
                    target_dict, todo_pressures_acc, todo_n_acc)
                remaining_dict = {
                    'Value': remaining_pressure,
                    'N': remaining_n,
                    'Unit': unit
                }
                remaining_pressure_acc, remaining_n_acc, unit = self.utils.acc_pressure(
                    remaining_dict, remaining_pressure_acc, remaining_n_acc)

            if len(remaining_pressure_acc) > 0:
                self.post(
                    channel,
                    "There following target pressure(s) remain:".format(
                        no=len(remaining_pressure_acc)))
                for i, p in enumerate(remaining_pressure_acc):
                    self.post(
                        channel, "№ {no}:  {p} {unit} (N = {n})".format(
                            no=i + 1, p=p, unit=unit, n=remaining_n_acc[i]))
            else:
                msg = "There are no remaining target pressures."
                self.post(channel, msg)

        if command.startswith('cu'):
            ok = True
            p = self.aget('current_target_pressure', 0)
            if p:
                msg = "The current target pressure is {}".format(p)
            else:
                msg = "The current target pressure is not set yet"
            self.post(channel, msg)

        if command.startswith('ga'):
            ok = True
            self.post(channel,
                      "calibration gas is {}".format(self.aget('gas', 0)))

        if command.startswith('ch'):
            ok = True
            self.post(
                channel,
                "I send my infos to channel #{}".format(self.OUT_CHANNEL))

        if command.startswith('id'):
            ok = True
            self.post(channel, "doc ids are:")
            lines = self.get_lines("cal_id")
            for line in lines:
                self.post(channel, self.aget("cal_id", line))

        if command.startswith('fu'):
            ok = True
            self.post(channel, "fullscale of the devices are:")
            lines = self.get_lines("fullscale_value")
            for line in lines:
                self.post(
                    channel,
                    "{} {} ".format(self.aget("fullscale_value", line),
                                    self.aget("fullscale_unit", line)))

        if command.startswith('he'):
            ok = True
            self.post(channel,
                      "Beside *he[lp]* further available commands are:")
            self.post(
                channel,
                "*to[do pressure]*, *re[maining pressures]*, *cu[rrent target pressure]*, *ch[annel]*, *ga[s]*, *fu[llscales]* or *id[s]*."
            )

        if not ok:
            self.post(channel, "Not sure what you mean. Try *help* command.")

    def post(self, channel, msg):

        self.slack_client.api_call("chat.postMessage",
                                   channel=channel,
                                   text=msg)

    def msg_in(self):
        self.log.debug("message in")

        self.anselm_id = self.slack_client.api_call("auth.test")["user_id"]
        while True:
            command, channel = self.parse_bot_commands(
                self.slack_client.rtm_read())
            if command:
                self.handle_command(command, channel)
            time.sleep(self.RTM_READ_DELAY)

    def msg_out(self):
        if self.info_channel_id:
            for item in self.p.listen():
                self.log.debug("received item: {}".format(item))
                if item['type'] == 'message':

                    self.slack_client.api_call("chat.postMessage",
                                               channel=self.info_channel_id,
                                               text=item.get('data'))
        else:
            self.log.error("got no info channel id")
Exemple #35
0
class NeoSlack(object):
    def __init__(self, nvim):
        logging.info("initializing class")
        self.nvim = nvim
        self.sc = SlackClient(os.environ["SLACK_TOKEN"])
        self.channels = self.sc.api_call("channels.list")["channels"]
        self.users = self.sc.api_call("users.list")["members"]
        self.channel_buffers = {}

    def get_buffer(self, buffer_name):
        ltf("get buffer")
        return [b for b in self.nvim.buffers if b.name == buffer_name][0]

    def get_channel_name(self, channel_id):
        logging.info("getting channel: {}".format(channel_id))
        return [ch["name"] for ch in self.channels
                if ch["id"] == channel_id][0]

    def get_channel_id(self, channel_name):
        ltf("getting channel: {}".format(channel_name))
        return [
            ch["id"] for ch in self.channels if ch["name"] == channel_name
        ][0]

    def get_user_name(self, user_id):
        logging.info("getting users")
        return [us["name"] for us in self.users if us["id"] == user_id][0]

    def create_channel_buffers(self):
        ltf("Creating buffers")
        for channel in self.channels:
            ltf("Creating buffer for {}".format(channel))
            channel = self.get_channel_name(channel["id"])
            buff_name = "/tmp/slack_{}".format(channel)
            self.nvim.command("new {}".format(buff_name))
            self.nvim.command("view")
            self.channel_buffers[channel] = self.get_buffer(buff_name)
            ltf("Created {} buffer.".format(channel))
        buff_name = "/tmp/slack_log"
        self.nvim.command("new {}".format(buff_name))
        self.channel_buffers["slack_log"] = self.get_buffer(buff_name)

    def write_event_to_buffer(self, event):
        ltf("writing event: {}".format(event))
        ts = datetime.fromtimestamp(float(event["ts"]))
        ts_out = datetime.strftime(ts, "%H:%M:%S")
        channel = self.get_channel_name(event["channel"])
        if "user" in event:
            username = self.get_user_name(event["user"])
        else:
            username = event["username"]
        msg = "{} [{}]({}): {}".format(ts_out, channel, username,
                                       event["text"])
        ltf("{}: writing {} to {}".format(datetime.now(), msg, channel))
        self.channel_buffers[channel].append(msg)
        ltf("done writing event")

    @neovim.command("SlackComment", range="", nargs="*", allow_nested=True)
    def comment(self, args, range):
        ltf("range: {} args: {}".format(range, args))
        channel_name = args[0]
        comment = " ".join(args[1:])
        self.sc.api_call("chat.postMessage",
                         channel="#{}".format(channel_name),
                         text=comment)

    def process_slack_stream(self, allow_nested=True):
        if self.sc.rtm_connect():
            while True:
                print("no op")
                for event in self.sc.rtm_read():
                    if event["type"] in ["message"]:
                        self.write_event_to_buffer(event)
                # for buff in self.channel_buffers:
                #     self.nvim.command(
                self.channel_buffers["slack_log"].append("0")
                sleep(0.25)

    @neovim.command("SlackStream")
    def slack_stream(self):
        ltf("{}: processing stream".format(datetime.now()))
        self.create_channel_buffers()
        self.process_slack_stream()
        p = multiprocessing.Process(target=self.process_slack_stream)
        # jobs.append(p)
        p.start()

    def get_summary(self):
        users = {u["id"]: u["real_name"] for u in self.users}
        return {
            channel["name"]: {
                "members": [users[m] for m in channel["members"]]
            }
            for channel in self.channels
        }

    @neovim.command("SlackSummary")
    def slack_summary(self):
        ltf("SUMMARY")
        buff_name = "/tmp/slack_channels"
        self.nvim.command("new {}".format(buff_name))
        self.nvim.command("view")
        buff = self.get_buffer(buff_name)
        for line in json.dumps(self.get_summary(), indent=2).split("\n"):
            buff.append(line)
Exemple #36
0
class SlackRTM(object):
    def __init__(self, sink_config, bot, loop, threaded=False):
        self.bot = bot
        self.loop = loop
        self.config = sink_config
        self.apikey = self.config['key']
        self.threadname = None
        self.lastimg = ''

        self.slack = SlackClient(self.apikey)
        if not self.slack.rtm_connect():
            raise ConnectionFailedError
        for key in ['self', 'team', 'users', 'channels', 'groups']:
            if key not in self.slack.server.login_data:
                raise IncompleteLoginError
        if threaded:
            if 'name' in self.config:
                self.name = self.config['name']
            else:
                self.name = '%s@%s' % (
                    self.slack.server.login_data['self']['name'],
                    self.slack.server.login_data['team']['domain'])
                logger.warning(
                    'no name set in config file, using computed name %s',
                    self.name)
            self.threadname = 'SlackRTM:' + self.name
            threading.current_thread().name = self.threadname
            logger.info('started RTM connection for SlackRTM thread %s',
                        pprint.pformat(threading.current_thread()))
            for t in threading.enumerate():
                if t.name == self.threadname and t != threading.current_thread(
                ):
                    logger.info('old thread found: %s - killing it',
                                pprint.pformat(t))
                    t.stop()

        self.update_userinfos(self.slack.server.login_data['users'])
        self.update_channelinfos(self.slack.server.login_data['channels'])
        self.update_groupinfos(self.slack.server.login_data['groups'])
        self.update_teaminfos(self.slack.server.login_data['team'])
        self.dminfos = {}
        self.my_uid = self.slack.server.login_data['self']['id']

        self.admins = []
        if 'admins' in self.config:
            for a in self.config['admins']:
                if a not in self.userinfos:
                    logger.warning(
                        'userid %s not found in user list, ignoring', a)
                else:
                    self.admins.append(a)
        if not len(self.admins):
            logger.warning('no admins specified in config file')

        self.hangoutids = {}
        self.hangoutnames = {}
        for c in self.bot.list_conversations():
            name = self.bot.conversations.get_name(c, truncate=True)
            self.hangoutids[name] = c.id_
            self.hangoutnames[c.id_] = name

        self.syncs = []
        syncs = _slackrtm_conversations_get(self.bot, self.name)
        if not syncs:
            syncs = []

        for s in syncs:
            sync = SlackRTMSync.fromDict(self.bot, s)
            if sync.slacktag == 'NOT_IN_CONFIG':
                sync.slacktag = self.get_teamname()
            sync.team_name = self.name  # chatbridge needs this for context
            self.syncs.append(sync)

        if 'synced_conversations' in self.config and len(
                self.config['synced_conversations']):
            logger.warning(
                'defining synced_conversations in config is deprecated')
            for conv in self.config['synced_conversations']:
                if len(conv) == 3:
                    hotag = conv[2]
                else:
                    if conv[1] not in self.hangoutnames:
                        logger.error(
                            "could not find conv %s in bot's conversations, but used in (deprecated) synced_conversations in config!",
                            conv[1])
                        hotag = conv[1]
                    else:
                        hotag = self.hangoutnames[conv[1]]
                _new_sync = SlackRTMSync(self.bot, conv[0], conv[1], hotag,
                                         self.get_teamname())
                _new_sync.team_name = self.name  # chatbridge needs this for context
                self.syncs.append(_new_sync)

    # As of https://github.com/slackhq/python-slackclient/commit/ac343caf6a3fd8f4b16a79246264a05a7d257760
    # SlackClient.api_call returns a pre-parsed json object (a dict).
    # Wrap this call in a compatibility duck-hunt.
    def api_call(self, *args, **kwargs):
        response = self.slack.api_call(*args, **kwargs)
        if isinstance(response, str):
            try:
                response = response.decode('utf-8')
            except:
                pass
            response = json.loads(response)

        return response

    def get_slackDM(self, userid):
        if not userid in self.dminfos:
            self.dminfos[userid] = self.api_call('im.open',
                                                 user=userid)['channel']
        return self.dminfos[userid]['id']

    def update_userinfos(self, users=None):
        if users is None:
            response = self.api_call('users.list')
            users = response['members']
        userinfos = {}
        for u in users:
            userinfos[u['id']] = u
        self.userinfos = userinfos

    def get_channel_users(self, channelid, default=None):
        channelinfo = None
        if channelid.startswith('C'):
            if not channelid in self.channelinfos:
                self.update_channelinfos()
            if not channelid in self.channelinfos:
                logger.error('get_channel_users: Failed to find channel %s' %
                             channelid)
                return None
            else:
                channelinfo = self.channelinfos[channelid]
        else:
            if not channelid in self.groupinfos:
                self.update_groupinfos()
            if not channelid in self.groupinfos:
                logger.error(
                    'get_channel_users: Failed to find private group %s' %
                    channelid)
                return None
            else:
                channelinfo = self.groupinfos[channelid]

        channelusers = channelinfo['members']
        users = {}
        for u in channelusers:
            username = self.get_username(u)
            realname = self.get_realname(u, "No real name")
            if username:
                users[username + " " + u] = realname

        return users

    def update_teaminfos(self, team=None):
        if team is None:
            response = self.api_call('team.info')
            team = response['team']
        self.team = team

    def get_teamname(self):
        # team info is static, no need to update
        return self.team['name']

    def get_slack_domain(self):
        # team info is static, no need to update
        return self.team['domain']

    def get_realname(self, user, default=None):
        if user not in self.userinfos:
            logger.debug('user not found, reloading users')
            self.update_userinfos()
            if user not in self.userinfos:
                logger.warning('could not find user "%s" although reloaded',
                               user)
                return default
        if not self.userinfos[user]['real_name']:
            return default
        return self.userinfos[user]['real_name']

    def get_username(self, user, default=None):
        if user not in self.userinfos:
            logger.debug('user not found, reloading users')
            self.update_userinfos()
            if user not in self.userinfos:
                logger.warning('could not find user "%s" although reloaded',
                               user)
                return default
        return self.userinfos[user]['name']

    def update_channelinfos(self, channels=None):
        if channels is None:
            response = self.api_call('channels.list')
            channels = response['channels']
        channelinfos = {}
        for c in channels:
            channelinfos[c['id']] = c
        self.channelinfos = channelinfos

    def get_channelgroupname(self, channel, default=None):
        if channel.startswith('C'):
            return self.get_channelname(channel, default)
        if channel.startswith('G'):
            return self.get_groupname(channel, default)
        if channel.startswith('D'):
            return 'DM'
        return default

    def get_channelname(self, channel, default=None):
        if channel not in self.channelinfos:
            logger.debug('channel not found, reloading channels')
            self.update_channelinfos()
            if channel not in self.channelinfos:
                logger.warning('could not find channel "%s" although reloaded',
                               channel)
                return default
        return self.channelinfos[channel]['name']

    def update_groupinfos(self, groups=None):
        if groups is None:
            response = self.api_call('groups.list')
            groups = response['groups']
        groupinfos = {}
        for c in groups:
            groupinfos[c['id']] = c
        self.groupinfos = groupinfos

    def get_groupname(self, group, default=None):
        if group not in self.groupinfos:
            logger.debug('group not found, reloading groups')
            self.update_groupinfos()
            if group not in self.groupinfos:
                logger.warning('could not find group "%s" although reloaded',
                               group)
                return default
        return self.groupinfos[group]['name']

    def get_syncs(self, channelid=None, hangoutid=None):
        syncs = []
        for sync in self.syncs:
            if channelid == sync.channelid:
                syncs.append(sync)
            elif hangoutid == sync.hangoutid:
                syncs.append(sync)
        return syncs

    def rtm_read(self):
        return self.slack.rtm_read()

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

    def matchReference(self, match):
        out = ""
        linktext = ""
        if match.group(5) == '|':
            linktext = match.group(6)
        if match.group(2) == '@':
            if linktext != "":
                out = linktext
            else:
                out = "@%s" % self.get_username(match.group(3),
                                                'unknown:%s' % match.group(3))
        elif match.group(2) == '#':
            if linktext != "":
                out = "#%s" % linktext
            else:
                out = "#%s" % self.get_channelgroupname(
                    match.group(3), 'unknown:%s' % match.group(3))
        else:
            linktarget = match.group(1)
            if linktext == "":
                linktext = linktarget
            out = '[{}]({})'.format(linktext, linktarget)
        out = out.replace('_', '%5F')
        out = out.replace('*', '%2A')
        out = out.replace('`', '%60')
        return out

    @asyncio.coroutine
    def upload_image(self, image_uri, sync, username, userid, channel_name):
        token = self.apikey
        logger.info('downloading %s', image_uri)
        filename = os.path.basename(image_uri)
        request = urllib.request.Request(image_uri)
        request.add_header("Authorization", "Bearer %s" % token)
        image_response = urllib.request.urlopen(request)
        content_type = image_response.info().get_content_type()

        filename_extension = mimetypes.guess_extension(
            content_type).lower()  # returns with "."
        physical_extension = "." + filename.rsplit(".", 1).pop().lower()

        if physical_extension == filename_extension:
            pass
        elif filename_extension == ".jpe" and physical_extension in [
                ".jpg", ".jpeg", ".jpe", ".jif", ".jfif"
        ]:
            # account for mimetypes idiosyncrancy to return jpe for valid jpeg
            pass
        else:
            logger.warning("unable to determine extension: {} {}".format(
                filename_extension, physical_extension))
            filename += filename_extension

        logger.info('uploading as %s', filename)
        image_id = yield from self.bot._client.upload_image(image_response,
                                                            filename=filename)

        logger.info('sending HO message, image_id: %s', image_id)
        yield from sync._bridgeinstance._send_to_internal_chat(
            sync.hangoutid,
            "shared media from slack", {
                "sync": sync,
                "source_user": username,
                "source_uid": userid,
                "source_title": channel_name
            },
            image_id=image_id)

    def config_syncto(self, channel, hangoutid, shortname):
        for sync in self.syncs:
            if sync.channelid == channel and sync.hangoutid == hangoutid:
                raise AlreadySyncingError

        sync = SlackRTMSync(self.bot, channel, hangoutid, shortname,
                            self.get_teamname())
        sync.team_name = self.name  # chatbridge needs this for context
        logger.info('adding sync: %s', sync.toDict())
        self.syncs.append(sync)
        syncs = _slackrtm_conversations_get(self.bot, self.name)
        if not syncs:
            syncs = []
        logger.info('storing sync: %s', sync.toDict())
        syncs.append(sync.toDict())
        _slackrtm_conversations_set(self.bot, self.name, syncs)
        return

    def config_disconnect(self, channel, hangoutid):
        sync = None
        for s in self.syncs:
            if s.channelid == channel and s.hangoutid == hangoutid:
                sync = s
                logger.info('removing running sync: %s', s)
                s._bridgeinstance.close()
                self.syncs.remove(s)
        if not sync:
            raise NotSyncingError

        syncs = _slackrtm_conversations_get(self.bot, self.name)
        if not syncs:
            syncs = []
        for s in syncs:
            if s['channelid'] == channel and s['hangoutid'] == hangoutid:
                logger.info('removing stored sync: %s', s)
                syncs.remove(s)
        _slackrtm_conversations_set(self.bot, self.name, syncs)
        return

    def config_setsyncjoinmsgs(self, channel, hangoutid, enable):
        sync = None
        for s in self.syncs:
            if s.channelid == channel and s.hangoutid == hangoutid:
                sync = s
        if not sync:
            raise NotSyncingError

        logger.info('setting sync_joins=%s for sync=%s', enable, sync.toDict())
        sync.sync_joins = enable

        syncs = _slackrtm_conversations_get(self.bot, self.name)
        if not syncs:
            syncs = []
        for s in syncs:
            if s['channelid'] == channel and s['hangoutid'] == hangoutid:
                syncs.remove(s)
        logger.info('storing new sync=%s with changed sync_joins', s)
        syncs.append(sync.toDict())
        _slackrtm_conversations_set(self.bot, self.name, syncs)
        return

    def config_sethotag(self, channel, hangoutid, hotag):
        sync = None
        for s in self.syncs:
            if s.channelid == channel and s.hangoutid == hangoutid:
                sync = s
        if not sync:
            raise NotSyncingError

        logger.info('setting hotag="%s" for sync=%s', hotag, sync.toDict())
        sync.hotag = hotag

        syncs = _slackrtm_conversations_get(self.bot, self.name)
        if not syncs:
            syncs = []
        for s in syncs:
            if s['channelid'] == channel and s['hangoutid'] == hangoutid:
                syncs.remove(s)
        logger.info('storing new sync=%s with changed hotag', s)
        syncs.append(sync.toDict())
        _slackrtm_conversations_set(self.bot, self.name, syncs)
        return

    def config_setimageupload(self, channel, hangoutid, upload):
        sync = None
        for s in self.syncs:
            if s.channelid == channel and s.hangoutid == hangoutid:
                sync = s
        if not sync:
            raise NotSyncingError

        logger.info('setting image_upload=%s for sync=%s', upload,
                    sync.toDict())
        sync.image_upload = upload

        syncs = _slackrtm_conversations_get(self.bot, self.name)
        if not syncs:
            syncs = []
        for s in syncs:
            if s['channelid'] == channel and s['hangoutid'] == hangoutid:
                syncs.remove(s)
        logger.info('storing new sync=%s with changed hotag', s)
        syncs.append(sync.toDict())
        _slackrtm_conversations_set(self.bot, self.name, syncs)
        return

    def config_setslacktag(self, channel, hangoutid, slacktag):
        sync = None
        for s in self.syncs:
            if s.channelid == channel and s.hangoutid == hangoutid:
                sync = s
        if not sync:
            raise NotSyncingError

        logger.info('setting slacktag="%s" for sync=%s', slacktag,
                    sync.toDict())
        sync.slacktag = slacktag

        syncs = _slackrtm_conversations_get(self.bot, self.name)
        if not syncs:
            syncs = []
        for s in syncs:
            if s['channelid'] == channel and s['hangoutid'] == hangoutid:
                syncs.remove(s)
        logger.info('storing new sync=%s with changed slacktag', s)
        syncs.append(sync.toDict())
        _slackrtm_conversations_set(self.bot, self.name, syncs)
        return

    def config_showslackrealnames(self, channel, hangoutid, realnames):
        sync = None
        for s in self.syncs:
            if s.channelid == channel and s.hangoutid == hangoutid:
                sync = s
        if not sync:
            raise NotSyncingError

        logger.info('setting showslackrealnames=%s for sync=%s', realnames,
                    sync.toDict())
        sync.showslackrealnames = realnames

        syncs = _slackrtm_conversations_get(self.bot, self.name)
        if not syncs:
            syncs = []
        for s in syncs:
            if s['channelid'] == channel and s['hangoutid'] == hangoutid:
                syncs.remove(s)
        logger.info('storing new sync=%s with changed hotag', s)
        syncs.append(sync.toDict())
        _slackrtm_conversations_set(self.bot, self.name, syncs)
        return

    def config_showhorealnames(self, channel, hangoutid, realnames):
        sync = None
        for s in self.syncs:
            if s.channelid == channel and s.hangoutid == hangoutid:
                sync = s
        if not sync:
            raise NotSyncingError

        logger.info('setting showhorealnames=%s for sync=%s', realnames,
                    sync.toDict())
        sync.showhorealnames = realnames

        syncs = _slackrtm_conversations_get(self.bot, self.name)
        if not syncs:
            syncs = []
        for s in syncs:
            if s['channelid'] == channel and s['hangoutid'] == hangoutid:
                syncs.remove(s)
        logger.info('storing new sync=%s with changed hotag', s)
        syncs.append(sync.toDict())
        _slackrtm_conversations_set(self.bot, self.name, syncs)
        return

    def handle_reply(self, reply):
        """handle incoming replies from slack"""

        try:
            msg = SlackMessage(self, reply)
        except ParseError as e:
            return
        except Exception as e:
            logger.exception('error parsing Slack reply: %s(%s)', type(e),
                             str(e))
            return

        # commands can be processed even from unsynced channels
        try:
            slackCommandHandler(self, msg)
        except Exception as e:
            logger.exception('error in handleCommands: %s(%s)', type(e),
                             str(e))

        syncs = self.get_syncs(channelid=msg.channel)
        if not syncs:
            # stop processing replies if no syncs are available (optimisation)
            return

        reffmt = re.compile(r'<((.)([^|>]*))((\|)([^>]*)|([^>]*))>')
        message = reffmt.sub(self.matchReference, msg.text)
        message = slack_markdown_to_hangups(message)

        for sync in syncs:
            if not sync.sync_joins and msg.is_joinleave:
                continue

            if msg.from_ho_id != sync.hangoutid:
                username = msg.realname4ho if sync.showslackrealnames else msg.username4ho
                channel_name = self.get_channelgroupname(msg.channel)

                if msg.file_attachment:
                    if sync.image_upload:

                        self.loop.call_soon_threadsafe(
                            asyncio. async,
                            self.upload_image(msg.file_attachment, sync,
                                              username, msg.user,
                                              channel_name))

                        self.lastimg = os.path.basename(msg.file_attachment)
                    else:
                        # we should not upload the images, so we have to send the url instead
                        response += msg.file_attachment

                self.loop.call_soon_threadsafe(
                    asyncio. async,
                    sync._bridgeinstance._send_to_internal_chat(
                        sync.hangoutid, message, {
                            "sync": sync,
                            "source_user": username,
                            "source_uid": msg.user,
                            "source_gid": sync.channelid,
                            "source_title": channel_name
                        }))

    @asyncio.coroutine
    def _send_deferred_media(self, image_link, sync, full_name, link_names,
                             photo_url, fragment):
        self.api_call('chat.postMessage',
                      channel=sync.channelid,
                      text="{} {}".format(image_link, fragment),
                      username=full_name,
                      link_names=True,
                      icon_url=photo_url)

    @asyncio.coroutine
    def handle_ho_message(self, event, conv_id, channel_id):
        user = event.passthru["original_request"]["user"]
        message = event.passthru["original_request"]["message"]

        if not message:
            message = ""

        message = hangups_markdown_to_slack(message)
        """slackrtm uses an overengineered pseudo SlackRTMSync "structure" to contain individual 1-1 syncs
            we rely on the chatbridge to iterate through multiple syncs, and ensure we only have
            to deal with a single mapping at this level

            XXX: the mapping SHOULD BE single, but let duplicates get through"""

        active_syncs = []
        for sync in self.get_syncs(hangoutid=conv_id):
            if sync.channelid != channel_id:
                continue
            if sync.hangoutid != conv_id:
                continue
            active_syncs.append(sync)

        for sync in active_syncs:
            bridge_user = sync._bridgeinstance._get_user_details(
                user, {"event": event})

            extras = []
            if sync.showhorealnames == "nick":
                display_name = bridge_user["nickname"] or bridge_user[
                    "full_name"]
            else:
                display_name = bridge_user["full_name"]
                if (sync.showhorealnames == "both" and bridge_user["nickname"]
                        and not bridge_user["full_name"]
                        == bridge_user["nickname"]):
                    extras.append(bridge_user["nickname"])

            if sync.hotag is True:
                if "chatbridge" in event.passthru and event.passthru[
                        "chatbridge"]["source_title"]:
                    chat_title = event.passthru["chatbridge"]["source_title"]
                    extras.append(chat_title)
            elif sync.hotag:
                extras.append(sync.hotag)

            if extras:
                display_name = "{} ({})".format(display_name,
                                                ", ".join(extras))

            slackrtm_fragment = "<ho://{}/{}| >".format(
                conv_id, bridge_user["chat_id"]
                or bridge_user["preferred_name"])
            """XXX: media sending:

            * if media link is already available, send it immediately
              * real events from google servers will have the medialink in event.conv_event.attachment
              * media link can also be added as part of the passthru
            * for events raised by other external chats, wait for the public link to become available
            """

            if "attachments" in event.passthru[
                    "original_request"] and event.passthru["original_request"][
                        "attachments"]:
                # automatically prioritise incoming events with attachments available
                media_link = event.passthru["original_request"]["attachments"][
                    0]
                logger.info(
                    "media link in original request: {}".format(media_link))

                message = "shared media: {}".format(media_link)

            elif isinstance(event, FakeEvent):
                if ("image_id" in event.passthru["original_request"]
                        and event.passthru["original_request"]["image_id"]):
                    # without media link, create a deferred post until a public media link becomes available
                    image_id = event.passthru["original_request"]["image_id"]
                    logger.info("wait for media link: {}".format(image_id))

                    loop = asyncio.get_event_loop()
                    task = loop.create_task(
                        self.bot._handlers.image_uri_from(
                            image_id, self._send_deferred_media, sync,
                            display_name, True, bridge_user["photo_url"],
                            slackrtm_fragment))

            elif (hasattr(event, "conv_event")
                  and hasattr(event.conv_event, "attachments")
                  and len(event.conv_event.attachments) == 1):
                # catch actual events with media link  but didn' go through the passthru
                media_link = event.conv_event.attachments[0]
                logger.info(
                    "media link in original event: {}".format(media_link))

                message = "shared media: {}".format(media_link)
            """standard message relay"""

            message = "{} {}".format(message, slackrtm_fragment)

            logger.info("message {}: {}".format(sync.channelid, message))
            self.api_call('chat.postMessage',
                          channel=sync.channelid,
                          text=message,
                          username=display_name,
                          link_names=True,
                          icon_url=bridge_user["photo_url"])

    def handle_ho_membership(self, event):
        # Generate list of added or removed users
        links = []
        for user_id in event.conv_event.participant_ids:
            user = event.conv.get_user(user_id)
            links.append(u'<https://plus.google.com/%s/about|%s>' %
                         (user.id_.chat_id, user.full_name))
        names = u', '.join(links)

        for sync in self.get_syncs(hangoutid=event.conv_id):
            if not sync.sync_joins:
                continue
            if sync.hotag:
                honame = sync.hotag
            else:
                honame = self.bot.conversations.get_name(event.conv)
            # JOIN
            if event.conv_event.type_ == hangups.MembershipChangeType.JOIN:
                invitee = u'<https://plus.google.com/%s/about|%s>' % (
                    event.user_id.chat_id, event.user.full_name)
                if invitee == names:
                    message = u'%s has joined %s' % (invitee, honame)
                else:
                    message = u'%s has added %s to %s' % (invitee, names,
                                                          honame)
            # LEAVE
            else:
                message = u'%s has left _%s_' % (names, honame)
            message = u'%s <ho://%s/%s| >' % (message, event.conv_id,
                                              event.user_id.chat_id)
            logger.debug("sending to channel/group %s: %s", sync.channelid,
                         message)
            self.api_call('chat.postMessage',
                          channel=sync.channelid,
                          text=message,
                          as_user=True,
                          link_names=True)

    def handle_ho_rename(self, event):
        name = self.bot.conversations.get_name(event.conv, truncate=False)

        for sync in self.get_syncs(hangoutid=event.conv_id):
            invitee = u'<https://plus.google.com/%s/about|%s>' % (
                event.user_id.chat_id, event.user.full_name)
            hotagaddendum = ''
            if sync.hotag:
                hotagaddendum = ' _%s_' % sync.hotag
            message = u'%s has renamed the Hangout%s to _%s_' % (
                invitee, hotagaddendum, name)
            message = u'%s <ho://%s/%s| >' % (message, event.conv_id,
                                              event.user_id.chat_id)
            logger.debug("sending to channel/group %s: %s", sync.channelid,
                         message)
            self.api_call('chat.postMessage',
                          channel=sync.channelid,
                          text=message,
                          as_user=True,
                          link_names=True)
Exemple #37
0
class slackThread(threading.Thread):
    def __init__(self, apikey):
        threading.Thread.__init__(self)
        self.APIKEY = apikey
        self.DATA = {}
        self.SC = SlackClient(self.APIKEY)
        self.CON = None
        self.lock = threading.Lock()
        self.messageId = 0
        self.handledEvents = {
            'message': self.__event__message,
        }
        self.ready = False

    def run(self):
        works = True
        self.CON = self.SC.rtm_connect()
        if self.CON == False:
            print('Failed starting a Slack RTM session.')
            works = False
        if not self.rebuildData():
            print('Failed accessing slack data.')
            works = False
        if works:
            print('Established Slack connection')
            self.ready = True
        else:
            print('Slack connection not established')
            return

        countForPing = 0
        while True:
            for event in self.SC.rtm_read():
                try:
                    self.handledEvents[event['type']](event)
                except:
                    #print(event)
                    pass
            countForPing += 0.1
            if countForPing > 3:
                self.SC.server.ping()
                countForPing = 0
            time.sleep(0.1)

    def rebuildData(self):
        self.lock.acquire()
        test = False
        try:
            test = json.loads((self.SC.api_call("api.test")).decode())
        except:
            return False
        if test.get('ok') == False:
            print('API Test failed. Full response:')
            print(test)
            self.lock.release()
            return False
        self.DATA['users'] = {}
        for user in json.loads(
            (self.SC.api_call("users.list")).decode()).get('members'):
            self.DATA['users'][user['id']] = {
                'name': user['name'],
            }
        self.DATA['channels'] = {}
        for channel in json.loads(
            (self.SC.api_call("channels.list")).decode()).get('channels'):
            self.DATA['channels'][channel['id']] = {
                'name': channel['name'],
            }
        self.lock.release()
        return True

    def __getMessageId(self):
        self.lock.acquire()
        mId = self.messageId
        self.messageId += 1
        self.lock.release()
        return mId

    def __getUserId(self, name):
        return self.__getId('users', name)

    def __getChannelId(self, name):
        return self.__getId('channels', name)

    def __getId(self, sub, name):
        if self.ready:
            for key in self.DATA[sub].keys():
                if self.DATA[sub][key]['name'] == name:
                    return key
            return None

    def __getPmChannelId(self, name):
        user = self.__getUserId(name)
        if user == None:
            return None
        channelId = self.DATA['users'][user].get('pm_channel_id')
        if channelId == None:
            data = json.loads((self.SC.api_call("im.open",
                                                user=user)).decode())
            channelId = data["channel"]["id"]
            self.DATA['users'][user]['pm_channel_id'] = channelId
        return channelId

    def sendMessageToUser(self, user, text):
        channelId = self.__getPmChannelId(user)
        if channelId == None:
            return
        self.__sendMessage(channelId, text)

    def sendMessageToChannel(self, channel, text):
        channelId = self.__getChannelId(channel)
        if channelId == None:
            return
        self.__sendMessage(channelId, text)

    def __sendMessage(self, target, text):
        self.lock.acquire()
        self.SC.api_call("chat.postMessage",
                         as_user="******",
                         channel=target,
                         text=text)
        self.lock.release()

    def __event__message(self, event):
        #print(self.DATA['users'][event['user']]['name'] + ": " + event['text'])
        pass
Exemple #38
0
class SlackSensor(PollingSensor):
    def __init__(self, sensor_service, config=None, poll_interval=None):
        super(SlackSensor, self).__init__(sensor_service=sensor_service,
                                          config=config,
                                          poll_interval=poll_interval)
        self._logger = self._sensor_service.get_logger(__name__)
        self._token = self._config['sensor']['token']
        self._handlers = {
            'message': self._handle_message,
        }

        self._user_info_cache = {}
        self._channel_info_cache = {}

    def setup(self):
        self._client = SlackClient(self._token)
        data = self._client.rtm_connect()

        if not data:
            msg = 'Failed to connect to the Slack API. Invalid token?'
            raise Exception(msg)

        self._populate_cache(user_data=self._api_call('users.list'),
                                channel_data=self._api_call('channels.list'))

    def poll(self):
        result = self._client.rtm_read()

        if result:
            self._handle_result(result=result)

    def cleanup(self):
        pass

    def add_trigger(self, trigger):
        pass

    def update_trigger(self, trigger):
        pass

    def remove_trigger(self, trigger):
        pass

    def _populate_cache(self, user_data, channel_data):
        """
        Populate users and channels cache from info which is returned on
        rtm.start
        """

        for user in user_data['members']:
            self._user_info_cache[user['id']] = user

        for channel in channel_data['channels']:
            self._channel_info_cache[channel['id']] = channel

    def _handle_result(self, result):
        for item in result:
            item_type = item['type']
            handler_func = self._handlers.get(item_type, lambda data: data)
            handler_func(data=item)

    def _handle_message(self, data):
        trigger = 'slack.message'
        event_type = data['type']

        if event_type not in EVENT_TYPE_WHITELIST or 'subtype' in data:
            # Skip unsupported event
            return

        # Note: We resolve user and channel information to provide more context
        user_info = self._get_user_info(user_id=data['user'])
        channel_info = self._get_channel_info(channel_id=data['channel'])

        payload = {
            'user': {
                'id': user_info['id'],
                'name': user_info['name'],
                'first_name': user_info['profile']['first_name'],
                'last_name': user_info['profile']['last_name'],
                'real_name': user_info['profile']['real_name'],
                'is_admin': user_info['is_admin'],
                'is_owner': user_info['is_owner']
            },
            'channel': {
                'id': channel_info['id'],
                'name': channel_info['name'],
                'topic': channel_info['topic']['value'],
            },
            'timestamp': int(float(data['ts'])),
            'text': data['text']
        }
        self._sensor_service.dispatch(trigger=trigger, payload=payload)

    def _get_user_info(self, user_id):
        if user_id not in self._user_info_cache:
            result = self._api_call('users.info', user=user_id)['user']
            self._user_info_cache[user_id] = result

        return self._user_info_cache[user_id]

    def _get_channel_info(self, channel_id):
        if channel_id not in self._channel_info_cache:
            result = self._api_call('channels.info', channel=channel_id)['channel']
            self._channel_info_cache[channel_id] = result

        return self._channel_info_cache[channel_id]

    def _api_call(self, method, **kwargs):
        result = self._client.api_call(method, **kwargs)
        result = json.loads(result)
        return result
Exemple #39
0
    def __init__(self, token):
        history_length = 20
        summary_frequency = 5
        summary_countdown = summary_frequency
        self.s = requests.Session()
        self.tea = TextEmotionAnalyzer(outputMode="json")
        self.token = token
        sc = SlackClient(token)
        self.history = bot_history(history_length)
        channel = "#dancewithdeanna"
        if sc.rtm_connect():
            while True:
                new_evts = sc.rtm_read()
                for evt in new_evts:
                    if len(evt) != 0:
                        if evt.has_key('text') and not evt.has_key('subtype'):
                            print evt['text']
                            top_emotion = self.analyse(evt['text'],
                                                       evt['user'],
                                                       evt['channel'])
                            if top_emotion is None:
                                print "top emotion is None"
                                continue
                            print top_emotion["docEmotions"]

                            if top_emotion["docEmotions"].has_key('anger'):
                                print sc.api_call(
                                    'chat.postMessage',
                                    channel=channel,
                                    text='Calm down!',
                                    username='******',
                                    icon_emoji=':woman::skin-tone-2:',
                                    as_user='******')
                            elif top_emotion["docEmotions"].has_key('fear'):
                                print sc.api_call(
                                    'chat.postMessage',
                                    channel=channel,
                                    text='Dont worry!',
                                    username='******',
                                    icon_emoji=':woman::skin-tone-2:',
                                    as_user='******')
                            elif top_emotion["docEmotions"].has_key('joy'):
                                print sc.api_call(
                                    'chat.postMessage',
                                    channel=channel,
                                    text='Yay!',
                                    username='******',
                                    icon_emoji=':woman::skin-tone-2:',
                                    as_user='******')
                            elif top_emotion["docEmotions"].has_key('sadness'):
                                print sc.api_call(
                                    'chat.postMessage',
                                    channel=channel,
                                    text='*hugs*!',
                                    username='******',
                                    icon_emoji=':woman::skin-tone-2:',
                                    as_user='******')
                            elif top_emotion["docEmotions"].has_key('disgust'):
                                print sc.api_call(
                                    'chat.postMessage',
                                    channel=channel,
                                    text='Gross!',
                                    username='******',
                                    icon_emoji=':woman::skin-tone-2:',
                                    as_user='******')

                            if (summary_countdown >= 0):
                                summary_countdown -= 1
                            else:
                                print_summary(self.history)
                    time.sleep(1)
        else:
            print "Connection Failed, invalid token?"
Exemple #40
0
class Slack:
    def __init__(self, tokenfile: str) -> None:
        try:
            with open(tokenfile) as f:
                token = f.readline().strip()
        except FileNotFoundError:
            exit("Unable to open the token file {}".format(tokenfile))
        self.client = SlackClient(token)
        self._usercache = {}  # type: Dict[str, User]
        self._usermapcache = {}  # type: Dict[str, User]

    def away(self, is_away: bool) -> None:
        """
        Forces the aways status or lets slack decide
        """
        status = 'away' if is_away else 'auto'
        r = self.client.api_call('users.setPresence', presence=status)
        response = load(r, Response)
        if not response.ok:
            raise ResponseException(response)

    @lru_cache()
    def get_members(self, id_: str) -> List[str]:
        r = self.client.api_call('conversations.members',
                                 channel=id_,
                                 limit=5000)
        response = load(r, Response)
        if response.ok:
            return load(r['members'], List[str])
        raise ResponseException(response)

    @lru_cache()
    def channels(self) -> List[Channel]:
        """
        Returns the list of slack channels
        """
        result = []  # type: List[Channel]
        r = self.client.api_call("channels.list",
                                 exclude_archived=True,
                                 exclude_members=True)
        response = load(r, Response)
        if response.ok:
            result.extend(load(r['channels'], List[Channel]))
        else:
            raise ResponseException(response)

        # Multiparty IM appear as both groups and mpim.
        # Fetch MPIM first, to exclude them in the list of groups.
        r = self.client.api_call("mpim.list",
                                 exclude_archived=True,
                                 exclude_members=True)
        response = load(r, Response)
        if response.ok:
            mpims = load(r['groups'], List[Channel])
            result.extend(mpims)
            mpim_ids = [mpim.id for mpim in mpims]
        else:
            raise ResponseException(response)

        r = self.client.api_call("groups.list",
                                 exclude_archived=True,
                                 exclude_members=True)
        response = load(r, Response)
        if response.ok:
            result.extend([
                group for group in load(r['groups'], List[Channel])
                if group.id not in mpim_ids
            ])
        else:
            raise ResponseException(response)
        return result

    @lru_cache()
    def get_channel(self, id_: str) -> Channel:
        """
        Returns a channel object from a slack channel id

        raises KeyError if it doesn't exist.
        """
        for c in self.channels():
            if c.id == id_:
                return c
        raise KeyError()

    @lru_cache()
    def get_channel_by_name(self, name: str) -> Channel:
        """
        Returns a channel object from a slack channel id

        raises KeyError if it doesn't exist.
        """
        for c in self.channels():
            if c.name == name:
                return c
        raise KeyError()

    @property
    def fileno(self) -> Optional[int]:
        return self.client.fileno

    def get_ims(self) -> List[IM]:
        """
        Returns a list of the IMs

        Some bullshit slack invented because 1 to 1 conversations
        need to have an ID to send to, you can't send directly to
        a user.
        """
        r = self.client.api_call("im.list", )
        response = load(r, Response)
        if response.ok:
            return load(r['ims'], List[IM])
        raise ResponseException(response)

    def get_user_by_name(self, name: str) -> User:
        return self._usermapcache[name]

    def get_usernames(self) -> List[str]:
        return list(self._usermapcache.keys())

    def prefetch_users(self) -> None:
        """
        Prefetch all team members for the slack team.
        """
        r = self.client.api_call("users.list")
        response = load(r, Response)
        if response.ok:
            for user in load(r['members'], List[User]):
                self._usercache[user.id] = user
                self._usermapcache[user.name] = user

    def get_user(self, id_: str) -> User:
        """
        Returns a user object from a slack user id

        raises KeyError if it does not exist
        """
        if id_ in self._usercache:
            return self._usercache[id_]

        r = self.client.api_call("users.info", user=id_)
        response = load(r, Response)
        if response.ok:
            u = load(r['user'], User)
            self._usercache[id_] = u
            self._usermapcache[u.name] = u
            return u
        else:
            raise KeyError(response)

    def send_message(self, channel_id: str, msg: str) -> None:
        """
        Send a message to a channel or group or whatever
        """
        r = self.client.api_call(
            "chat.postMessage",
            channel=channel_id,
            text=msg,
            as_user=True,
        )
        response = load(r, Response)
        if response.ok:
            return
        raise ResponseException(response)

    def send_message_to_user(self, user_id: str, msg: str):

        # Find the channel id
        found = False
        for i in self.get_ims():
            if i.user == user_id:
                channel_id = i.id
                found = True
                break

        # A conversation does not exist, create one
        if not found:
            r = self.client.api_call(
                "im.open",
                return_im=True,
                user=user_id,
            )
            response = load(r, Response)
            if not response.ok:
                raise ResponseException(response)
            channel_id = r['channel']['id']
        self.send_message(channel_id, msg)

    def events_iter(self) -> Iterator[Optional[SlackEvent]]:
        """
        This yields an event or None. Don't call it without sleeps
        """
        if self.client.rtm_connect():
            while True:
                try:
                    events = self.client.rtm_read()
                except:
                    if not self.client.rtm_connect():
                        sleep(10)
                    events = []

                for event in events:
                    t = event.get('type')
                    subt = event.get('subtype')

                    try:
                        if t == 'message' and not subt:
                            yield _loadwrapper(event, Message)
                        elif t == 'message' and subt == 'slackbot_response':
                            yield _loadwrapper(event, Message)
                        elif t == 'message' and subt == 'file_share':
                            yield _loadwrapper(event, MessageFileShare)
                        elif t == 'message' and subt == 'message_changed':
                            event['message']['channel'] = event['channel']
                            event['previous_message']['channel'] = event[
                                'channel']
                            yield MessageEdit(
                                previous=load(event['previous_message'],
                                              Message),
                                current=load(event['message'], Message))
                        elif t == 'message' and subt == 'message_deleted':
                            event['previous_message']['channel'] = event[
                                'channel']
                            yield _loadwrapper(event['previous_message'],
                                               MessageDelete)
                        elif t == 'message' and subt == 'bot_message':
                            yield _loadwrapper(event, MessageBot)
                        elif t == 'user_change':
                            # Changes in the user, drop it from cache
                            u = load(event['user'], User)
                            if u.id in self._usercache:
                                del self._usercache[u.id]
                                #FIXME don't know if it is wise, maybe it gets lost forever del self._usermapcache[u.name]
                            #TODO make an event for this
                        elif t == 'file_deleted':
                            yield _loadwrapper(event, FileDeleted)
                        elif t in USELESS_EVENTS:
                            continue
                        else:
                            print(event)
                    except Exception as e:
                        print('Exception: %s' % e)
                yield None
Exemple #41
0
class Bot(object):
    def __init__(self, key, codeword='iamgod'):
        self.slack_client = SlackClient(key)
        self.bot_name = "chocbot"
        self.bot_id = self.get_bot_id()
        print('My ID is:', self.bot_id)
        self.codeword = codeword

        self.scoreboard = Scoreboard()
        self.nominators = Scoreboard('Nominators')

        if self.bot_id is None:
            exit("Error, could not find " + self.bot_name)

        self.restore_state()

        self.event = Event(self)
        self.listen()

    def get_bot_id(self):
        api_call = self.slack_client.api_call("users.list")
        if api_call.get('ok'):
            # retrieve all users so we can find our bot
            users = api_call.get('members')
            for user in users:
                if 'name' in user and user.get('name') == self.bot_name:
                    return "<@" + user.get('id') + ">"

            return None

    def get_user_name(self, userid):
        api_call = self.slack_client.api_call("users.list")
        if api_call.get('ok'):
            # retrieve all users so we can find our bot
            users = api_call.get('members')
            for user in users:
                if 'name' in user and user.get('id') == userid:
                    return user.get('name')

        return None

    def listen(self):
        if self.slack_client.rtm_connect(with_team_state=False):
            print("Successfully connected, listening for commands")
            while True:
                self.event.wait_for_event()
                time.sleep(1)
        else:
            exit("Error, Connection Failed")

    def send_message(self, channel, message):
        #sends a message to the noted channel
        self.slack_client.api_call("chat.postMessage",
                                   channel=channel,
                                   text=message,
                                   as_user=True)

    def save_state(self, filename='bot_state.pkl'):
        #dumps the state out to an appropriate file
        try:
            state = {
                'scoreboard': self.scoreboard,
                'nominators': self.nominators
            }
            pickle.dump(state, open(filename, 'wb'))
        except:
            print('Count not save state. Is the location writeable?')

    def restore_state(self, filename='bot_state.pkl'):
        try:
            state = pickle.load(open(filename, 'rb'))
            self.scoreboard = state['scoreboard']
            self.nominators = state['nominators']
            print('Restored state from save file.')
        except:
            print('Save file not found')
Exemple #42
0
if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description='''
        This script posts a message into a slack channel.
        Sample commands:
        post.py mychannel "This is a message."
        ''',
        epilog='''''')
    parser.add_argument('channel',
                        type=str,
                        nargs=1,
                        help='Channel to post to')
    parser.add_argument('message', type=str, nargs=1, help='Message to post')
    args = parser.parse_args()
    print(args)

    config = configparser.RawConfigParser()
    config.read('creds.cfg.sample')

    token = config.get("Slack", "token")

    client = SlackClient(token)
    print(client.rtm_connect())

    try:
        post(client, args.channel[0], args.message[0])
    except Exception as e:
        sys.exit("Error: %s" % e.message)
Exemple #43
0
def get_connected_slack_client(secrets_env):
    slack_client = SlackClient(secrets_env.str('SLACK_TOKEN'))
    if slack_client.rtm_connect(with_team_state=False):
        return slack_client
    raise RuntimeError('SlackClient.rtm_connect failed')
Exemple #44
0
class Robotson():
    def __init__(self, token):
        self.token = token
        self.slack = SlackClient(token=self.token)
        self.cleverbot = Cleverbot()
        self.botname = settings.BOT_NAME
        self.facebook = Facebook()
        self.twitter = Twitter()
        self.network = SocialNetwork()

    def run(self, interval):
        if self.slack.rtm_connect():
            while True:
                full_message = self.slack.rtm_read()
                if full_message:
                    content = full_message[0]
                    if content.get("type") == 'message':
                        sender = self.username_as_str(content.get("user"))
                        channel = content.get("channel")
                        message = content.get("text")

                        try:
                            match = re.search(r'<@[A-Z0-9]+>', message)
                            bot_mention = match.group() if match else ""

                            if bot_mention:
                                # TODO: Remove hardcoding
                                if settings.BOT_UID in bot_mention:
                                    self.talk(channel, sender, message)
                            elif settings.SHARE_TRIGGER in message.lower():
                                self.share(message)
                        except:
                            pass
                time.sleep(interval)
        else:
            raise SlackNotConnected

    def share(self, message):
        try:
            match_share_trigger = re.search(
                r'%s[\:]?' % settings.SHARE_TRIGGER, message)
            message = message.replace(match_share_trigger.group(), "")

            match_lt_mt = re.search(r'[<]+(.*)[>]+', message)
            message = message.replace(match_lt_mt.group(0),
                                      match_lt_mt.group(1))

            message = message.strip()

            url = re.findall(
                r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+',
                message)
            if len(url) > 0:
                if not self.network.already_posted(url[0]):
                    self.facebook.post(message)
                    # self.twitter.post(message)
                    self.network.save_message(message)
        except Exception:
            pass

    def talk(self, channel, user, message):
        try:
            answer = '@%s: %s' % (user, self.cleverbot.ask(message))
            self.slack.rtm_send_message(channel=channel, message=answer)
        except Exception:
            pass

    def username_as_str(self, userid):
        try:
            response = json.loads(
                self.slack.api_call('users.info', user=userid))
            return response.get("user").get("name")
        except:
            return ""
Exemple #45
0
if __name__ == "__main__":

  parser = argparse.ArgumentParser(
    formatter_class=argparse.RawDescriptionHelpFormatter,
    description='''
This script posts a message into a slack channel.
Sample commands:
post.py mychannel "This is a message."
''',
    epilog='''''' )
  parser.add_argument('channel', type=str, nargs=1,
                   help='Channel to post to')
  parser.add_argument('message', type=str, nargs=1,
                   help='Message to post')
  args = parser.parse_args()

  config = ConfigParser.RawConfigParser()
  config.read('creds.cfg')
  
  token = config.get("Slack","token")

  client = SlackClient(token)
  client.rtm_connect()

  try:
    post(client, args.channel[0], args.message[0])
  except Exception as e:
    sys.exit("Error: %s" % e.message)

            raise SystemExit(BOT_NAME + " FAILED to locate ePO API credentials; please provide credentails and try again")
            
        url = ServerLocation + '/remote/core.help'
        query_result = requests.get(url, auth=HTTPBasicAuth(ePO_SERVER_usr,ePO_SERVER_pass = ""), verify=False)

        if query_result.text.find("<title> - Error report</title>") != -1:
            if DEBUG == 1:
                print("\n Startup - Credential Check == DEBUG query_result output: \n" + query_result.text)
            raise SystemExit("Connected to ePO API but failed to verify credentials; check credentials and restart")
    except:
        if DEBUG == 1:
            print("\n Startup - Credential Check == DEBUG result output: \n" + query_result.text)
        raise SystemExit(BOT_NAME + " FAILED to verify ePO API credentials; please check credentials & connection before trying again")
    
    #Main Bot Operation
    if slack_client.rtm_connect():  #Establish connection to slack.com
        print(BOT_NAME + " is running and connected to slack.com")
        
        while True: #Slack Operating Loop
            
            #Search for commands
            try:
                command, channel = parse_slack_output(slack_client.rtm_read())
            except:
                print("Connection to slack.com FAILED... " + BOT_NAME + " could not perform slack_client.rtm_read()!")
            #Process commands if found
            if command and channel:

                if DEBUG == 1:
                    print(command)
                    print(command[:MaxCommandLen])
Exemple #47
0
class RtmBot(object):
    def __init__(self, config):
        '''
            Params:
                - config (dict):
                    - SLACK_TOKEN: your authentication token from Slack
                    - BASE_PATH (optional: defaults to execution directory) RtmBot will
                        look in this directory for plugins.
                    - LOGFILE (optional: defaults to rtmbot.log) The filename for logs, will
                        be stored inside the BASE_PATH directory
                    - DEBUG (optional: defaults to False) with debug enabled, RtmBot will
                        break on errors
        '''
        # set the config object
        self.config = config

        # set slack token
        self.token = config.get('SLACK_TOKEN', None)
        if not self.token:
            raise ValueError("Please add a SLACK_TOKEN to your config file.")

        # get list of directories to search for loading plugins
        self.active_plugins = config.get('ACTIVE_PLUGINS', [])

        # set base directory for logs and plugin search
        working_directory = os.path.abspath(os.path.dirname(sys.argv[0]))

        self.directory = self.config.get('BASE_PATH', working_directory)
        if not self.directory.startswith('/'):
            path = os.path.join(os.getcwd(), self.directory)
            self.directory = os.path.abspath(path)

        self.debug = self.config.get('DEBUG', False)
        # establish logging
        log_file = config.get('LOGFILE', 'rtmbot.log')
        if self.debug:
            log_level = logging.DEBUG
        else:
            log_level = logging.INFO
        logging.basicConfig(filename=log_file,
                            level=log_level,
                            format='%(asctime)s %(message)s')
        logging.info('Initialized in: {}'.format(self.directory))

        # initialize stateful fields
        self.last_ping = 0
        self.bot_plugins = []
        self.slack_client = SlackClient(self.token)

    def _dbg(self, debug_string):
        if self.debug:
            logging.debug(debug_string)

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

    def _start(self):
        self.connect()
        self.load_plugins()
        for plugin in self.bot_plugins:
            try:
                self._dbg("Registering jobs for {}".format(plugin.name))
                plugin.register_jobs()
            except NotImplementedError:  # this plugin doesn't register jobs
                self._dbg("No jobs registered for {}".format(plugin.name))
            except Exception as error:
                self._dbg("Error registering jobs for {} - {}".format(
                    plugin.name, error)
                )
        while True:
            for reply in self.slack_client.rtm_read():
                self.input(reply)
            self.crons()
            self.output()
            self.autoping()
            time.sleep(.1)

    def start(self):
        if 'DAEMON' in self.config:
            if self.config.get('DAEMON'):
                import daemon
                with daemon.DaemonContext():
                    self._start()
        self._start()

    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"]
            self._dbg("got {}".format(function_name))
            for plugin in self.bot_plugins:
                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
                    channel.send_message(output[1])
                    limiter = True

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

    def load_plugins(self):
        ''' Given a set of plugin_path strings (directory names on the python path),
        load any classes with Plugin in the name from any files within those dirs.
        '''
        self._dbg("Loading plugins")
        if not self.active_plugins:
            self._dbg("No plugins specified in conf file")
            return  # nothing to load

        for plugin_path in self.active_plugins:
            self._dbg("Importing {}".format(plugin_path))

            if self.debug is True:
                # this makes the plugin fail with stack trace in debug mode
                cls = import_string(plugin_path)
            else:
                # otherwise we log the exception and carry on
                try:
                    cls = import_string(plugin_path)
                except ImportError as error:
                    logging.exception("Problem importing {} - {}".format(
                        plugin_path, error)
                    )

            plugin_config = self.config.get(cls.__name__, {})
            plugin = cls(slack_client=self.slack_client, plugin_config=plugin_config)  # instatiate!
            self.bot_plugins.append(plugin)
            self._dbg("Plugin registered: {}".format(plugin))
Exemple #48
0
class Robot(object):
    def __init__(self):
        self.client = SlackClient(SLACK_TOKEN_MGR)
        self.apps, self.docs = self.load_apps()

    def load_apps(self):
        docs = ['']
        apps = {}

        app = import_module('apps.system')
        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
            arguments = (self, channel, payloads, user, command)
            pool.apply_async(func=app.run, args=arguments)

    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):
        tokens = text.split(' ', 1)
        if 1 < len(tokens):
            return tokens[0], tokens[1]
        else:
            return (text, '')

    def run(self):
        if self.client.rtm_connect():
            debug_chn = '#bot'
            if os.path.isfile('booting_mgr'):
                file = open('booting_mgr', 'r')
                reboot_chn = file.readline()
                self.client.api_call('chat.postMessage',
                                     username=BOT_NAME + ' 매니저',
                                     as_user='******',
                                     icon_url=ICON_URL,
                                     channel=reboot_chn,
                                     text='재시작 완료')
                os.remove('booting_mgr')
            log_file = check_output(['ls', '-c', './log']).split('\n')[0]
            log_file = open('./log/' + log_file, 'r')
            is_debug = False
            while True:
                if os.path.isfile('DEBUG'):
                    log_file.close()
                    log_file = check_output(['ls', '-c',
                                             './log']).split('\n')[0]
                    log_file = open('./log/' + log_file, 'r')
                    dbg_file = open('DEBUG', 'r')
                    dbg_chn = dbg_file.readline()
                    dbg_file.close()
                    is_debug = True
                    os.remove('DEBUG')
                elif os.path.isfile('DEBUG_'):
                    os.remove('DEBUG_')
                    is_debug = False
                if is_debug:
                    txt = ''
                    txt = line = log_file.readline()
                    while line:
                        line = log_file.readline()
                        txt += line
                    if txt:
                        self.client.api_call('chat.postMessage',
                                             username=BOT_NAME + ' 매니저(Debug)',
                                             as_user='******',
                                             icon_url=ICON_URL,
                                             channel=dbg_chn,
                                             text=txt)
                events = self.client.rtm_read()
                if events:
                    messages = self.extract_messages(events)
                    self.handle_messages(messages)
                gevent.sleep(0.3)
Exemple #49
0
import spotipy.util as util
from slackclient import SlackClient

# get all required settings
slack_api_key = os.environ["KLAUS_SLACK_API_KEY"]
spotify_playlist_user = os.environ["KLAUS_SPOTIFY_PLAYLIST_USER"]
spotify_playlist_id = os.environ["KLAUS_SPOTIFY_PLAYLIST_ID"]
spotify_api_client_id = os.environ["KLAUS_SPOTIFY_API_CLIENT_ID"]
spotify_api_secret = os.environ["KLAUS_SPOTIFY_API_CLIENT_SECRET"]
spotify_api_callback_url = os.environ["KLAUS_SPOTIFY_API_CALLBACK_URL"]

sc = SlackClient(slack_api_key)
spre = re.compile(r"(?!spotify\:|http(?!s))\:\/\/[a-z]+\.spotify\.com\/(track|artist|album)(?:\:|\/)([a-zA-Z0-9]+)")

# check if we're connected
if sc.rtm_connect():
    while True:
        # get latest messages
        msg = sc.rtm_read()
        for d in msg:
            # make sure we have an actual message
            if d.get('text') and len(spre.findall(d.get('text'))):
                # extract some required information from the spotify link
                rc = spre.findall(d.get('text'))
                for f in rc:
                    sid = f[1]
                    # make sure we have a track link
                    if f[0] == 'track':
                        # check history if we added this track already
                        f = open('pltracks.txt', 'r+')
                        exists = False
Exemple #50
0
 def handle():
     team = Team.objects.first()
     client = SlackClient(team.bot_access_token)
     webhook_url = os.environ.get('SLACK_WEBHOOK_SECRET')
     # -------------------------------------------------------------------------
     slack_data = {
         "text":
         "new image",
         "attachments": [{
             "text":
             "Optional text that appears within the attachment",
             "image_url":
             "https://www.cleverfiles.com/howto/wp-content/uploads/2016/08/mini.jpg",
             # "thumb_url": "https://en.wikipedia.org/wiki/Linux#/media/File:Tux.svg"
         }]
     }
     mesg_one = {
         "text":
         "Would you like to play a game?",
         "attachments": [{
             "text":
             "Choose a game to play",
             "fallback":
             "You are unable to choose a game",
             "callback_id":
             "wopr_game",
             "color":
             "#3AA3E3",
             "attachment_type":
             "default",
             "actions": [{
                 "name": "game",
                 "text": "Chess",
                 "type": "button",
                 "value": "chess"
             }, {
                 "name": "game",
                 "text": "Falken's Maze",
                 "type": "button",
                 "value": "maze"
             }, {
                 "name": "game",
                 "text": "Thermonuclear War",
                 "style": "danger",
                 "type": "button",
                 "value": "war",
                 "confirm": {
                     "title": "Are you sure?",
                     "text": "Wouldn't you prefer a good game of chess?",
                     "ok_text": "Yes",
                     "dismiss_text": "No"
                 }
             }]
         }]
     }
     mesg_two = {
         "text":
         "New comic book alert!",
         "attachments": [{
             "title":
             "The Further Adventures of Slackbot",
             "fields": [{
                 "title": "Volume",
                 "value": "1",
                 "short": True
             }, {
                 "title": "Issue",
                 "value": "3",
                 "short": True
             }],
             "author_name":
             "Stanford S. Strickland",
             "author_icon":
             "http://a.slack-edge.com/7f18https://a.slack-edge.com/bfaba/img/api/homepage_custom_integrations-2x.png",
             "image_url":
             "http://i.imgur.com/OJkaVOI.jpg?1"
         }, {
             "title":
             "Synopsis",
             "text":
             "After @episod pushed exciting changes to a devious new branch back in Issue 1, Slackbot notifies @don about an unexpected deploy..."
         }, {
             "fallback":
             "Would you recommend it to customers?",
             "title":
             "Would you recommend it to customers?",
             "callback_id":
             "comic_1234_xyz",
             "color":
             "#3AA3E3",
             "attachment_type":
             "default",
             "actions": [{
                 "name": "recommend",
                 "text": "Recommend",
                 "type": "button",
                 "value": "recommend"
             }, {
                 "name": "no",
                 "text": "No",
                 "type": "button",
                 "value": "bad"
             }]
         }]
     }
     # ----------------------------------------------------------------------------
     if client.rtm_connect():
         while True:
             events = client.rtm_read()
             print("%s----%s" % (team, events))
             for event in events:
                 print(event)
                 if 'type' in event and event[
                         'type'] == 'message' and event['text'] == 'hi':
                     # client.rtm_send_message(event['channel'],"hello world")
                     client.api_call(
                         "chat.postMessage",
                         channel=event['channel'],
                         text="Hello from Python! :tada:",
                     )
                 elif 'type' in event and event[
                         'type'] == 'message' and event['text'] == 'image':
                     requests.post(
                         webhook_url,
                         data=json.dumps(slack_data),
                         headers={'Content-Type': 'application/json'})
                 elif 'type' in event and event[
                         'type'] == 'message' and event['text'] == 'button1':
                     requests.post(
                         webhook_url,
                         data=json.dumps(mesg_one),
                         headers={'Content-Type': 'application/json'})
                 elif 'type' in event and event[
                         'type'] == 'message' and event['text'] == 'button2':
                     requests.post(
                         webhook_url,
                         data=json.dumps(mesg_two),
                         headers={'Content-Type': 'application/json'})
             time.sleep(1)
Exemple #51
0
class NewsSlackBot(object):
    """
    News Slackbot uses extensible news clients to fetch news articles
    from different sources.

    * News topic is determined by a channel's topic or purpose.

    """
    def __init__(self, secrets_yaml='secrets.yml'):
        with open(secrets_yaml, 'rb') as secrets_file:
            self.config = config = yaml.load(secrets_file)
        self.bot = bot = {
            'token': config['slackbot']['token'],
            'id': config['slackbot'].get('id'),
            'name': config['slackbot'].get('name'),
        }

        # Instantiate Slack client.
        self.slack_client = SlackClient(bot['token'])

        # Set bot id.
        if not bot['id']:
            if not bot['name']:
                raise EnvironmentError

            users_list = self.get('users.list')
            if not users_list.get('ok'):
                raise EnvironmentError
            users = [u for u in users_list.get('members') if 'name' in u]
            bot['id'] = next(
                u.get('id') for u in users if u['name'] == bot['name'])
        bot['at'] = '<@{id}>'.format(id=bot['id'])

        self._clients = None

        self.meta_by_channel_id = defaultdict(
            lambda: defaultdict(lambda: None))
        self._channels = None, None

    def get(self, name, key=None):
        result = self.slack_client.api_call(name)
        return result if key is None else result[key]

    @staticmethod
    def update_purpose(meta, purpose):
        for line in purpose.splitlines():
            try:
                key, value = (t.strip() for t in line.split(':', 1))
            except ValueError:
                continue
            key = key.lower()

            if key == 'topic':
                meta[key].append(value)
            elif key == 'frequency':
                if value.isdigit():
                    meta[key] = int(value)
            elif key == 'limit':
                if value.isdigit():
                    meta[key] = int(value)
            elif key == 'language':
                if len(value) == 2 and value.isalpha():
                    meta[key] = value

    def parse_meta(self, channel):
        meta = self.meta_by_channel_id[channel['id']]
        meta.update(
            topic=[],
            frequency=60,
            limit=2,
            language='en',
        )

        topic = channel['topic']['value']
        if topic:
            meta['topic'].append(topic)
        self.update_purpose(meta, channel['purpose']['value'])

        return channel

    @property
    def channels(self):
        last_update, channels = self._channels

        now = datetime.now()
        if last_update is None or now - last_update > timedelta(minutes=10):
            channels = [
                self.parse_meta(c)
                for c in self.get('channels.list', 'channels')
                if c['is_member']
            ]
            self._channels = now, channels

        return channels

    def loop_forever(self):
        if self.slack_client.rtm_connect():
            LOGGER.info('StarterBot connected and running!')
            while True:
                command, channel_id = self.parse_rtm_events(
                    self.slack_client.rtm_read())
                if command and channel_id:
                    # Respond to messages.
                    self.handle_command(command, channel_id)
                else:
                    # Try to send news updates.
                    self.send_news()

                sleep(1)
        else:
            LOGGER.critical(
                'Connection failed. Invalid Slack token or bot ID?')

    def parse_rtm_events(self, events):
        """
        The Slack Real Time Messaging API is an events firehose.
        This parsing function returns any for any non-empty message.

        """
        at_bot = self.bot['at']
        for evt in events:
            LOGGER.debug(evt)
            channel_id, text = evt.get('channel'), evt.get('text')
            if 'purpose' in evt:
                self.update_purpose(self.meta_by_channel_id[channel_id],
                                    evt['purpose'])
            if evt.get('user') == self.bot[
                    'id'] or not text or evt['type'] != 'message':
                continue
            elif at_bot in text \
                    or channel_id not in map(itemgetter('id'), self.channels):
                # Handle command only if @mention or direct channel (IM).
                tokens = filter(None, (t.strip() for t in text.split(at_bot)))
                # return text, whitespace and @mention removed
                return ' '.join(t.lower() for t in tokens), channel_id
        return None, None

    def handle_command(self, cmd, channel):
        cmd = cmd.split()[0]
        response = 'I am unable to process commands at this time...'
        self.slack_client.api_call('chat.postMessage',
                                   channel=channel,
                                   text=response,
                                   as_user=True)

    @property
    def clients(self):
        """
        Yield all of the NewsClients existing in non-private modules of the
        given package.

        """
        if not self._clients:
            self._clients = []
            for _, name, is_pkg in iter_modules([CLIENTS_PKG]):
                if not is_pkg and not name.startswith('_'):
                    module = import_module('{pkg}.{client}'.format(
                        pkg=CLIENTS_PKG, client=name))
                    if hasattr(module, 'NewsClient'):
                        self._clients.append(module.NewsClient(self.config))
        return self._clients

    def send_news(self):
        now = datetime.now()
        for client in self.clients:
            for channel in self.channels:
                meta = self.meta_by_channel_id[channel['id']]
                last_update = meta['last_update']
                if last_update is None \
                        or last_update < now - timedelta(minutes=meta['frequency']):
                    meta['last_update'] = now
                else:
                    continue
                for article in client.fetch(topic=' OR '.join(meta['topic']),
                                            limit=meta['limit'],
                                            lang=meta['language']):
                    self.slack_client.api_call('chat.postMessage',
                                               channel=channel['id'],
                                               text=article,
                                               as_user=True)
                    sleep(1)
Exemple #52
0
        else:
            s1session.headers.update(
                {'Authorization': 'APIToken ' + s1CorrectAPICredentials})
            if s1APICredentialCheck():
                handle_command(command, channel, slackUserID)
            else:
                slackAPITokenPrompt("Your S1 API Token has expired")
    except Exception as e:
        slackS1SendMessage(channel, 'You broke me because of: \n' + str(e))
        time.sleep(5)


#TODO: MAKE THREADS TIME OUT

if __name__ == "__main__":
    while True:
        if slack_client.rtm_connect(with_team_state=False,
                                    auto_reconnect=True):
            print("S1Bot 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"]
            while True:
                command, channel, slackUserID = parse_bot_commands(
                    slack_client.rtm_read())
                if command:
                    thread.start_new_thread(handle_bot_command,
                                            (command, channel, slackUserID))
                time.sleep(RTM_READ_DELAY)
        else:
            print("Connection failed. Exception traceback printed above.")
Exemple #53
0
class Kairo:
    name = None
    slack_token = None
    slack_token_name = 'KAIRO_SLACK_TOKEN'
    slack_client = None
    users = None

    def __init__(self, name):
        self.name = name
        self.load_token_from_env(self.slack_token_name)

    def load_token_from_env(self, name):
        slack_token = os.environ.get(name)
        self.slack_token = slack_token

    def get_user_name_by_id(self, id):
        users = self.users
        return [user["profile"]["display_name"] for user in users.get("members", {}) if user["id"] == id][0]

    def get_sleep_time(self):
        return 1

    def running(self):
        return True

    def parse_slack_message(self, slack_incoming_message):
        output_list = slack_incoming_message
        if output_list and len(output_list) > 0:
            for output in output_list:
                if output and 'text' in output:
                    return output['text'].strip(), output['channel'], output['user']

        return None, None, None

    def parse_input(self, command):
        parts = command.split(' ')
        size = len(parts)
        command = parts[0] if size > 0 else None
        count = 1
        args = []
        while count < size:
            argument = parts[count].strip()
            if argument is not '':
                args.append(argument)
            count = count + 1
        return command, args

    def handle_command(self, text, channel, user):
        if text is not '':
            command, args = self.parse_input(text)
            key = self.get_key(command, args)
            if key in self.commands:
                action = self.commands[key]
                text = action(user, *args)

                if text is not None:
                    self.send_response(text, channel)

    def start_bot(self, token=None):
        slack_token = token if (token is not None) else self.slack_token
        if slack_token is None:
            raise RuntimeError('missing slack token')

        self.slack_client = SlackClient(slack_token)
        self.users = self.slack_client.api_call("users.list")
        bot_id = self.slack_client.api_call("auth.test")["user_id"]
        bot_name = self.get_user_name_by_id(bot_id)
        sleep_delay = self.get_sleep_time()

        if self.slack_client.rtm_connect():
            print(bot_name + " connected and running!")
            while self.running():
                text = self.slack_client.rtm_read()
                command, channel, user = self.parse_slack_message(text)
                if command and channel:
                    self.handle_command(command, channel, user)

                time.sleep(sleep_delay)
        else:
            print("Connection failed. Invalid Slack token or bot ID?")
        return True

    def send_response(self, text, channel):
        self.slack_client.api_call("chat.postMessage", channel=channel, text=text, as_user=True)

    def get_key(self, command, args):
        return command + str(len(args))

    commands = {}

    def parse_command(self, text, command_function):
        command, args = self.parse_input(text)
        key = self.get_key(command, args)
        self.commands[key] = command_function

    def command(self, text):
        def _command(command_function):
            self.parse_command(text, command_function)
            return command_function
        return _command
Exemple #54
0
#!/usr/bin/env python

import os
from slackclient import SlackClient
import time

# Globally important
cooldown = 28800  # 28800 seconds = 8 hours
last_message_from = {}
slack_token = os.environ["SLACK_TOKEN"]

autoresponder_message = "Your polite message goes here"

# Execution starts here
client = SlackClient(slack_token)
if client.rtm_connect(auto_reconnect=True):
    myself = client.api_call("auth.test")["user_id"]

    while client.server.connected is True:
        events = client.rtm_read()

        # Process events from rtm; event format is an array of events
        for event in events:
            event_type = event.get("type")

            # Handle messages
            if event_type == "message":
                msg_sender = event.get("user")
                msg_channel = event.get("channel", "N/A")

                # Ignore messages from ourselves
Exemple #55
0
class Slackbot:
    def __init__(self, token, client, id):

        self.TOKEN = token
        self.ID = id
        self.CLIENT = client
        self._inputqueue = Queue.Queue(50)
        self._outputqueue = Queue.Queue(50)

        self.users = {'USLACKBOT': 'slackbot'}
        self.channelids = []

    def start(self):

        self.CLIENT = SlackClient(self.TOKEN)
        print self.CLIENT
        print 'CONNECTING...'

        if self.CLIENT.rtm_connect():
            print 'CONNECTED.'

            channels = json.loads(self.CLIENT.api_call('channels.list',
                                                       {}))['channels']
            for chan in channels:
                self.channelids.append(chan['id'])

            print 'CHANNELS: %s' % self.channelids

            userlist = json.loads(self.CLIENT.api_call('users.list',
                                                       {}))['members']
            for user in userlist:
                self.users[str(user['id'])] = str(user['name'])
            print 'USERS: %s' % self.users

            inp = _inputThread(self.CLIENT, self._inputqueue)
            self.process = _processThread(self.CLIENT, self, self.channelids,
                                          self.users)
            out = _outputThread(self.CLIENT, self._outputqueue)
            inp.start()
            self.process.start()
            out.start()
        else:
            print "Connection Failed."

#	# functionality

    def quit(self):
        self.process.stop()
        self.onQuit()

    def sendMessage(self, channel, text):
        self._outputqueue.put({u'channel': channel, u'text': text})

    def setTopic(self, channel, topic):
        self.channels.setTopic({u'channel': channel, u'topic': topic})

#	# event handling done by subclass

    def onMessageReceived(self, channel, sender, message):

        # This function must be overridden by a class that inherits IRCbot.
        pass

    def onPrivateMessageReceived(self, channel, sender, message):

        # this function must be overriden by a class that inherits IRCbot.
        pass

    def onQuit(self):

        # this function should be overridden by the class that inherits this one
        pass
Exemple #56
0
import os
from slackclient import SlackClient

client = SlackClient('<slackbot token>')


def say_hello(data):
    try:
        textData = data['text']

        if "<" in textData and ">" in textData:
            channel_id = data['channel']
            thread_ts = data['ts']
            user = data['user']

            client.api_call('chat.postMessage',
                            channel=channel_id,
                            text=textData,
                            thread_ts=thread_ts)
    except:
        print("")


if client.rtm_connect():
    while client.server.connected is True:
        for data in client.rtm_read():
            if "type" in data and data["type"] == "message":
                say_hello(data)
else:
    print("Connection Failed")
Exemple #57
0
        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:
            at_bot = "<@%s>" % BOT_ID
            if output and 'text' in output and at_bot in output[
                    'text'] and 'channel' in output:
                # return text after the @ mention, whitespace removed
                return output['text'].split(at_bot)[1].strip(), \
                       output['channel']
    return None, None


if __name__ == "__main__":
    slack_client = SlackClient(SLACK_KEY)
    cape_client = CapeClient()

    if slack_client.rtm_connect():
        print("Connected")
    else:
        print("Failed to connect")
        sys.exit()

    while True:
        message, channel = parse_slack_output(slack_client.rtm_read())
        if message and channel:
            handle_question(message, channel, slack_client, cape_client)
        time.sleep(READ_WEBSOCKET_DELAY)
Exemple #58
0
                    text=author + " a supprimé son rappel quotidien: " + data[3]
                )
            else:
                result = make_response("Erreur de suppression du rappel", 402)

    return result


def start_app():

    # Start the flask app for root management
    app.run()

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"]

        ReminderManager.init_reminder_process(slack_client)
        PollManager.init_poll_process(slack_client)

        t = threading.Thread(target=start_app)
        t.daemon = True
        t.start()

        while True:
            command, channel, author = parse_bot_commands(slack_client.rtm_read())
Exemple #59
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}]"
                )
        return None

    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
Exemple #60
0
elif SlackApiKey == "":
    SlackApiKey = os.environ['SlackApiKey']
else:
    pass

if DiscordApiKey == "":
    logger.error("There wasn't an API key for Discord specified. Quitting...")
    sys.exit(1)
elif DiscordApiKey == "":
    DiscordApiKey = os.environ['DiscordApiKey']
else:
    pass

# Connect to Slack.
sc = SlackClient(SlackApiKey)
sc.rtm_connect()

# Set up URL's and headers for Discord.
headers = {
    "Authorization": "Bot {}".format(DiscordApiKey),
    "User-Agent":
    "DiscordSlackBridge (http://reddit.com/r/teendeveloper, v0.1)",
    "Content-Type": "application/json",
}

lastMessage = ''

# Run the Bridge

# We have to query all users in slack to map their id to their names later. Querying every message
# does not have sense, so we will just do it now and then flush the cache when someone will