def slack_notifier(slack_token, secret_conf_path, server, user, password, build_url): branches = run_command("git branch") branch_name_reg = re.search("\* (.*)", branches) branch_name = branch_name_reg.group(1) if branch_name == 'master': print_color("Starting Slack notifications about instances", LOG_COLORS.GREEN) attachments, integrations_counter = get_attachments(secret_conf_path, server, user, password, build_url) sc = SlackClient(slack_token) sc.api_call( "chat.postMessage", channel="devops-events", username="******", as_user="******", attachments=attachments, text="You have {0} instances configurations".format(integrations_counter) ) sc.api_call( "chat.postMessage", channel="content-lab-tests", username="******", as_user="******", attachments=attachments, text="You have {0} instances configurations".format(integrations_counter) )
class Output(cowrie.core.output.Output): def __init__(self): self.slack_channel = CONFIG.get('output_slack', 'channel') self.slack_token = CONFIG.get('output_slack', 'token') cowrie.core.output.Output.__init__(self) def start(self): pass def stop(self): pass def write(self, logentry): for i in list(logentry.keys()): # Remove twisted 15 legacy keys if i.startswith('log_'): del logentry[i] self.sc = SlackClient(self.slack_token) self.sc.api_call( "chat.postMessage", channel=self.slack_channel, text="%s %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), json.dumps(logentry, indent=4, sort_keys=True)) )
def main(): response = '' scope = ['https://www.googleapis.com/auth/analytics.readonly'] key_file_location = '_analytics-key.json' service = get_service('analytics', 'v3', scope, key_file_location) profile = get_first_profile_id(service) metrics = 'rt:activeUsers' dimensions = 'rt:referralPath,rt:source' sort = '-rt:activeUsers' detailed = get_detailed_totals(get_results(service, profile, metrics=metrics, dimensions=dimensions, sort=sort)) total_active = detailed[0] details = detailed[1] if int(total_active) >= 500 : try: # e.g. _slack-key.json # {"token": "<slack token>"} with open('_slack-key.json') as json_file: json_data = json.load(json_file) token = json_data['token'] sc = SlackClient(token) chan = 'google-analytics' message = '\n*Google Analytics Real-Time*\n Active Users:\t*' + total_active + '*\n' + details # View this post for GAE requests error # https://github.com/kennethreitz/requests/compare/master...agfor:master print sc.api_call('chat.postMessage', as_user='******', channel=chan, text=message) print ('************ DONE ***************') response = 'Posted message to Slack #google-analytics' except HTTPError, error: response = ('Arg, there was an API error : %s : %s' % (error.resp.status, error._get_reason()))
def check(self): sc = SlackClient(self.config["bot"]["token"]) history = sc.api_call("channels.history", channel=self.config["bot"]["channel"], oldest=self.lastcheck ) botname = "%s" % self.config["bot"]["name"] #sometimes there are no messages! if "messages" in history: for message in history["messages"]: if botname in message["text"]: timestamp = message["ts"] command = message["text"].split(" ") if command[1] == self.config["hostname"]: if command[2] == "df": self._action_df() self._set_lastcheck(timestamp) elif command[2] == "mem": self._action_mem() self._set_lastcheck(timestamp) elif command[2] == "top": self._action_top() self._set_lastcheck(timestamp) else: self._send_message("I don't know what this action is '%s'. Supported actions: df, mem, top" % command[2]) sc.api_call("chat.postMessage", as_user="******", channel=self.config["bot"]["channel"], text="I don't know what this action is '%s'. Supported actions: df, mem, top" % command[2]) self._set_lastcheck(timestamp) elif command[1] == "rollcall": self._send_message("%s on %s reporting in" % (self.config["bot"]["name"], self.config["hostname"]))
def process_message(data): token = "xoxo" # Your Slack Token sc = SlackClient(token) print sc.api_call("users.info",user=data['user']) print sc.api_call("channels.info",channel=data['channel']) Greetings = ['Hi', 'Hello', 'hi', 'hello', 'HI', 'HELLO'] marco = ['Marco'] polo = ['Polo'] # Say Hello for greeting in Greetings: if greeting in data['text']: sendstring = "Say Marco" outputs.append([data['channel'], sendstring]) # Say marco for m in marco: if m in data['text'] or m.lower() in data['text'] or m.upper() in data['text']: sendstring = "Polo!\nSay it again" outputs.append([data['channel'], sendstring]) # Say polo for p in polo: if p in data['text'] or p.lower() in data['text'] or p.upper() in data['text']: sendstring = "Marco!\nBut next time say Marco" outputs.append([data['channel'], sendstring]) break
def postNotification(token, channelID, service, calendar, timePeriod): events = [] message = "" sc = SlackClient(token) if timePeriod == "today": events = get_todays_events(token, channelID, service, calendar) elif timePeriod == "this week": events = get_weeks_events(token, channelID, service, calendar) elif timePeriod == "this month": events = get_months_events(token, channelID,service, calendar) if not events: period = "*_No events scheduled for " + timePeriod + " :sleepy: _*\n" for event in events: period = "*_Here are the events happening " + timePeriod + " :smile: _*\n" period = period.encode('utf-8') start_date = dateutil.parser.parse(event['start'].get('dateTime')) start_date = start_date.strftime("%A, %B %d %Y @ %I:%M %p") end_date = dateutil.parser.parse(event['end'].get('dateTime')) end_date = end_date.strftime("%A, %B %d %Y @ %I:%M %p") message += "\n - " + "*" + event['summary'] + "*" + "\n"+ start_date + " to " + end_date + "\n" + "*Where:* " + event['location'] + "\n" + "*Description:* " + event['description'] + "\n" + event['htmlLink'] + "\n" message = message.encode('utf-8') sc.api_call("chat.postMessage",username="******",channel=channelID,text=period + message)
def router(): _logger = get_logger(__name__) if request.form.get("token") == os.environ.get("SLACK_WEBHOOK_SECRET"): # Get info from incoming request channel_id = request.form.get("channel_id") user = request.form.get("user_name") message = request.form.get("text") _logger.info("Incoming message from {0} on {1}: {2}".format(channel_id, user, message)) # Parse and route try: response = parse_message(message, user) except Exception as e: response = fail(e, user) slack_client = SlackClient(os.environ.get("SLACK_TOKEN")) slack_client.api_call( "chat.postMessage", channel=channel_id, username='******', icon_emoji=':sausage:', **response ) return Response(), 200
def _register_deployment(): branch = local('git rev-parse --abbrev-ref HEAD', capture=True) author = local('git log -1 --pretty=format:"%an"', capture=True) commit = local('git log -1 --pretty=format:"%B"', capture=True) git_url = f'https://github.com/CDE-UNIBE/qcat/tree/{branch}' sc = SlackClient(settings.SLACK_TOKEN) sc.api_call( 'chat.postMessage', channel='server-info', username='******', text=f'Branch "{branch}" deployed: {git_url}', attachments=[ { 'pretext': f'Great success!', 'title': commit, 'title_link': git_url, 'fields': [ { 'title': 'Branch', 'value': 'develop', 'short': False }, { 'title': 'Author', 'value': author, 'short': False } ], 'image_url': 'https://qcat.wocat.net/static/assets/favicons/favicon-32x32.png' } ] )
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')
def citigroup_slack_bot(): bot_token = SECRETS_DICT['CITIGROUP_SLACKBOT_TOKEN'] sc = SlackClient(bot_token) log_message_window = datetime.timedelta(hours=12) oldest_window = datetime.datetime.now() - log_message_window oldest_window = time.mktime(oldest_window.timetuple()) cronbox_channel_id = 'C0KC1FWNA' cronbox_messages = sc.api_call('channels.history', channel=cronbox_channel_id, oldest=oldest_window) cronbox_messages_dict = json.loads(cronbox_messages) found_git_sync = False for message in cronbox_messages_dict['messages']: if 'synced crontab with git' in message['text']: found_git_sync = True warnings_channel_id = 'C0KCAG7AL' if not found_git_sync: sc.api_call('chat.postMessage', channel=warnings_channel_id, text='@channel: did not find message with "synced crontab with git" ' 'in channel #cronbox. ' 'This suggests that cronbox is not syncing its crontab with git. ', link_names=1) else: sc.api_call('chat.postMessage', channel=warnings_channel_id, text='k')
def fail_message(self): sc = SlackClient(self.bot_token) sc.api_call( "chat.postMessage", channel=self.channel_id, text="Sorry <@{}|{}> not sure if I can do that".format(self.reply_user_id, self.reply_user_name), username=self.username, as_user="******", icon_emoji=':instapaper:') return 200
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?"
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 ask_question(question): answer = question['answer'] hints = list() hint = ["`Подсказка: "] + ['.']*len(answer) + ['`'] hints.append("".join(hint)) offset = 1 rand_indexes = list(range(len(answer))) random.shuffle(rand_indexes) for i in rand_indexes: hint[offset+i] = answer[i] hints.append("".join(hint)) sc = SlackClient(BOT_TOKEN) # TODO: add post_to_channel function sc.api_call( "chat.postMessage", as_user="******", channel=CHANNEL, text="#" + str(question['id']) + ": " + question['text'] ) yield from asyncio.sleep(5) for i, hint in enumerate(hints): sc.api_call( "chat.postMessage", as_user="******", channel=CHANNEL, text=hint ) if i < len(hints) - 1: yield from asyncio.sleep(3.5)
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()
class Slack: def __init__(self, config, token_file): self.disabled = True try: from slackclient import SlackClient except: return try: self.channel = config['channel'] self.method = config['method'] self.username = config['username'] self.emoji = config['emoji'] except (TypeError, KeyError) as e: return try: with open(token_file) as stoken: r = stoken.readlines() slack_token = ''.join(r).strip() self.client = SlackClient(slack_token) except IOError: return self.disabled = False def api_call(self, text): if not self.disabled: self.client.api_call(self.method, channel=self.channel, username=self.username, icon_emoji=self.emoji, text=text) print ("Your current configuration for slack notifications is deprecated! Please switch to latest configuration.")
class SlackAPI(object): def __init__(self, token, username="******"): self.client = SlackClient(token) self._all_channels = self._get_channels() self.username = username def _get_channels(self): res = self.client.api_call("channels.list") _channels = json.loads(res.decode()) _parsed_channels = _channels.get("channels", None) if _parsed_channels is None: raise Exception("Could not get Slack channels. Are you sure your token is correct?") return _parsed_channels def get_channels(self, reload_channels=False): if not self._all_channels or reload_channels: self._all_channels = self._get_channels() return self._all_channels def channel_name_to_id(self, channel_name): for channel in self._all_channels: if channel["name"] == channel_name: return channel["id"] raise ChannelNotFoundError("Channel {} not in the list of available channels".format(channel_name)) def bulk_message(self, message, post_to=[]): for channel in post_to: if not channel.startswith("@"): channel = self.channel_name_to_id(channel) logging.debug("Posting message to {}".format(channel)) self.client.api_call("chat.postMessage", text=message, channel=channel, username=self.username) return True
def send_message(channel, message, username): slack_token = os.environ.get('SLACK_API_TOKEN') client = SlackClient(slack_token) client.api_call( 'chat.postMessage', channel=channel, text=message, username=username )
class BasicSlackClient: """ Basic slack client.git """ def __init__(self, token: str, default_channel: str, default_username: str): """ Constructor. :param token: authentication tocken from BasicSlackClient """ self._default_channel = default_channel self._default_username = default_username self._slack_client = SlackClient(token) def post(self, message: str, channel: str=None, username: str=None): """ Post the given message to the given channel as the given username. :param message: the message to post :param channel: the channel to post to :param username: the username to post as """ if channel is None: channel = self._default_channel if username is None: username = self._default_username self._slack_client.api_call(_SLACK_CLIENT_POST_MESSAGE, channel=channel, text=message, username=username)
def sendSlackAlert(token, channel, username, *args): sc = SlackClient(token) sc.api_call( "chat.postMessage", channel=str(channel), text=args[0] + "\n", username=str(username))
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:' )
class Utils: def __init__(self): self.sc = SlackClient(token) def send(self, chan, message): """Print to chat function using the slackclient api""" self.sc.api_call("chat.postMessage", channel = chan, text = "`" + message + "`", icon_emoji=':robot_face:') def whisper(self, message): return message def is_empty_input(param, self): """Check parameters to see if it is empty""" param = request.args.get("text") if param is None: self.help() return True return False def is_user_online(self, username): """Grats the user_ID (U123456789) via username""" data = self.sc.api_call("users.list", presence='1') try: for key in data['members']: if key['name'] == username: return key['presence'] except: pass return None
def post_message(self): sc = SlackClient(self.token) print sc.api_call("api.test") sc.api_call( "chat.postMessage", channel="#general", text="Hello from Python! :tada:", username=self.username, as_user="******", icon_emoji=':robot_face:' )
class SlackInterface(object): TOPIC_EXTRACT_EXPR = "^DC([0-9]+).*$" def __init__(self, config): self.sc = SlackClient(config["slack_token"]) self.bot_token = config["slackbot_token"] self.channel_name = config["channel_name"] self.channel_id = config["channel_id"] def get_challenge_number_from_topic(self): info = json.loads(self.sc.api_call("channels.info", channel=self.channel_id)) if "channel" in info and "topic" in info["channel"] and "value" in info["channel"]["topic"]: topic = info["channel"]["topic"]["value"] print "got", topic match = re.match(SlackInterface.TOPIC_EXTRACT_EXPR, topic) if match is None: return 0 else: return int(match.group(1)) else: return 0 def post_to_slack(self, poster_name, challenge_num, challenge): print self.sc.api_call( "channels.setTopic", channel=self.channel_id, topic="DC%d: %s" % (challenge_num, challenge) ) post_url = "https://rands-leadership.slack.com/services/hooks/slackbot?token=%s&channel=%%23%s" % ( self.bot_token, self.channel_name, ) post_data = " @%s says: Daily Challenge #%d: %s" % (poster_name, challenge_num, challenge) r = requests.post(post_url, data=post_data) print r.content
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!"
def success_message(self, num_articles): sc = SlackClient(self.bot_token) sc.api_call( "chat.postMessage", channel=self.channel_id, text="Hello <@{}|{}> just grabbed {} new articles for you :newspaper:!".format(self.reply_user_id, self.reply_user_name, num_articles), username=self.username, as_user="******", icon_emoji=':instapaper:' ) return 200
def slack_notify_message(message): bot_token = SECRETS_DICT['CITIGROUP_SLACKBOT_TOKEN'] sc = SlackClient(bot_token) general_channel_id = 'C0GEA5C1X' sc.api_call('chat.postMessage', channel=general_channel_id, text='@channel: {}'.format(message), link_names=1, as_user=True)
class SlackExfiltrator(): def __init__(self, slackSlaveID, slackToken, encKey): self.slackSlaveID = slackSlaveID self.slackToken = slackToken self.encKey = encKey self.encDriver = AESCipher(key=self.encKey) self.slackObj = None def _connect2Slack(self): self.slackObj = SlackClient(self.slackToken) if self.slackObj.api_call("api.test")['ok'] == True: sys.stdout.write("[+]\tConnected to Slack. API is valid!\n") return True else: sys.stderr.write("Unable to connect to slack. Maybe token is wrong?\n") sys.stderr.write("%s\n" % self.slackObj.api_call("api.test")['error']) sys.exit(1) def Listen(self): SC.api_call("chat.postMessage", as_user=True, channel=self.slackSlaveID, text="Bot is now online and will accept your calls.") if SC.rtm_connect(): while True: answer = SC.rtm_read() try: answer = answer[0] except IndexError: continue try: user = answer['user'] mtype = answer['type'] except KeyError: continue if answer['type'] == 'message' and answer['user'] == self.slackSlaveID: sys.stdout.write("[.]\tGot a message from SlackSlaveID!\n") try: data = base64.b64decode(answer['text']) except: sys.stderr.write("[-]\tMessage from SlaveID is not Base64!\n") continue try: decData = self.encDriver.decrypt(data) except: sys.stderr.write("[!]\tYou are not using the same key.\n") continue fname = str(random.randint(1111,9999)) + ".raw" f = open(fname, 'wb') f.write(decData) f.close() sys.stdout.write("[+]\tFile '%s' has been saved.\n" % fname) time.sleep(GLOBS.TIMEOUT)
def notify(message): config = _configuration() if config.notification_channel is NOT_SET: raise missingConfigurationException('notification-channel') if config.access_token is NOT_SET: raise missingConfigurationException('access-token') client = SlackClient(config.access_token) message = '%s %s' % (message, _SLACK_MESSAGE_SUFFIX) client.api_call('chat.postMessage', channel='#%s' % config.notification_channel, text=message, as_user=True, parse='full')
def send_telegram_message(message, destination): from slackclient import SlackClient token = "xoxp-60436121012-65421128289-66951783558-32760d4109" sc = SlackClient(token) sc.api_call("chat.postMessage", channel=destination, text=message, username="******" )
def rtm(token, queue, workspace, workspace_token): sc = SlackClient(workspace_token) print('rtm start for ', workspace) room_parking_channel = None @MSGRAPH.tokengetter def get_token(): """Called by flask_oauthlib.client to retrieve current access token.""" return (token, '') # create member dict members = list() slack_api_members = sc.api_call('users.list')['members'] for x in slack_api_members: name = x['profile']['display_name'] if x['profile'][ 'display_name'] else x['profile']['real_name'] query_res = Member.get_by_user_workspace(x['id'], workspace) if query_res: member = query_res else: member = Member(name, x['profile']['real_name'], x['id'], workspace) member.add() members.append(member) for x in members: if x.display_name == 'roomparking': room_parking_channel = x.user_id print(room_parking_channel) elif not x.channel_id: res = sc.api_call('conversations.open', users=x.user_id) if not res.get('error', None): x.channel_id = res['channel']['id'] x.update() channel_members = dict() for x in members: if x.channel_id: channel_members[x.channel_id] = x with APP.test_request_context(): if sc.rtm_connect(): while True: try: res = sc.rtm_read() # processing, etc except (WebSocketConnectionClosedException, TimeoutError) as e: print('Connecitons was closed. Restarting.') print(e.args) sc.rtm_connect() continue if not queue.empty(): server_token = queue.get() print('token', server_token) member = channel_members.get(server_token['channel'], False) if server_token['status'] == 'error' or ( not server_token.get('access_token', False)) or ( not server_token.get('refresh_token', False)): sc.rtm_send_message( server_token['channel'], 'Sorry there was a problem please try again.') if member and workspace == server_token['workspace']: member.token = server_token['access_token'] member.refresh_token = server_token['refresh_token'] member.expires = server_token['expires'] print('expires', member.expires, ' ', member.display_name) member.update() sc.rtm_send_message( member.channel_id, 'Successful authentication! \n {0}'.format( GREETING_TEXT)) print('authorized') else: print('not my token') queue.put(server_token) time.sleep(2) if len(res) > 0 and type(res[0].get('channel', None)) is not str: print('res', res) continue # if len(res) > 0: # print(res) if len(res) > 0 and res[0].get( 'channel', None) in channel_members.keys() and res[0].get( 'type', None) == 'message' and res[0].get( 'user', None) != room_parking_channel: member = channel_members[res[0].get('channel', None)] # sc.rtm_send_message(member.channel_id, '{0}, {1}, {2}, {3}'.format(member.expires, utc.localize(member.expires),(datetime.now(utc)+timedelta(hours=1)), eastern.localize(member.expires)<=(datetime.now(utc)+timedelta(hours=1)))) if not res[0].get('text', False) or res[0].get( 'subtype') and res[0]['subtype'] == 'bot_message': continue if res[0]['text'].lower() == 'delete token': member.token = None member.refresh_token = None member.expires = None member.update() if member.token and member.expires and utc.localize( member.expires) <= datetime.now(utc): print('checking for new token') if not check_token(member): sc.rtm_send_message( member.channel_id, 'Couldn\'t update token. Please login again.') else: print('updated token! for ', member.display_name) # sc.rtm_send_message(member.channel_id, 'updated token succesfully.') if not member.token: sc.rtm_send_message( member.channel_id, 'Hello, {name}! This is the Room Booking app for Bloomberg Center at CornellTech.\n' 'It can help you quickly book any room for the next hour. This app will create new meeting on your calendar and invite the selected room. \n' 'To continue please authorize with your Cornell Office 365 account: \n Click here: {ip}?channel={channel}&workspace={workspace} \n' 'Use your Cornell Email ([email protected]).'. format(name=member.first_name, ip=SERVER_IP, channel=member.channel_id, workspace=workspace)) else: token = member.token print('booking for ', member.display_name) member_state = None if member.state: member_state = json.loads(member.state) if member_state and member_state['state'] == 'cancel': try: words = res[0]['text'] if words == 'exit': member.state = None member.update() sc.rtm_send_message( member.channel_id, GREETING_TEXT) continue elif int(words) in range( 1, len(member_state['data']) + 1): event = member_state['data'][int(words) - 1] # sc.rtm_send_message(member.channel_id, event['id']) res = MSGRAPH.delete( 'me/events/' + event['id'], data=None, format='json', headers=request_headers()) if res.status == 204: sc.rtm_send_message( member.channel_id, 'Successfully deleted meeting.') member_state['data'].pop( int(words) - 1) response_res = list() if len(member_state['data']) > 0: for pos, x in enumerate( member_state['data']): response_res.append( '{4}. Meeting at {0}. Starting at {1} until {2} (timezone: {3})' .format( x['location'], x["start"], x["end"], x['timeZone'], pos + 1)) sc.rtm_send_message( member.channel_id, 'Here are the remaining meetings found for your account: \n {0} \n. Please respond with position of the meeting. Ex: ```1``` If only one meeting is presented, please still type: 1.\nTo exit cancellation submenu type ```exit```' .format( '\n'.join(response_res))) else: sc.rtm_send_message( member.channel_id, 'Returning back to the main menu.' ) member_state = None sc.rtm_send_message( member.channel_id, GREETING_TEXT) else: sc.rtm_send_message( member.channel_id, 'Error deleting meeting:') sc.rtm_send_message( member.channel_id, res.data) else: raise ValueError( 'Please put number from 1 to {0}.'. format(len(member_state['data']))) member.state = json.dumps( member_state) if member_state else None member.update() except Exception as e: print(e) sc.rtm_send_message(member.channel_id, e.args[0]) sc.rtm_send_message( member.channel_id, 'Wrong command. Try: ```exit``` or ```1```' ) else: try: ### autocomplete time time_start = datetime.now(eastern) if time_start.minute >= 20 and time_start.minute < 50: time_start = time_start.replace( microsecond=0, second=0, minute=30, tzinfo=None) elif time_start.minute >= 50: time_start = time_start.replace( microsecond=0, second=0, minute=0, hour=time_start.hour + 1, tzinfo=None) else: time_start = time_start.replace( microsecond=0, second=0, minute=0, tzinfo=None) time_end = time_start + timedelta(hours=1) ### over words = res[0]['text'].split() if words[0].lower() == 'help': sc.rtm_send_message( member.channel_id, GREETING_TEXT) elif words[0].lower() == 'where': if len(words) > 1 and words[1] in room_no: sc.rtm_send_message( member.channel_id, 'https://roomparking.cornelltech.io/rooms/{0}.jpg' .format(words[1])) else: sc.rtm_send_message( member.channel_id, 'Wrong input. Try ```where 375```') elif words[0].lower() == 'cancel': msft_resp = MSGRAPH.get( "me/events", headers=request_headers()).data # print(msft_resp) meetings_by_bot = list() if len(msft_resp.get('value', list())) > 0: for x in msft_resp['value']: end_date_time = eastern.localize( dateutil.parser.parse( x['end']['dateTime'])) if x['bodyPreview'] == "This event is created by RoomParking Slackbot, " \ "contact Eugene ([email protected]) for help." and end_date_time > datetime.now(eastern): meetings_by_bot.append( get_meeting_info(x)) sc.rtm_send_message( member.channel_id, get_meeting_info(x)) # print(x) if len(meetings_by_bot) > 0: dumped_state = json.dumps({ 'state': 'cancel', 'data': meetings_by_bot }) if len(dumped_state) > 3000: raise ValueError( 'Sorry there are too many meetings created that we can\'t handle at once please delete them manually.' ) member.state = dumped_state member.update() response_res = list() for pos, x in enumerate( meetings_by_bot): response_res.append( '{4}. Meeting at {0}. Starting at {1} until {2} (timezone: {3})' .format( x['location'], x["start"], x["end"], x['timeZone'], pos + 1)) sc.rtm_send_message( member.channel_id, 'Here are the meetings found for your account: \n {0} \nPlease respond with the position of the meeting. Ex: ```1``` If only one meeting present, please still type: 1. To exit the cancelling submenu type exit.' .format('\n'.join(response_res))) print(meetings_by_bot) sc.rtm_send_message( member.channel_id, meetings_by_bot) else: sc.rtm_send_message( member.channel_id, 'No meetings created by our bot has been found for your account. Book some now! ' ) elif words[0].lower() == 'list': if len(words[1]) > 1: floor = int(words[1][0]) else: floor = int(words[1]) if floor not in [0, 1, 2, 3, 4]: raise ValueError( 'Floor is wrong. Use: 0, 1, 2, 3, 4' ) sc.rtm_send_message( member.channel_id, 'Looking for available rooms...') rooms = get_available_by_floor( floor, MSGRAPH, time_start, time_end) if rooms['result'] == 'success': if rooms['data']: sc.rtm_send_message( member.channel_id, 'Available rooms on floor #{0}: \n {1}. \n (VC - Video Conferencing, D - Display)' .format( floor, ','.join(rooms['data'])) ) # \n\nCheck Michael Wilber\'s nice visualization of available rooms: https://cornell-tech-rooms.herokuapp.com else: sc.rtm_send_message( member.channel_id, 'There are no available rooms on floor #{0}' .format(floor)) else: if rooms['data']['error'][ 'message'] == 'Access token has expired.': member.token = None member.refresh_token = None member.update() sc.rtm_send_message( member.channel_id, 'Token is expired, please login again.' ) else: sc.rtm_send_message( member.channel_id, rooms['data']) continue elif words[0].lower() == 'book': if len(words) == 1: sc.rtm_send_message( member.channel_id, 'Please specify room number, like: "book 375".' ) continue if len(words) == 3: time_new = words[2] if 'pm' == time_new[-2:]: time_hour = int(time_new[:-2]) + 12 elif 'am' == time_new[-2:]: time_hour = int(time_new[:-2]) else: raise ValueError( 'If you want to specify time for booking. Follow this example: ```book 375 11am``` or ```book 375 2pm``` The room will be booked for one hour starting the time you posted.' ) # sc.rtm_send_message(member.channel_id, time_start.isoformat()) # sc.rtm_send_message(member.channel_id, time_end.isoformat()) time_start = time_start.replace( microsecond=0, second=0, minute=0, hour=time_hour, tzinfo=None) time_end = time_start + timedelta( hours=1) # sc.rtm_send_message(member.channel_id, time_start.isoformat()) # sc.rtm_send_message(member.channel_id, time_end.isoformat()) room = words[1] print(room) room_full = get_room_by_no(room) if not room_full: sc.rtm_send_message( member.channel_id, 'Room not found. Try "list {floor_id}" to get rooms.' ) continue if room == '367': sc.rtm_send_message( member.channel_id, 'Somehow room 367 cannot be booked. It is not in the Outlook Calendar. I will raise a ticket to IT about it. \n Eugene' ) continue available = is_available_now( room_full, time_start, time_end) if available['result'] == 'success': if not available['data']: sc.rtm_send_message( member.channel_id, 'Room is already occupied!') sc.rtm_send_message( member.channel_id, available) continue else: book_json = create_booking_json( user=member.first_name, room=room_full, time_start=time_start. isoformat(), time_end=time_end.isoformat()) msft_resp = MSGRAPH.post( "me/calendar/events", data=book_json, format='json', headers=request_headers()).data if msft_resp.get('error'): raise SystemError(msft_resp) print(msft_resp) sc.rtm_send_message( member.channel_id, 'The room {4} is booked from {0}:{1:02d} to {2}:{3:02d}.' .format( time_start.hour, time_start.minute, time_end.hour, time_end.minute, room)) else: if available[ 'data'] == 'Access token has expired.': member.token = None member.expires = None member.refresh_token = None member.update() sc.rtm_send_message( member.channel_id, 'Token is expired, please login again.' ) else: sc.rtm_send_message( member.channel_id, available['data']) else: raise ValueError( 'No commands were matched.') # find_room_json(words[1], str()) except Exception as e: print(e) sc.rtm_send_message(member.channel_id, e.args[0]) sc.rtm_send_message( member.channel_id, 'Try: ```help``` or ```book 397``` or ```list 4``` or ```book 375 2pm```' ) else: print("Connection Failed")
# for cities in data: # logger.logit('city=%s',cities) # for key, value in cities.items(): # countryset.add(cities['country']) # logger.debug('List of countries= %s',countryset) # todo update countries in data with full name instead of abbreviations # ---- End of Open Weather Map initialization # Constants AT_BOT = "<@" + BOT_ID + ">" EXAMPLE_COMMAND = '' CHATROOM = <insert slack room ID> ROBITSHOP = <insert slack room ID> logger.debug(slack_client.api_call("channels.list")) # Functions to parse Slack output and handle commands ------------------------ def handle_command(rb_command, rb_channel): """ Receives commands directed at the bot and determines if they are valid commands. If so, then acts on the commands. If not, returns back what it needs for clarification. """ response = '' rb_command = rb_command.lower() if rb_command.startswith(EXAMPLE_COMMAND): if 'pizza party' in rb_command:
secrets_file = 'slack_secret.json' f = open(secrets_file, 'r') content = f.read() f.close() auth_info = json.loads(content) auth_token = auth_info["access_token"] bot_user_id = auth_info["user_id"] sc = SlackClient(auth_token) sc.rtm_connect() if sc.rtm_connect(): while True: time.sleep(1) response = sc.rtm_read() for item in response: if item.get("type") != 'message': continue if item.get("user") == None: continue message_text = item.get('text') if not message_matches(bot_user_id, message_text): continue response = sc.api_call("users.info", user=item["user"]) username = response['user'].get('name') entity = extract_entity(message_text) message = create_message(username, entity) sc.api_call("chat.postMessage", channel="#assignment2_bots", text=message)
result[key] = None field_id, transform = v data = fields.get(field_id) result[key] = transform(data["value"] if data else None) return result def is_ok(result): return bool(result.get("ok")) if __name__ == "__main__": result = slack.api_call("users.list") if not is_ok(result): fatal("failed to get list of users") profiles = [] users = [u for u in result["members"] if is_valid_user(u)] for user in users: user_id = user["id"] user_name = user["name"] result = slack.api_call("users.profile.get", user=user_id) if not is_ok(result): error(f"failed to get user {user_name} ({user_id})") continue
class SlackWrapper: DEFAULT_SUPPORTED_FILE_TYPES = ["jpg", "jpeg", "png"] def __init__( self, token=None, on_file_shared_fn=None, supported_file_types=DEFAULT_SUPPORTED_FILE_TYPES, ): """ SlackWrapper handles communication with the Slack RTM and Web APIs Params: `slack_token` : String, `supported_file_types` a list of supported file types `on_file_shared_fn` : Fn(string, string) -> None - handle files """ self.__token = token self.__client = SlackClient(token) self.__should_quit = False self.__edward_id = None self.__supported_file_types = supported_file_types self.__on_file_shared_fn = on_file_shared_fn def stop(self): self.__should_quit = True def start(self): """ Connect to Slacks real time messaging client and kick of the main loop """ # first things first; let's connect to Slack RTM if self.__client.rtm_connect(auto_reconnect=True, with_team_state=False): # once that succeeds, store our own user ID for filtering self.__edward_id = self.__client.api_call("auth.test")["user_id"] # kick off main loop; blocking self.run_main_loop() def run_main_loop(self): """ The main loop is responsible for reading messages off of the queue and kicking off converting jobs should a message be applicable. """ EdwardLogger.info("Ready for action!") # start looping as long as we're connected while self.__client.server.connected: # should we shutdown? if self.__should_quit: break batch = self.__client.rtm_read() job_params = self.extract_job_params(batch) if job_params is not None: # all is OK, spawn handler download_url, response_channel_id = job_params # perform the callback containing the job parameters EdwardLogger.info("File attached and valid") self.__on_file_shared_fn(download_url, response_channel_id) # no matter if we scheduled a job or not; wait a bit time.sleep(1) def extract_job_params(self, messages): """ extract_job_params takes the original slack message and returns either `None` or a tuple containing the `download_url` and `return_channel_id`: * download_url is a string containing the download location * return_channel_id is the Slack channel id on which to reply to """ # if there's no messages to be polled, we receive an empty list. # we should just skip and try again if len(messages) == 0: return None # the messages are returned in an array; grab the first one # and start validating the payload message = messages[0] # if message does not contain required fields, ignore it if not self.__is_message_valid(message): return None # if message author is edward itself, ignore to avoid endless looping if message["user"] == self.__edward_id: return None # if it isn't a file_shared message, just ignore and continue if not self.__is_file_attached(message): return None # if there is a file but it is not supported by Edward file = message["files"][0] pretty_type = file["pretty_type"].lower() if pretty_type not in self.__supported_file_types: EdwardLogger.info("Attached file not of valid type, skipping") return None # if we've reached this point, we encountered a valid message. # extract the important bits and return a tuple containing job params return (file["url_private_download"], message["channel"]) def download_image(self, image_download_url): """ download_file accepts a file_download_url, provides authorization and kicks off the request to Slack Web API """ return requests.get(image_download_url, headers=self.__get_http_headers()) def upload_image(self, channel_id, bytes): """ upload_image uploads the sequence of bytes to the provided slack channel. It returns either None if all went well, or the error if something blew up """ response = self.__client.api_call( "files.upload", channels=channel_id, file=bytes ) if response["ok"]: return None else: return response["error"] def __get_http_headers(self): return {"Authorization": "Bearer {}".format(self.__token)} def __is_message_valid(self, message): return ( "type" in message and message["type"] == "message" and "user" in message ) def __is_file_attached(self, message): return ( message["type"] == "message" and "files" in message and len(message["files"]) > 0 )
Executes bot command if the command is known """ # Default response is help text for the user default_response = "Not sure what you mean. Try *{}*.".format( EXAMPLE_COMMAND) # Finds and executes the given command, filling in response response = None # This is where you start to implement more commands! if command.startswith(EXAMPLE_COMMAND): response = "Sure...write some more code then I can do that!" # Sends the response back to the channel slack_client.api_call("chat.postMessage", channel=channel, text=response or default_response) if __name__ == "__main__": if slack_client.rtm_connect(with_team_state=False): print("Starter Bot connected and running!") # Read bot's user ID by calling Web API method 'auth.test' starterbot_id = slack_client.api_call("auth.test")["user_id"] while True: command, channel = parse_bot_commands(slack_client.rtm_read()) if command: handle_command(command, channel) time.sleep(RTM_READ_DELAY) else: print("Connection failed. Exception traceback printed above.")
def handle(self, *args, **options): try: bounty_client = BountyClient() sc = SlackClient(settings.SLACK_TOKEN) while True: # poll by the second if not settings.LOCAL: time.sleep(1) response = sqs_client.receive_message( QueueUrl=settings.QUEUE_URL, AttributeNames=['MessageDeduplicationId'], MessageAttributeNames=['All'], ) messages = response.get('Messages') if not messages: continue message = messages[0] receipt_handle = message['ReceiptHandle'] message_attributes = message['MessageAttributes'] event = message_attributes['Event']['StringValue'] bounty_id = int(message_attributes['BountyId']['StringValue']) fulfillment_id = int( message_attributes['FulfillmentId']['StringValue']) message_deduplication_id = message_attributes[ 'MessageDeduplicationId']['StringValue'] transaction_from = message_attributes['TransactionFrom'][ 'StringValue'] event_timestamp = message_attributes['TimeStamp'][ 'StringValue'] contract_method_inputs = json.loads( message_attributes['ContractMethodInputs']['StringValue']) # If someone uploads a data hash that is faulty, then we want to blacklist all events around that # bounty id. We manage this manually if redis_client.get('blacklist:' + str(bounty_id)): redis_client.set(message_deduplication_id, True) sqs_client.delete_message( QueueUrl=settings.QUEUE_URL, ReceiptHandle=receipt_handle, ) continue logger.info('attempting {}: for bounty id {}'.format( event, str(bounty_id))) if event == 'BountyIssued': bounty_client.issue_bounty(bounty_id, contract_method_inputs, event_timestamp) if event == 'BountyActivated': bounty_client.activate_bounty(bounty_id, contract_method_inputs) if event == 'BountyFulfilled': bounty_client.fulfill_bounty(bounty_id, fulfillment_id, contract_method_inputs, event_timestamp, transaction_from) if event == 'FulfillmentUpdated': bounty_client.update_fulfillment(bounty_id, fulfillment_id, contract_method_inputs) if event == 'FulfillmentAccepted': bounty_client.accept_fulfillment(bounty_id, fulfillment_id) if event == 'BountyKilled': bounty_client.kill_bounty(bounty_id) if event == 'ContributionAdded': bounty_client.add_contribution(bounty_id, contract_method_inputs) if event == 'DeadlineExtended': bounty_client.extend_deadline(bounty_id, contract_method_inputs) if event == 'BountyChanged': bounty_client.change_bounty(bounty_id, contract_method_inputs) if event == 'IssuerTransferred': bounty_client.transfer_issuer(bounty_id, contract_method_inputs) if event == 'PayoutIncreased': bounty_client.increase_payout(bounty_id, contract_method_inputs) logger.info(event) # We should create a separate client to manage these # notifications to slack sc.api_call('chat.postMessage', channel=settings.NOTIFICATIONS_SLACK_CHANNEL, text='Event {} passed for bounty {}'.format( event, str(bounty_id))) # This means the contract subscriber will never send this event # through to sqs again redis_client.set(message_deduplication_id, True) sqs_client.delete_message( QueueUrl=settings.QUEUE_URL, ReceiptHandle=receipt_handle, ) except Exception as e: # goes to rollbar logger.exception(e) raise e
) #leader_board sort by time and pick top10(sql) rows = cur.fetchall() cur.close() conn.close() q1 = rows except Exception, e: print(e) response = "Your result is " + str(res[0].split("\n")[0]) + "\n TOP10: \n" i = 1 for elem in q1: response = response + str(i) + " " + elem[0].strftime( "%Y-%m-%d %H:%M:%S") + " " + str(elem[1]) + "\n" i = i + 1 slack_client.api_call("chat.postMessage", channel=channel, text=response or default_response) pass def parse_direct_mention(message_text): """ Finds a direct mention (a mention that is at the beginning) in message text and returns the user ID which was mentioned. If there is no direct mention, returns None """ #print("Received " + message_text) submission(message_text) matches = re.search(MENTION_REGEX, message_text) # the first group contains the username, the second group contains the remaining message return (matches.group(1), matches.group(2).strip()) if matches else (None, None)
def main(): TOKEN = "TOKEN GOES HERE" GAME = "GAME.z5" CHANNEL = "#CHANNEL-NAME-HERE" CHANNEL_ID = "CHANNEL ID GOES HERE" USERNAME = "******" ICON_EMOJI = ":robot_face:" DFROTZ_PATH = "./dfrotz" sc = SlackClient(TOKEN) if sc.rtm_connect(): sc.api_call( "chat.postMessage", channel=CHANNEL, text="Let's play Interactive Fiction!", username=USERNAME, icon_emoji=ICON_EMOJI ) p = Popen([DFROTZ_PATH, GAME], stdout=PIPE, stdin=PIPE, bufsize=1) q = Queue() t = Thread(target=enqueue_output, args=(p.stdout, q)) t.daemon = True # thread dies with the program t.start() game_start_message = read_from_game(q) print(sc.api_call("chat.postMessage", channel=CHANNEL, text=game_start_message,username=USERNAME, icon_emoji=ICON_EMOJI)) while True: msg = sc.rtm_read() if len(msg) > 0 and isinstance(msg[0],dict): messageInfo = msg[0] if "subtype" in messageInfo: if messageInfo["subtype"] == "bot_message": print("smells like a bot") continue if "channel" in messageInfo: if messageInfo["channel"] != CHANNEL_ID: continue try: if messageInfo["type"] == "message": print(messageInfo["user"] + " said: " + messageInfo["text"]) if messageInfo["text"].lower().find("quit") != -1: print("Passing") continue if messageInfo["text"].lower().find("@") != -1: print("Passing") continue if messageInfo["text"].lower().find("www") != -1: print("URL?") continue if messageInfo["text"].find("`") != -1: print("comment") continue p.stdin.write((messageInfo["text"].split("\n")[0] +"\n").encode()) p.stdin.flush() game_message = read_from_game(q) sc.api_call("chat.postMessage", channel=CHANNEL, text=game_message,username=USERNAME, icon_emoji=ICON_EMOJI) except: print("Didn't like message") print(messageInfo) time.sleep(1) print("done")
for user in user_request["members"]: user_list[user["name"]] = user["id"] if "request_metadata" in user_request and "next_cursor" in user_request[ "request_metadata"]: return { **user_list, **create_user_list(user_request["request_metadata"]["next_cursor"]) } else: return user_list def parse_direct_mention(message_text): matches = re.search(MENTION_REGEX, message_text) # the first group contains the username, the second group contains the remaining message return (matches.group(1), matches.group(2).strip()) if matches else (None, None) # Start the server on port 3000 if __name__ == "__main__": starterbot_id = slack_client.api_call("auth.test")["user_id"] channel_request = slack_client.api_call("channels.list", exclude_archived=1) for channel_item in channel_request["channels"]: slack_channels[channel_item["name"]] = channel_item["id"] if starterbot_id in channel_item["members"]: slack_joined_channels.append(channel_item["name"]) slack_users = create_user_list() server_process.start()
# For this demo app, The task ID should be the last segment of the URL attach_json = [{ "fallback": "Upgrade your Slack client to use messages like these.", "color": "#CC0000", "actions": [{ "type": "button", "text": ":red_circle: Complete Task: " + task_id, "url": "https://roach.ngrok.io/workflow/" + task_id, }] }] # Post the message to Slack, storing the result as `res` res = slack.api_call("chat.postMessage", channel="#link-buttons", text="Let's get started!", attachments=attach_json) # Store the message `ts` and `channel`, so we can request the message # permalink later when the user clicks the link button TASK_IDS[task_id] = {'channel': res['channel'], 'ts': res['message']['ts']} # This is where our link button will link to, showing the user a # task to complete before redirecting them to the `/complete` page @app.route("/workflow/<task_id>", methods=['GET']) def test(task_id): task_form = """<form method="POST" action="/complete/{}"> <input type="submit" value="Do The Thing" /> </form>""".format(task_id)
def parse_direct_mention(message_text): """ Finds a direct mention (a mention that is at the beginning) in message text and returns the user ID which was mentioned. If there is no direct mention, returns None """ matches = re.search(MENTION_REGEX, message_text) # the first group contains the username, the second group contains the remaining message return (matches.group(1), matches.group(2).strip()) if matches else (None, None) if __name__ == "__main__": startTime = time.time() if slack_client.rtm_connect(with_team_state=False): print("Instance manager connected and running!") # Read bot's user ID by calling Web API method `auth.test` arbiter_id = slack_client.api_call("auth.test")["user_id"] slack_client.api_call( "chat.postMessage", channel=slackChannel, text="Preparing to shutdown the following Google Cloud instances in 5 min (use `@The Arbiter save <list of instance numbers>` to save instances)." ) #Get all running nodes for node in get_all_instances(): if node.state == "running": runningNodes.append(node) #Build string list of nodes message="" for idx, node in enumerate(get_node_names(runningNodes)): nodeDict[idx] = node message+="%s %s\n" % (idx, node)
import os from slackclient import SlackClient sc = SlackClient('XXX') sc.api_call("chat.postMessage", channel="#etl_Chris", as_user="******", text="ETL Success")
word = line.strip() # remove newline (\n) characters nouns.append(word) # add to list of words! message = None #create a loop that runs/sends random messages to slack every 5 seconds while True: # we pick a random word from our text file/noun list noun = random.choice(nouns) # format the message - only noun is random message = 'The ' + noun + ' ' + verb + ' through the ' + place + '.' print message # save your message for posterity with open('Messages.txt', 'a') as f: # 'a' = append to the file, not overwrite everything :) f.write(message + '\n') # be sure to add a line break so your tweets don't get mashed together print 'posting message...' # Connect to Slack with our Oauth settings and post messages to general channel slack_client.api_call( "chat.postMessage", channel="#general", text=message ) # wait 5 seconds before running the next loop time.sleep(5)
class AutoMemer: bot_commands = { "add <sub>": "Adds <sub> to the list of subreddits scraped", "delete <sub>": "Deletes <sub> from the list of subreddits scraped", "details <meme_url>": ("Gives details for a meme if meme_url has been scraped", ), "help": "Prints a list of commands and short descriptions", "increase threshold <threshold> {optional_subreddit}": ("Sets threshold for {optional_subreddit} to the old threshold + " "or - the <threshold> value passed. Defaults to global"), "kill": "Kills automemer. Program is stopped, no scraping, no posting", "link <url>": "Prints the link associated with the url passed", "list settings": "Prints out all settings", "list subreddits": "Prints a list of subreddits currently being scraped", "list thresholds": "Prints the thresholds for subs", "num-memes {postable_only} {by_sub}": ("Prints the number of memes currently waiting to be posted. " "To only post memes with enough upvotes use `num-memes postable_only`, to get a " "breakdown by subreddit use `num-memes by_sub`"), "pop {num}": "pops {num} memes (or as many as there are) from the queue", "set scrape interval <int>": "sets the scrape interval to <int> minutes", "set threshold <threshold> {optional_subreddit}": ("Sets threshold upvotes a meme must meet to be scraped. If " "{optional_subreddit} is specified, sets <threshold> specifically " "for that sub, otherwise a global threshold is set (applied to " "subs without a specific threshold)"), } def __init__(self, bot_id, channel_id, bot_token, debug=False): self.bot_id = bot_id self.at_bot = "<@" + bot_id + ">" self.channel_id = channel_id self.client = SlackClient(bot_token) self.messages = queue.Queue() self.lock = Lock() self.debug = debug self.users_list = self.client.api_call("users.list") def dict_factory(cursor, row): d = {} for idx, col in enumerate(cursor.description): d[col[0]] = row[idx] return d self.conn = sqlite3.connect('memes/memes.sqlite3') self.conn.row_factory = dict_factory self.cursor = self.conn.cursor() self.log_file = './memes/log_file.txt' self.scraped_path = './memes/scraped.json' self.settings_path = './memes/settings.json' # creating directories and files os.makedirs('memes', exist_ok=True) if not os.path.isfile(self.scraped_path): file = open(self.scraped_path, 'x') file.write(json.dumps({})) file.close() if not os.path.isfile(self.settings_path): file = open(self.settings_path, 'x') file.write(json.dumps({})) file.close() @staticmethod def current_time_as_min(): now = datetime.datetime.now() midnight = now.replace(hour=0, minute=0, second=0, microsecond=0) return (now - midnight).seconds // 60 def run(self): READ_WEBSOCKET_DELAY = 1 # 1 second delay between reading from firehose while True: try: if self.client.rtm_connect(): print("AutoMemer connected and running!") while True: scraped_reddit = False # have we added the contents of scraped.json to our queue added_memes_to_queue = False post_to_slack_interval = self.load_post_to_slack_interval( ) slack_outputs = self.parse_slack_output( self.client.rtm_read()) for output in slack_outputs: self.handle_command(output) time_as_minutes = self.current_time_as_min() if time_as_minutes % 10 == 0 and not scraped_reddit: Process(target=scrape_reddit.scrape, args=(self.cursor, self.conn, self.lock)).start() scraped_reddit = True # we just added the memes elif time_as_minutes % 10 > 0: # the minute passed, reset scraped_reddit scraped_reddit = False if (time_as_minutes % post_to_slack_interval == 0 and not added_memes_to_queue): self.add_new_memes_to_queue() added_memes_to_queue = True elif (time_as_minutes % post_to_slack_interval > 0 and added_memes_to_queue): added_memes_to_queue = False self.pop_queue() time.sleep(READ_WEBSOCKET_DELAY) else: print("Connection failed. Invalid Slack token or bot ID?") break except (websocket._exceptions.WebSocketConnectionClosedException, BrokenPipeError) as e: pass def handle_command(self, output): """ Receives commands directed at the bot and determines if they are valid commands. If so, then acts on the commands. If not, returns back what it needs for clarification. """ command = output.get('@mention') if command is None: return response = '>{}\n'.format(command) command = command.lower() # specific command responses if command.startswith("add"): response += self._command_add_sub(output) elif command.startswith("delete") or command.startswith('remove'): response += self._command_delete_sub(output) elif command.startswith("details"): response += self._command_details(output) elif command == "help": response += self._command_help() elif command.startswith("increase threshold"): response += self._command_set_threshold(output, mode="+") elif command.startswith("list thresholds"): response += self._command_list_thresholds() elif command.startswith("link"): response += self._command_details(output, link_only=True) elif command == "list settings": response += self._command_list_settings() elif command == "list subreddits": response += self._command_list_subs() elif command.startswith("set threshold"): response += self._command_set_threshold(output) elif command.startswith("set post interval"): response += self._command_set_post_interval(output) elif command.startswith("pop"): reply = self._command_pop(output) if reply == "": # if we get an empty string back we've already popped the memes return else: response += reply elif command.startswith('num-memes'): response += self._command_num_memes(output) elif command == "kill": self.client.api_call("chat.postMessage", channel=MEME_SPAM_CHANNEL, text="have it your way", as_user=True) sys.exit(0) elif command.startswith("echo "): response = ''.join(output.get('@mention').split()[1:]) else: # a default response response = (">*{}*\nI don't know this command :dealwithitparrot:\n" .format(command)) # construct the response msg = { 'channel': output['channel'], 'text': response, } if 'thread_ts' in output: # the message we are responding too was threaded, so thread msg['thread_ts'] = output['thread_ts'] self.messages.put(msg) def load_post_to_slack_interval(self): self.lock.acquire() try: with open(self.settings_path, mode='r', encoding='utf-8') as f: settings = f.read() settings = json.loads(settings) interval = settings['scrape_interval'] return interval except Exception as e: utils.log_error(e) return 60 finally: self.lock.release() def add_new_memes_to_queue(self, limit=None, user_prompt=False): _, postable = self.count_memes() # post 20% of the current number of memes in the queue, or 10 limit = limit or max(10, int(0.2 * sum(postable.values()))) self.lock.acquire() try: with open(self.scraped_path, mode='r', encoding='utf-8') as f: scraped = f.read() scraped_memes = json.loads(scraped) with open(self.settings_path, mode='r', encoding='utf-8') as f: settings = json.loads(f.read()) thresholds = settings['threshold_upvotes'] memes_by_sub = defaultdict(list) for post, data in sorted(list(scraped_memes.items()), key=lambda x: x[1]['created_utc']): memes_by_sub[data['sub']].append(data) list_of_subs = list(memes_by_sub.keys()) sub_ind = 0 while limit > 0 and any(memes_by_sub.values()): # while we haven't reached the limit and have more memes to post sub = list_of_subs[sub_ind] sub_threshold = thresholds.get(sub.lower(), thresholds['global']) while memes_by_sub[sub]: # while there are memes from this sub meme = memes_by_sub[sub].pop(0) del scraped_memes[meme['url']] ups = int(meme.get('highest_ups')) if ups > sub_threshold: utils.set_posted_to_slack(self.cursor, meme['id'], self.conn, True) limit -= 1 meme_text = ( "*{title}* _(from /r/{sub})_ `{ups:,d}`\n{url}". format(title=meme.get('title').strip('*'), sub=sub.strip('_'), ups=ups, url=meme['url'])) self.messages.put({ 'channel': MEME_SPAM_CHANNEL, 'text': meme_text, }) break sub_ind = (sub_ind + 1) % len(list_of_subs) with open(self.scraped_path, mode='w', encoding='utf-8') as f: f.write(json.dumps(scraped_memes, indent=2)) if limit > 0 and user_prompt: self.messages.put({ 'channel': MEME_SPAM_CHANNEL, 'text': 'Sorry, we ran out of memes :(' }) except Exception as e: self.messages.put({ 'channel': MEME_SPAM_CHANNEL, 'text': ("There was an error :sadparrot:\n" ">`{}`".format(str(e))), }) utils.log_error(e) finally: self.lock.release() def pop_queue(self): if not self.messages.empty(): msg = self.messages.get() if not self.debug: self.client.api_call("chat.postMessage", **msg, as_user=True) else: msg['api'] = 'chat.postMessage' msg['as_user'] = True msg['time'] = datetime.datetime.now().isoformat(), with open(self.log_file, 'a') as f: f.write(json.dumps(msg, indent=2) + ',\n') def parse_slack_output(self, slack_rtm_output): """ the Slack Real Time Messaging API is an events firehose. this parsing function returns None unless a message is directed at the Bot, based on its ID. """ if slack_rtm_output: for output in slack_rtm_output: output['time'] = datetime.datetime.now().isoformat() if 'user' in output: output['username'] = self._get_name(output['user']) if 'text' in output and self.at_bot in output['text']: # return text after the @ mention, whitespace removed output['@mention'] = output['text'].split( self.at_bot)[1].strip() self.log_slack_rtm(slack_rtm_output) return slack_rtm_output def log_slack_rtm(self, message): if not isinstance(message, str): message = json.dumps(message, indent=2) with open(self.log_file, 'a') as f: f.write(message + ',\n') def count_memes(self): self.lock.acquire() try: with open(self.scraped_path, mode='r', encoding='utf-8') as f: memes = f.read() with open(self.settings_path, mode='r', encoding='utf-8') as f: settings = f.read() memes = json.loads(memes) settings = json.loads(settings) thresholds = settings['threshold_upvotes'] total, postable = Counter(), Counter() for post, data in memes.items(): if not data.get('over_18'): sub = data.get('sub', '').lower() ups = data.get('highest_ups') sub_threshold = thresholds.get(sub, thresholds['global']) total[sub] += 1 if ups >= sub_threshold: postable[sub] += 1 return total, postable except OSError: return Counter(), Counter() finally: self.lock.release() def _command_help(self): text = "" for command, description in sorted(AutoMemer.bot_commands.items(), key=lambda x: x[0]): text += '`{}` - {}\n'.format(command, description) return text def _command_add_sub(self, output): response = "" command = output.get('@mention').lower().split() if len(command) != 2: response += "command must be in the form `add [name]`" else: command = command[1] settings = json.loads(open(self.settings_path).read()) settings['subs'].append(command) self.lock.acquire() try: with open(self.settings_path, mode='w', encoding='utf-8') as f: f.write(json.dumps(settings, indent=2)) finally: self.lock.release() response += "_/r/{}_ has been added!".format(command) return response def _command_delete_sub(self, output): response = "" command = output.get('@mention').lower().split() if len(command) != 2: response += "command must be in the form `delete [name]`" else: sub = command[1] settings = json.loads(open(self.settings_path).read()) previous_subs = settings['subs'] previous_thresholds = settings['threshold_upvotes'] if sub not in previous_subs: response += ( "_/r/{0}_ is not currently being followed, to add it use the" " command `add {0}`".format(sub)) else: previous_subs.remove(sub) settings['subs'] = previous_subs if sub in previous_thresholds: del previous_thresholds[sub] self.lock.acquire() try: with open(self.settings_path, mode='w', encoding='utf-8') as f: f.write(json.dumps(settings, indent=2)) finally: self.lock.release() response += "_/r/{}_ has been removed".format(sub) return response def _command_list_settings(self): response = "" self.lock.acquire() try: with open(self.settings_path, mode='r', encoding='utf-8') as f: settings = json.loads(f.read()) finally: self.lock.release() for key, val in sorted(settings.items()): if key == "subs": val = sorted(val) response += "`{key}`: {val}\n".format(key=key, val=json.dumps(val, indent=2)) return response def _command_list_thresholds(self): response = "" self.lock.acquire() try: settings = json.loads(open(self.settings_path).read()) thresholds = settings.get('threshold_upvotes') response += json.dumps(thresholds, indent=2) except OSError as e: response += ':sadparrot: error\n' response += str(e) finally: self.lock.release() return response def _command_list_subs(self): response = "" self.lock.acquire() try: settings = json.loads(open(self.settings_path).read()) subs = sorted(settings.get('subs')) response += ( 'The following subreddits are currently being followed: {}'. format(str(subs))) except OSError as e: response += ':sadparrot: error\n' response += str(e) finally: self.lock.release() return response def _command_set_threshold(self, output, mode=None): command_str = 'set' if mode == '+': command_str = 'increase' response = "" command = output.get('@mention').lower().split() if len(command) not in [3, 4]: response += ( "command must be in the form '{change} threshold {threshold} <optional-sub>'" .format(change=command_str)) elif len(command) == 3 or command[-1].lower() == 'global': threshold = command[2] try: threshold = int(threshold) except ValueError: response += "{threshold} is not a valid integer".format( threshold=threshold) else: old_t, new_t = self._command_set_threshold_to(threshold, mode=mode) response += ( "The global threshold has been set to *{threshold}*! (previously {old})" .format(threshold=new_t, old=old_t)) else: sub = command[-1].lower() with open(self.settings_path, mode='r', encoding='utf-8') as f: settings = json.loads(f.read()) if sub not in settings['subs']: response += "{} is not in the list of subreddits. run `list subreddits` to view a list".format( sub) else: threshold = command[2] try: threshold = int(threshold) except ValueError: response += "{threshold} is not a valid integer".format( threshold=threshold) else: old_t, new_t = self._command_set_threshold_to(threshold, sub=sub, mode=mode) response += ( "The threshold upvotes for _{sub}_ has been set to *{threshold}*! (previously {old})" .format(sub=sub, threshold=new_t, old=old_t)) return response def _command_set_threshold_to(self, upvote_value, sub='global', mode=None): self.lock.acquire() try: settings = json.loads(open(self.settings_path).read()) old_t = settings['threshold_upvotes'].get(sub, 'global') new_t = upvote_value if mode == '+': if old_t == 'global': new_t += settings['threshold_upvotes']['global'] else: new_t += old_t new_t = max(1, new_t) settings['threshold_upvotes'][sub] = new_t with open(self.settings_path, 'w') as f: f.write(json.dumps(settings, indent=2)) return old_t, new_t finally: self.lock.release() def _command_details(self, output, link_only=False): response = "" command = output.get('@mention').split() if len(command) != 2: response += "command must be in the form `details <meme_url>`\n" else: meme_url = html.unescape(command[1][1:-1]) meme_data = scrape_reddit.update_reddit_meme( self.cursor, self.conn, meme_url, self.lock) if meme_data is None: response += "I could find any data for this url: `{}`, sorry\n".format( meme_url) else: if link_only: for meme in meme_data: response += meme.get('link') + '\n' else: for meme in meme_data: for key, val in sorted(meme.items()): response += "`{key}`: {data}\n".format(key=key, data=val) response += '\n' return response def _command_set_post_interval(self, command): response = "" interval = output.get('@mention').split() if len(interval) != 4: response += "command must be in the form `set post interval <integer>`" else: interval = interval[-1] try: interval = int(interval) except ValueError: response += "{} is not an integer :parrotcop:".format(interval) else: if interval >= 1440: response += ("```\n" ">>> minutes_per_day()\n" "1440" "```\n" "Too many minutes!") elif interval <= 0: response += "Please enter a number greater than 0" else: self.lock.acquire() try: with open(self.settings_path, mode='r', encoding='utf-8') as s: settings = s.read() settings = json.loads(settings) settings['scrape_interval'] = interval global scrape_interval scrape_interval = interval with open(self.settings_path, mode='w', encoding='utf-8') as s: s.write(json.dumps(settings, indent=2)) response += "scrape_interval has been set to *{}*!".format( str(interval)) finally: self.lock.release() return response def _command_pop(self, output): response = "" command = output.get('@mention').split() if len(command) == 2: try: limit = int(command[1]) except ValueError: response += "{} isn't a number!".format(str(command[1])) else: if limit <= 0: response += "You can't pop 0 or fewer memes.." else: self.add_new_memes_to_queue(limit, user_prompt=True) else: self.add_new_memes_to_queue(user_prompt=True) return response def _command_num_memes(self, output): response = "" command = output.get('@mention').lower().split() by_sub = 'by_sub' in command postable_only = 'postable_only' in command total, postable = self.count_memes() subs_lower_to_title = {sub.lower(): sub for sub in total} if not by_sub: if not postable_only: text = "Total memes: {}\nPostable memes: {}".format( str(sum(total.values())), str(sum(postable.values()))) response += text else: response += "Postable memes: {}".format( str(sum(postable.values()))) else: if not postable_only: for sub in sorted(list(map(lambda x: x.lower(), total.keys()))): response += "*{sub}*: {good} ({tot})\n".format( sub=subs_lower_to_title[sub], good=postable[subs_lower_to_title[sub]], tot=total[subs_lower_to_title[sub]]) response += "\n*Combined*: {} ({})".format( str(sum(postable.values())), str(sum(total.values()))) else: for sub in sorted(list(map(lambda x: x.lower(), total.keys()))): response += "*{sub}*: {ups}\n".format( sub=subs_lower_to_title[sub], ups=postable[subs_lower_to_title[sub]]) response += "\n*Combined*: {}".format( str(sum(postable.values()))) return response def _get_name(self, user_id): if self.users_list is None: return user_id for member in self.users_list['members']: if member['id'] == user_id: name = member.get('name') if name is not None: return name profile = members.get('profile') if profile is not None: name = profile.get('real_name') if name is not None: return name return user_id return user_id
class Bot(object): """ Instanciates a Bot object to handle Slack onboarding interactions.""" def __init__(self): super(Bot, self).__init__() self.name = "pythonboardingbot" self.emoji = ":robot_face:" # When we instantiate a new bot object, we can access the app # credentials we set earlier in our local development environment. self.oauth = {"client_id": os.environ.get("CLIENT_ID"), "client_secret": os.environ.get("CLIENT_SECRET"), # Scopes provide and limit permissions to what our app # can access. It's important to use the most restricted # scope that your app will need. "scope": "bot"} self.verification = os.environ.get("VERIFICATION_TOKEN") # NOTE: Python-slack requires a client connection to generate # an oauth token. We can connect to the client without authenticating # by passing an empty string as a token and then reinstantiating the # client with a valid OAuth token once we have one. self.client = SlackClient("") # We'll use this dictionary to store the state of each message object. # In a production envrionment you'll likely want to store this more # persistantly in a database. self.messages = {} def auth(self, code): """ Authenticate with OAuth and assign correct scopes. Save a dictionary of authed team information in memory on the bot object. Parameters ---------- code : str temporary authorization code sent by Slack to be exchanged for an OAuth token """ # After the user has authorized this app for use in their Slack team, # Slack returns a temporary authorization code that we'll exchange for # an OAuth token using the oauth.access endpoint auth_response = self.client.api_call( "oauth.access", client_id=self.oauth["client_id"], client_secret=self.oauth["client_secret"], code=code ) # To keep track of authorized teams and their associated OAuth tokens, # we will save the team ID and bot tokens to the global # authed_teams object team_id = auth_response["team_id"] authed_teams[team_id] = {"bot_token": auth_response["bot"]["bot_access_token"]} # Then we'll reconnect to the Slack Client with the correct team's # bot token self.client = SlackClient(authed_teams[team_id]["bot_token"]) def open_dm(self, user_id): """ Open a DM to send a welcome message when a 'team_join' event is recieved from Slack. Parameters ---------- user_id : str id of the Slack user associated with the 'team_join' event Returns ---------- dm_id : str id of the DM channel opened by this method """ new_dm = self.client.api_call("im.open", user=user_id) dm_id = new_dm["channel"]["id"] return dm_id def post_msg(self, team_id, channel): new_msg = self.client.api_call("chat.postMessage", channel=channel, text="Hello from Python! :tada:") return new_msg #def test_msg(self, team_id, user_id): def onboarding_message(self, team_id, user_id): """ Create and send an onboarding welcome message to new users. Save the time stamp of this message on the message object for updating in the future. Parameters ---------- team_id : str id of the Slack team associated with the incoming event user_id : str id of the Slack user associated with the incoming event """ # We've imported a Message class from `message.py` that we can use # to create message objects for each onboarding message we send to a # user. We can use these objects to keep track of the progress each # user on each team has made getting through our onboarding tutorial. # First, we'll check to see if there's already messages our bot knows # of for the team id we've got. if self.messages.get(team_id): # Then we'll update the message dictionary with a key for the # user id we've recieved and a value of a new message object self.messages[team_id].update({user_id: message.Message()}) else: # If there aren't any message for that team, we'll add a dictionary # of messages for that team id on our Bot's messages attribute # and we'll add the first message object to the dictionary with # the user's id as a key for easy access later. self.messages[team_id] = {user_id: message.Message()} message_obj = self.messages[team_id][user_id] # Then we'll set that message object's channel attribute to the DM # of the user we'll communicate with message_obj.channel = self.open_dm(user_id) # We'll use the message object's method to create the attachments that # we'll want to add to our Slack message. This method will also save # the attachments on the message object which we're accessing in the # API call below through the message object's `attachments` attribute. message_obj.create_attachments() post_message = self.client.api_call("chat.postMessage", channel=message_obj.channel, username=self.name, icon_emoji=self.emoji, text=message_obj.text, attachments=message_obj.attachments ) timestamp = post_message["ts"] # We'll save the timestamp of the message we've just posted on the # message object which we'll use to update the message after a user # has completed an onboarding task. message_obj.timestamp = timestamp def update_emoji(self, team_id, user_id): """ Update onboarding welcome message after recieving a "reaction_added" event from Slack. Update timestamp for welcome message. Parameters ---------- team_id : str id of the Slack team associated with the incoming event user_id : str id of the Slack user associated with the incoming event """ # These updated attachments use markdown and emoji to mark the # onboarding task as complete completed_attachments = {"text": ":white_check_mark: " "~*Add an emoji reaction to this " "message*~ :thinking_face:", "color": "#439FE0"} # Grab the message object we want to update by team id and user id message_obj = self.messages[team_id].get(user_id) # Update the message's attachments by switching in incomplete # attachment with the completed one above. message_obj.emoji_attachment.update(completed_attachments) # Update the message in Slack post_message = self.client.api_call("chat.update", channel=message_obj.channel, ts=message_obj.timestamp, text=message_obj.text, attachments=message_obj.attachments ) # Update the timestamp saved on the message object message_obj.timestamp = post_message["ts"] def update_pin(self, team_id, user_id): """ Update onboarding welcome message after recieving a "pin_added" event from Slack. Update timestamp for welcome message. Parameters ---------- team_id : str id of the Slack team associated with the incoming event user_id : str id of the Slack user associated with the incoming event """ # These updated attachments use markdown and emoji to mark the # onboarding task as complete completed_attachments = {"text": ":white_check_mark: " "~*Pin this message*~ " ":round_pushpin:", "color": "#439FE0"} # Grab the message object we want to update by team id and user id message_obj = self.messages[team_id].get(user_id) # Update the message's attachments by switching in incomplete # attachment with the completed one above. message_obj.pin_attachment.update(completed_attachments) # Update the message in Slack post_message = self.client.api_call("chat.update", channel=message_obj.channel, ts=message_obj.timestamp, text=message_obj.text, attachments=message_obj.attachments ) # Update the timestamp saved on the message object message_obj.timestamp = post_message["ts"] def update_share(self, team_id, user_id): """ Update onboarding welcome message after recieving a "message" event with an "is_share" attachment from Slack. Update timestamp for welcome message. Parameters ---------- team_id : str id of the Slack team associated with the incoming event user_id : str id of the Slack user associated with the incoming event """ # These updated attachments use markdown and emoji to mark the # onboarding task as complete completed_attachments = {"text": ":white_check_mark: " "~*Share this Message*~ " ":mailbox_with_mail:", "color": "#439FE0"} # Grab the message object we want to update by team id and user id message_obj = self.messages[team_id].get(user_id) # Update the message's attachments by switching in incomplete # attachment with the completed one above. message_obj.share_attachment.update(completed_attachments) # Update the message in Slack post_message = self.client.api_call("chat.update", channel=message_obj.channel, ts=message_obj.timestamp, text=message_obj.text, attachments=message_obj.attachments ) # Update the timestamp saved on the message object message_obj.timestamp = post_message["ts"]
def invite_to_slack(email): if settings.DEBUG: return {} sc = SlackClient(settings.SLACK_TOKEN) response = sc.api_call('users.admin.invite', email=email) return response
class HRSlackBot(object): def __init__(self, host, port, botname, **kwargs): self.sc = SlackClient(SLACKBOT_API_TOKEN) self.sc.rtm_connect() self.botname = botname self.host = host self.port = str(port) self.lang = 'en' self.icon_url = 'https://avatars.slack-edge.com/2016-05-30/46725216032_4983112db797f420c0b5_48.jpg' self.session_manager = SessionManager() self.weights = kwargs.get('weights') def send_message(self, channel, attachments): self.sc.api_call( "chat.postMessage", channel=channel, attachments=attachments, username=self.botname.title(), icon_url=self.icon_url) def error(self, channel, msg): attachments = [{ 'title': msg, 'color': 'danger', 'fallback': msg }] logger.error(msg) self.send_message(channel, attachments) def info(self, channel, msg): attachments = [{ 'title': msg, 'color': '#36a64f', 'fallback': msg }] logger.info(msg) self.send_message(channel, attachments) def run(self): while True: time.sleep(0.2) messages = self.sc.rtm_read() if not messages: continue for message in messages: if message['type'] != u'message': continue if message.get('subtype') == u'bot_message': continue usr_obj = self.sc.api_call( 'users.info', token=SLACKTEST_TOKEN, user=message['user']) if not usr_obj['ok']: continue profile = usr_obj['user']['profile'] name = profile.get('first_name') or profile.get('email') question = message.get('text') channel = message.get('channel') sid = self.session_manager.get_sid(name, self.botname) session = self.session_manager.get_session(sid) if session is not None: assert hasattr(session.sdata, 'client') client = session.sdata.client else: client = Client(HR_CHATBOT_AUTHKEY, username=name, botname=self.botname, host=self.host, port=self.port, response_listener=self) client.set_marker('Slack') if self.weights: client.set_weights(self.weights) self.session_manager.add_session(name, self.botname, client.session) session = self.session_manager.get_session(client.session) if session is not None: session.sdata.client = client session.sdata.channel = channel self.info(channel, "Session <{url}/v1.1/session_history?session={sid}&Auth={auth}|{sid}>".format( url=CHATBOT_SERVER_URL, sid=session.sid, auth=HR_CHATBOT_AUTHKEY)) else: self.error(channel, "Can't get session") continue logger.info("Question {}".format(question)) if question in [':+1:', ':slightly_smiling_face:', ':)', 'gd']: ret, _ = client._rate('good') if ret: logger.info("Rate good") answer = 'Thanks for rating' color = 'good' else: logger.info("Rate failed") answer = 'Rating failed' color = 'danger' attachments = [{ 'title': answer, 'color': color, 'fallback': answer }] self.send_message(channel, attachments) continue if question in [':-1:', ':disappointed:', ':(', 'bd']: ret, _ = client._rate('bad') if ret: logger.info("Rate bad") answer = 'Thanks for rating' color = 'good' else: logger.info("Rate failed") answer = 'Rating failed' color = 'danger' attachments = [{ 'title': answer, 'color': color, 'fallback': answer }] self.send_message(channel, attachments) continue try: client.ask(question) except Exception as ex: self.error(channel, ex.message) # session could change after ask if client.session != session.sid: self.session_manager.remove_session(session.sid) self.session_manager.add_session( name, self.botname, client.session) session = self.session_manager.get_session(client.session) session.sdata.client = client session.sdata.channel = channel self.info(channel, "Session <{url}/v1.1/session_history?session={sid}&Auth={auth}|{sid}>".format( url=CHATBOT_SERVER_URL, sid=session.sid, auth=HR_CHATBOT_AUTHKEY)) logger.info("Session is updated") def on_response(self, sid, response): answer = '' title = '' session = self.session_manager.get_session(sid) if session is None: time.sleep(0.5) session = self.session_manager.get_session(sid) if session is None: logger.error("No such session {}".format(session)) return channel = session.sdata.channel if response is None or not response.get('text'): answer = u"Sorry, I can't answer it right now" else: answer = response.get('text') trace = response.get('trace', '') botid = response.get('botid', '') if trace: formated_trace = format_trace(trace) if formated_trace: title = 'answered by {}\n\ntrace:\n{}'.format(botid, '\n'.join(formated_trace)) attachments = [{ 'pretext': answer, 'title': title, 'color': '#3AA3E3', 'fallback': answer, }] self.send_message(channel, attachments)
class Tangerine(object): jinja_environment = Environment def __init__(self, slack_token=None, settings=None): settings = settings or {} settings.setdefault('tangerine', {}) settings['tangerine'].setdefault('sleep', 0.5) settings['tangerine'].setdefault('template_folder', 'templates') self.settings = Box(settings, frozen_box=True, default_box=True) self.listeners = [] self.scheduled_tasks = [] self.client = SlackClient( slack_token or self.settings.tangerine.auth_token, ) self.sleep = self.settings.tangerine.sleep @classmethod def config_from_yaml(cls, path_to_yaml): with open(path_to_yaml, 'r') as ymlfile: settings = yaml.load(ymlfile) log.info('settings from %s loaded successfully', path_to_yaml) return cls(settings=settings) def _verify_rule(self, supplied_rule): """Rules must be callable with (user, message) in the signature. Strings are automatically converted to callables that match. """ # If string, make a simple match callable if isinstance(supplied_rule, six.string_types): return lambda user, message: supplied_rule in message.lower() if not six.callable(supplied_rule): raise ValueError('Bot rules must be callable or strings') expected = ('user', 'message') signature = tuple(inspect.getargspec(supplied_rule).args) try: # Support class- and instance-methods where first arg is # something like `self` or `cls`. assert len(signature) in (2, 3) assert expected == signature or expected == signature[-2:] except AssertionError: msg = 'Rule signuture must have only 2 arguments: user, message' raise ValueError(msg) return supplied_rule def listen_for(self, rule, **options): """Decorator for adding a Rule. See guidelines for rules.""" trigger = None if isinstance(rule, six.string_types): trigger = rule rule = self._verify_rule(rule) def decorator(f): self.add_listener(rule, f, trigger, f.__doc__, **options) return f return decorator def cron(self, schedule, **options): def decorator(f): self.add_cron(schedule, f, **options) return f return decorator def run(self): self.running = True if self.client.rtm_connect(): try: self.event_loop() except (KeyboardInterrupt, SystemExit): log.info('attempting graceful shutdown...') self.running = False try: sys.exit(0) except SystemExit: os._exit(0) def event_loop(self): while self.running: time.sleep(self.sleep) self.process_stream() self.process_scheduled_tasks() def read_stream(self): data = self.client.rtm_read() if not data: return data return [Box(d) for d in data][0] def process_stream(self): data = self.read_stream() if not data or data.type != 'message' or 'user' not in data: return self.respond(data.user, data.text, data.channel) def process_scheduled_tasks(self): now = datetime.datetime.now() for idx, task in enumerate(self.scheduled_tasks): if now > task.next_run: t = self.scheduled_tasks.pop(idx) t.run() self.add_cron(t.schedule, t.fn, **t.options) def respond(self, user, message, channel): sendable = { 'user': user, 'message': message, 'channel': channel, } if not message: return for rule, view_func, _, _, options in self.listeners: if rule(user, message): args = inspect.getargspec(view_func).args kwargs = {k: v for k, v in sendable.items() if k in args} response = view_func(**kwargs) if response: if 'hide_typing' not in options: # TODO(nficano): this should be configurable time.sleep(.2) self.client.server.send_to_websocket({ 'type': 'typing', 'channel': channel, }) time.sleep(.5) if '{user.username}' in response: response = response.replace( '{user.username}', self.get_user_name(user), ) self.speak(response, channel) def add_listener(self, rule, view_func, trigger, docs, **options): """Adds a listener to the listeners container; verifies that `rule` and `view_func` are callable. """ if not six.callable(rule): raise TypeError('rule should be callable') if not six.callable(view_func): raise TypeError('view_func should be callable') self.listeners.append( Listener(rule, view_func, trigger, docs, options), ) def add_cron(self, schedule, f, **options): self.scheduled_tasks.append(Task(schedule, f, **options)) def speak(self, message, channel, **kwargs): self.client.api_call( 'chat.postMessage', as_user=True, channel=channel, text=message, **kwargs, ) def get_user_info(self, user_id): return self.client.api_call('users.info', user=user_id) def get_user_name(self, user_id): user = self.get_user_info(user_id) return user.get('user', {}).get('name') def get_user_id_from_username(self, username): for m in self.client.api_call('users.list')['members']: if username.lower() == m.get('name', '').lower(): return m['id'] def get_channel_id_from_name(self, channel): channel = channel.lower().replace('#', '') types = ','.join(['public_channel', 'private_channel']) response = self.client.api_call( 'conversations.list', types=types, limit=1000, ) for c in response['channels']: if channel == c['name'].lower(): return c['id'] response = self.client.api_call('channels.list', limit=1000) for c in response['channels']: if channel == c['name'].lower(): return c['id'] def get_channel_name_from_channel_id(self, channel_id): types = ','.join(['public_channel', 'private_channel']) response = self.client.api_call( 'conversations.list', types=types, limit=1000, ) for c in response['channels']: if channel_id == c['id']: return c['name'] response = self.client.api_call('channels.list', limit=1000) for c in response['channels']: if channel_id == c['id']: return c['name'] def get_template_path(self): if os.path.isabs(self.settings.tangerine.template_folder): return self.settings.tangerine.template_folder else: return os.path.join( os.getcwd(), self.settings.tangerine.template_folder, ) def get_jinja_environment(self): return self.jinja_environment( loader=FileSystemLoader(self.get_template_path()), autoescape=select_autoescape(['txt']), ) def render_template(self, template_name, **context): env = self.get_jinja_environment() template = env.get_template(template_name) return template.render(**context)
#Get all messages in a channel def allMessages(channel='C5GRK45A9'): msg_endpoint = 'https://slack.com/api/channels.history?token=%s&channel=%s&pretty=1' % ( token, channel) all_messages = requests.get(msg_endpoint) text_all_message = all_messages.text with open("messages.json", "w") as message_file: message_file.write(text_all_message) message_file.close() return all_messages.json() # Get all channels in the Team channels = sc.api_call("channels.list")['channels'] print("There are {} channels in OPEN-Andela.".format(len(channels))) # Save the channels information to a .txt file with open("channels.txt", "w") as textfile: textfile.write("There are {} channels in OPEN-Andela.\n".format( len(channels))) textfile.write("") for channel in channels: textfile.write("----------------------------------------------- \n") textfile.write("Channel Name: {} \n".format(channel["name"])) textfile.write("Channel Name (Normalized): {} \n".format( channel["name_normalized"])) textfile.write("Number of Members: {} \n".format( channel['num_members']))
class Bot(object): """ Instanciates a Bot object to handle Slack onboarding interactions.""" def __init__(self): super(Bot, self).__init__() self.name = "appone" self.emoji = ":robot_face:" # When we instantiate a new bot object, we can access the app # credentials we set earlier in our local development environment. # Scopes provide and limit permissions to what our app # can access. It's important to use the most restricted # scope that your app will need. self.oauth = { "client_id": os.environ.get("SLACK_CLIENT_ID"), "client_secret": os.environ.get("SLACK_CLIENT_SECRET"), "scope": os.environ.get("SLACK_BOT_SCOPE", "bot") } self.user_name_map = {} self.channel_data_map = {} # NOTE: Python-slack requires a client connection to generate # an oauth token. We can connect to the client without authenticating # by passing an empty string as a token and then reinstantiating the # client with a valid OAuth token once we have one. bot_oauth_default = "xoxb-445512136161-446113431922-MbaetJ62o8U1mr4u91BTauSq" bot_oauth_token = os.environ.get("SLACK_BOT_OAUTH_ACCESS", bot_oauth_default) #dprint ("calling SlackClient with token %s" % (bot_oauth_token,)) self.client = SlackClient(bot_oauth_token) self.circleci_user_token = os.environ['CIRCLECI_HODOLIZER_TOKEN'] self.circleci_project_token = os.environ['CIRCLECI_HB_LITTLEBOT_TOKEN'] self.circleci_client = circleclient.CircleClient( self.circleci_user_token) self.circleci_repo_list = [] # We'll use this dictionary to store the state of each message object. # In a production envrionment you'll likely want to store this more # persistantly in a database. self.messages = {} def get_all_users_map(self): """ Get all users for which we have visibility and create a dictionary map by user 'id' to user information. """ slack_client = self.client api_call = slack_client.api_call("users.list") users = api_call.get('members') if api_call.get('ok'): for user in users: self.user_name_map[user['id']] = user return self.user_name_map def get_all_channel_data_map(self): """ Get all channels for which we have visibility and create a dictionary map by channels 'id' to channels information. """ slack_client = self.client api_call = slack_client.api_call("channels.list") channels = api_call.get('channels') if api_call.get('ok'): for chan in channels: self.channel_data_map[chan['id']] = chan return self.channel_data_map def get_channel_name_map(self): """ Use the self.channel_name_map created by get_all_channel_map() to return a map with just id:name mappings """ channel_id_name_map = {} all_channel_map = self.get_all_channel_data_map() if all_channel_map: for id, data in all_channel_map.items(): channel_id_name_map[data["name"]] = id return channel_id_name_map else: dprint("No channel mappings could be found") def get_bot_userid(self, bot_name): # Courtesy of Twilio # https://www.twilio.com/blog/2016/05/add-phone-calling-slack-python.html # retrieve all users so we can find our bot users = self.get_all_users_map() if users: for uid, udata in users.items(): u_name = udata["profile"].get("display_name") if not u_name: u_name = udata["profile"].get("real_name") #dprint ("got user: %s \t id: %s" % (u_name, uid)) #dprint ("%s" % str(udata)) for user in users: if 'name' in users[user] and users[user].get( 'name') == bot_name: bot_id = users[user]["profile"].get('bot_id', user) #dprint("Bot ID for '" + users[user]['name'] + "' is " + bot_id) #dprint("Bot Record: %s" % str(users[user])) return bot_id dprint("could not find bot user with the name " + bot_name) return '' def auth(self, code): """ Authenticate with OAuth and assign correct scopes. Save a dictionary of authed team information in memory on the bot object. Parameters ---------- code : str temporary authorization code sent by Slack to be exchanged for an OAuth token """ # After the user has authorized this app for use in their Slack team, # Slack returns a temporary authorization code that we'll exchange for # an OAuth token using the oauth.access endpoint dprint("\ncalling auth client.api_call\n") auth_response = self.client.api_call( "oauth.access", client_id=self.oauth["client_id"], client_secret=self.oauth["client_secret"], code=code) dprint("\ncclient.api_call returns %s\n" % (str(auth_response), )) # To keep track of authorized teams and their associated OAuth tokens, # we will save the team ID and bot tokens to the global # authed_teams object team_id = auth_response["team_id"] authed_teams[team_id] = { "bot_token": auth_response["bot"]["bot_access_token"] } # Then we'll reconnect to the Slack Client with the correct team's # bot token self.client = SlackClient(authed_teams[team_id]["bot_token"]) def open_dm(self, user_id): """ Open a DM to send a welcome message when a 'team_join' event is recieved from Slack. Parameters ---------- user_id : str id of the Slack user associated with the 'team_join' event Returns ---------- dm_id : str id of the DM channel opened by this method """ new_dm = self.client.api_call("im.open", user=user_id) dprint("\nOpen DM to user %s channel %s\n" % (user_id, str(new_dm["channel"]))) #dprint ("\nnew_dm: %s\n" % str(new_dm)) dm_id = new_dm["channel"]["id"] return dm_id def get_message_object(self, team_id, user_id, message_type=None): # We've imported a Message class from `message.py` that we can use # to create message objects for each onboarding message we send to a # user. We can use these objects to keep track of the progress each # user on each team has made getting through our onboarding tutorial. # First, we'll check to see if there's already messages our bot knows # of for the team id we've got. if self.messages.get(team_id): # Then we'll update the message dictionary with a key for the # user id we've recieved and a value of a new message object self.messages[team_id].update( {user_id: message.Message(message_type=message_type)}) else: # If there aren't any message for that team, we'll add a dictionary # of messages for that team id on our Bot's messages attribute # and we'll add the first message object to the dictionary with # the user's id as a key for easy access later. self.messages[team_id] = { user_id: message.Message(message_type=message_type) } message_obj = self.messages[team_id][user_id] # Then we'll set that message object's channel attribute to the DM # of the user we'll communicate with message_obj.channel = self.open_dm(user_id) return message_obj def help_message(self, team_id, user_id, message_text): """ Create and send an onboarding welcome message to new users. Save the time stamp of this message on the message object for updating in the future. Parameters ---------- team_id : str id of the Slack team associated with the incoming event user_id : str id of the Slack user associated with the incoming event """ message_type = "helpmsg" message_obj = self.get_message_object(team_id, user_id, message_type=message_type) message_obj.text = message_text post_message = self.client.api_call( "chat.postMessage", channel=message_obj.channel, username=self.name, icon_emoji=self.emoji, text=message_obj.text, ) timestamp = post_message["ts"] # We'll save the timestamp of the message we've just posted on the # message object which we'll use to update the message after a user # has completed an onboarding task. message_obj.timestamp = timestamp def git_usage_message(self): return ("""I'm sorry. I don't understand your git command. I understand git [%s] if you would like to try one of those. Commit also requires a -m commit_message""" % "|".join(GIT_SUPPORTED)) def git_handler(self, team_id, user_id, incoming_text): """ Get the git status of this project. Parameters ---------- team_id : str id of the Slack team associated with the incoming event user_id : str id of the Slack user associated with the incoming event incoming_text: str The git request string """ message_type = "git_handler" message_obj = self.get_message_object(team_id, user_id, message_type) # Find the action immediately following the git command. # git status, git add, git commit are curently supported. p2 = re.compile(r"(?<=\bgit\s)(\w+)") match_obj = p2.search(incoming_text) if match_obj: git_action = match_obj.group() bad_command = False if git_action and git_action in GIT_SUPPORTED: arg_string = '' print("git action is %s" % (git_action, )) if git_action == 'commit': # We need a commit message flagged with -m flag_pos = incoming_text.find("-m") if flag_pos >= 0: arg_string = "-m '%s'" % incoming_text[flag_pos + len("-m"):] else: # Can't do a commit without a commit message bad_command = True elif git_action == 'add': # We only commit the whole directory arg_string = " ." elif git_action == 'push' or git_action == 'status': # No args for these arg_string = "" else: bad_command = True if not bad_command: p = subprocess.Popen('git %s %s' % (git_action, arg_string), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) for line in p.stdout.readlines(): message_obj.text += "%s" % line retval = p.wait() else: # git_action undefined. message_obj.text = self.git_usage_message() retval = 1 else: retval = 1 post_message = self.client.api_call( "chat.postMessage", channel=message_obj.channel, username=self.name, icon_emoji=self.emoji, text=message_obj.text, ) if "ts" in post_message: timestamp = post_message["ts"] message_obj.timestamp = timestamp else: dprint("post_message: %s" % str(post_message)) return retval def docker_handler(self, team_id, user_id, incoming_text): """ Get the docker command and handle it. Parameters ---------- team_id : str id of the Slack team associated with the incoming event user_id : str id of the Slack user associated with the incoming event incoming_text: str The docker request string """ message_type = "docker_handler" message_obj = self.get_message_object(team_id, user_id, message_type) # Find the action immediately following the docker command. # docker container ls, docker image ls are curently supported. docker_action = docker_parser.parse_command(incoming_text) dprint("Got docker_action: %s from incoming text: %s" % (docker_action, incoming_text)) if docker_action[:len('docker')] == 'docker': dprint("running docker action: %s" % (docker_action)) p = subprocess.Popen('%s' % docker_action, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) for line in p.stdout.readlines(): message_obj.text += "%s" % line retval = p.wait() else: message_obj.text = docker_action # The usage message retval = 0 post_message = self.client.api_call( "chat.postMessage", channel=message_obj.channel, username=self.name, icon_emoji=self.emoji, text=message_obj.text, ) timestamp = post_message["ts"] return retval def get_circleci_repos(self): """ Get the circleci repositories that the bot user is following returns a list of repo names """ project_info_list = self.circleci_client.projects.list_projects() for project_dict in project_info_list: self.circleci_repo_list.append(project_dict["reponame"]) def circleci_build_info(self, raw_results): """ Returns selected circleci information about the last build. Input is raw_results from a call to circleci build.recent """ if not isinstance(raw_results, dict): dprint("build info: Not a dict: %s" % (str(raw_results), )) return None else: rr = raw_results build_version = rr.get("platform", "Not found") build_url = rr.get("build_url", "Not found") commit_date = rr.get("committer_date", "Not found") author_name = rr.get("author_name", "Not found") build_num = rr.get("build_num", "Not found") outcome = rr.get("outcome", "Not found") # There may be several commits in this build committer_names = [] commit_urls = [] commit_subjects = [] commit_dates = [] for commit_detail in rr.get("all_commit_detail", []): committer_names.append(rr.get("committer_name", "Not found")) commit_urls.append(rr.get("commit_url", "Not found")) commit_subjects.append(rr.get("subject", "Not found")) commit_dates.append(rr.get("committer_date", "Not found")) # Should html'ify this for return results_string = """ Build Version: %(build_version)s\n Build URL: %(build_url)s\n Commit Date: %(commit_date)s\n Author Name: %(author_name)s\n Build Number: %(build_num)s\n Outcome: %(outcome)s""" % locals() for commit_subject, commiter_name, commit_url, commiter_date in list( zip(commit_subjects, committer_names, commit_urls, commit_dates)): results_string = results_string + """ Commit Subject: %s\n Committer Name: %s\n Commit Date: %s\n Commit URL: %s\n """ % (commit_subject, commier_name, commiter_date, commit_url) return results_string def circleci_handler(self, team_id, user_id, incoming_text): #my_repo_name = "HB_Littlebot" self.get_circleci_repos() repo_name = '' outgoing_text = "Sorry, I could not determine which repo you wanted.\n" \ "I know about these: %s" % (str(", ".join(self.circleci_repo_list),)) message_type = "helpmsg" if self.circleci_repo_list: # Retrieve User data user_info = self.circleci_client.user.info() dprint("\nUSER info: %s\n" % (str(user_info), )) user_name = user_info['login'] incoming_text = incoming_text.lower() for repo in self.circleci_repo_list: if incoming_text.find(repo.lower()) >= 0: repo_name = repo if repo_name: if incoming_text.find("last build") >= 0: last_build = self.circleci_client.build.recent( user_name, repo_name, branch='master') if last_build: outgoing_text = self.circleci_build_info(last_build[0]) print("build_info: %s" % (str(last_build), )) message_type = "circleci_last_build" else: message_type = "circleci_handler" outgoing_text = "Sorry, I could not get that information.\n" \ "I know about these repos: %s" % (str(", ".join(self.circleci_repo_list),)) message_type = "helpmsg" dprint("circleci_handler returning %s" % outgoing_text) message_obj = self.get_message_object(team_id, user_id, message_type=message_type) message_obj.text = outgoing_text post_message = self.client.api_call( "chat.postMessage", channel=message_obj.channel, username=self.name, icon_emoji=self.emoji, text=message_obj.text, ) timestamp = post_message["ts"] # We'll save the timestamp of the message we've just posted on the # message object which we'll use to update the message after a user # has completed an onboarding task. message_obj.timestamp = timestamp """ # Retrieve information about projects project_info_list = self.circleci_client.projects.list_projects() #dprint ("\nPROJECT info: %s\n" % (str(project_info),)) for project_dict in project_info_list: my_repo_name = "HB_Littlebot" if project_dict["reponame"] == my_repo_name: dprint ("\nPROJECT info: %s\n" % (str(project_dict),)) repo_name = project_dict["reponame"] dprint ("searching for %s" % repo_name) # Retrieve last 10 builds of branch master recent_builds = self.circleci_client.build.recent(user_name, repo_name, branch='master') for key, val in recent_builds[0].items(): dprint ("key:%s\n\t%s" % (key,val)) """ def echo_message(self, team_id, user_id, incoming_text): """ Create and send an onboarding welcome message to new users. Save the time stamp of this message on the message object for updating in the future. Parameters ---------- team_id : str id of the Slack team associated with the incoming event user_id : str id of the Slack user associated with the incoming event """ message_type = "echo_message" message_obj = self.get_message_object(team_id, user_id, message_type) # We'll use the message object's method to create the attachments that # we'll want to add to our Slack message. This method will also save # the attachments on the message object which we're accessing in the # API call below through the message object's `attachments` attribute. #message_obj.create_attachments() user_name = self.user_name_map[user_id]['profile']['display_name'] dprint("IN Echo handler with message: %s" % (incoming_text, )) outgoing_text = "Hi %s. You said %s." % (user_name, incoming_text) if incoming_text.lower().find("hunka hunka") >= 0: outgoing_text += " Yes, I have a burning love for bots too." dprint("Looking in %s for id %s" % (self.user_name_map[user_id]['profile'], user_id)) message_obj.text = outgoing_text post_message = self.client.api_call( "chat.postMessage", channel=message_obj.channel, username=self.name, icon_emoji=self.emoji, text=message_obj.text, ) timestamp = post_message["ts"] # We'll save the timestamp of the message we've just posted on the # message object which we'll use to update the message after a user # has completed an onboarding task. message_obj.timestamp = timestamp def update_pin(self, team_id, user_id): """ Update onboarding welcome message after recieving a "pin_added" event from Slack. Update timestamp for welcome message. Parameters ---------- team_id : str id of the Slack team associated with the incoming event user_id : str id of the Slack user associated with the incoming event """ # These updated attachments use markdown and emoji to mark the # onboarding task as complete completed_attachments = { "text": ":white_check_mark: " "~*Pin this message*~ " ":round_pushpin:", "color": "#439FE0" } # Grab the message object we want to update by team id and user id #message_obj = self.messages[team_id].get(user_id) # Grab the message object we want to update by team id and user id if team_id in self.messages and user_id in self.messages[team_id]: message_obj = self.messages[team_id].get(user_id) else: message_type = "update_pin" message_obj = self.get_message_object(team_id, user_id, message_type) # Update the message's attachments by switching in incomplete # attachment with the completed one above. message_obj.pin_attachment.update(completed_attachments) # Update the message in Slack post_message = self.client.api_call( "chat.update", channel=message_obj.channel, ts=message_obj.timestamp, text=message_obj.text, attachments=message_obj.attachments) # Update the timestamp saved on the message object message_obj.timestamp = post_message["ts"] def update_share(self, team_id, user_id): """ Update onboarding welcome message after recieving a "message" event with an "is_share" attachment from Slack. Update timestamp for welcome message. Parameters ---------- team_id : str id of the Slack team associated with the incoming event user_id : str id of the Slack user associated with the incoming event """ # These updated attachments use markdown and emoji to mark the # onboarding task as complete completed_attachments = { "text": ":white_check_mark: " "~*Share this Message*~ " ":mailbox_with_mail:", "color": "#439FE0" } # Grab the message object we want to update by team id and user id #message_obj = self.messages[team_id].get(user_id) # Grab the message object we want to update by team id and user id if team_id in self.messages and user_id in self.messages[team_id]: message_obj = self.messages[team_id].get(user_id) else: message_type = "update_share" message_obj = self.get_message_object(team_id, user_id, message_type) # Update the message's attachments by switching in incomplete # attachment with the completed one above. message_obj.share_attachment.update(completed_attachments) # Update the message in Slack post_message = self.client.api_call( "chat.update", channel=message_obj.channel, ts=message_obj.timestamp, text=message_obj.text, attachments=message_obj.attachments) # Update the timestamp saved on the message object message_obj.timestamp = post_message["ts"] def send_message(self, team_id, user_id, message_text): """ Send a message on the part of the app for something that doesn't have explicit handling. Parameters ---------- team_id : str id of the Slack team associated with the incoming event user_id : str id of the Slack user associated with the incoming event message : A text message to send to the caller. """ # Grab the message object we want to update by team id and user id #message_obj = self.messages[team_id].get(user_id) # Grab the message object we want to update by team id and user id if team_id in self.messages and user_id in self.messages[team_id]: message_obj = self.messages[team_id].get(user_id) else: message_type = "send_message" message_obj = self.get_message_object(team_id, user_id, message_type) # Update the message in Slack message_obj.text = message_text post_message = self.client.api_call( "chat.update", channel=message_obj.channel, ts=message_obj.timestamp, text=message_obj.text, ) # Update the timestamp saved on the message object message_obj.timestamp = post_message["ts"]
import os from slackclient import SlackClient # globals for use throughout script BOT_NAME = 'vroomba' # this should be pulled from an environment variable, check readme for deets slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN')) if __name__ == "__main__": api_call = slack_client.api_call("users.list") if api_call.get('ok'): # retrieve all users so we can find the bot users = api_call.get('members') for user in users: if user.get('name') == BOT_NAME: print("Bot ID for " + user['name'] + " is " + user.get('id')) else: print("not " + BOT_NAME + ", hiding ID...")
class DispatcherBot(RedisHash): _slack = None def __init__(self, name='dispatcher'): super().__init__(namespace='slack', prefix='bot', key=name) self.attrib_lists.update( dict(string=['api_key', 'name'], bool=[], int=[], list=[], set=['watching_events', 'watching_users', 'messages'], path=[])) self.__namespaceinit__() if not self or not self.name: self.__bindattrs__() self.name = name if self._slack is None: self._slack = SlackClient(ET_SLACK_IDENTIFIER) if not self.watching_events: self.watching_events.extend(['presence_change', 'message']) def start(self): if self._slack.rtm_connect(): while True: events = self._slack.rtm_read() if events: self.handle_events(events) gevent.sleep(2) else: logger.error('Connection Failed!') def handle_events(self, events): events = [e for e in events if e['type'] in self.watching_events] for event in events: method = '_{}_handler'.format(event['type']) handler = getattr(self, method) try: handler(event) except Exception as err: logger.exception(err) def _presence_change_handler(self, event): if 'active' != event['presence']: return if not self.watching_users or event['user'] not in self.watching_users: return elif not self.messages: return for msg_id in self.messages: msg = DispatcherBotMessage(msg_id) if msg.delivered or not msg.to_users or event[ 'user'] not in msg.to_users: continue channel = self._slack.api_call('im.open', user=event['user']) if self._slack.rtm_send_message(channel, msg.content): msg.to_users.remove(event['user']) self.watching_users.remove(event['user']) if not msg.to_users: msg.delivered = True
response = random_response() url = get_youtube_url(split_command[1].strip()) response = "{0}\n\n{1}".format(response, url) break else: response = "You are missing the all important punctuation fool." logging.info("{0}: {1}".format(command, response)) # Sends the response back to the channel CLIENT.api_call("chat.postMessage", channel=channel, text=response, as_user=True) # end def handle_command if __name__ == "__main__": if 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 = CLIENT.api_call("auth.test")["user_id"] while True: command, channel = parse_bot_commands(CLIENT.rtm_read()) if command: handle_command(command, channel) time.sleep(RTM_READ_DELAY) else: print("Connection failed. Exception traceback printed above.")
class Bot: def __init__(self, token, name="beaker", verbose=False): self.token = token self.name = name self.plugins = [] self.module_objects = [] self.cron_plugins = [] self.tag = "@" self.user_id = None self.verbose = verbose self.admins = defaultdict(dict) self.scheduler = BackgroundScheduler() try: self.sc = SlackClient(self.token) except Exception as e: print(e) sys.exit() self.get_plugins() self.setup_cron_jobs() self.read_admins() atexit.register(self.save_admins) def read_admins(self): try: with open("admins.json", "r") as fp: self.admins = json.load(fp) except (JSONDecodeError, FileNotFoundError): pass def get_plugins(self, reload_modules=False): modules = [ file for file in os.listdir("plugins") if file.endswith(".py") and file != "base.py" and not file.startswith("__") ] if not reload_modules: for module in modules: self.module_objects.append( import_module("plugins." + module.split(".")[0])) else: reload(plugins.base) new_modules = [] for module in self.module_objects: new_module = reload(module) new_modules.append(new_module) self.module_objects.clear() self.module_objects = new_modules self.plugins.clear() invalidate_caches() if self.verbose: print(plugins.base.Plugin.__subclasses__()) for subclass in plugins.base.Plugin.__subclasses__(): _klass = subclass() self.plugins.append(_klass) if hasattr(_klass, "CRON"): self.cron_plugins.append(_klass) if self.verbose: print("module objects: {}".format(self.module_objects)) print("plugins: {}".format(self.plugins)) print("Cron plugins: {}".format(self.cron_plugins)) def setup_cron_jobs(self): self.scheduler.start() for plugin in self.cron_plugins: self.scheduler.add_job(plugin.cron, CronTrigger.from_crontab(plugin.CRON), jitter=60 * 10) def help(self, params): # No params, general beaker help if not params: return { "text": "Beaker bot help. You're on your own for now, Ain't nobody got time for writing help" } for module in self.plugins: if params in module.commands: return module.help() # Module not found else: return {"text": "could not find module {}".format(params)} def call_plugin(self, command, params=""): for plugin in self.plugins: if command in plugin.commands: data = plugin.message_recieved(plugin, params) return data else: return { "text": "Could not find a plugin for command {}".format(command) } def show_commands(self): commands = [] for module in self.plugins: commands.extend([c for c in module.commands]) # TODO: These internal commands should be defined on the class object commands.extend(["help", "say", "whatdayisit", "refresh"]) help_message = "Available commands: {}".format(", ".join(sorted(commands))) \ if commands else "No commands available" return {"text": help_message} def handle_message(self, message): # Ignore messages from the bot itself if message.get("user", "") == self.user_id: return if not message.get("text"): return if self.verbose: print(message) # The first character for the first word should be our call tag. If not us, return if message["text"][0] != self.tag: return # Split message into individual words split = message["text"].split(" ") # The command is the word that follows the tag command = split[0][1:] # The params to the command are any words that follow the command params = " ".join(split[1:]) data = {} # First look for the command in our internal handlers if command == "help": data = self.help(params) elif command == "whatdayisit": data["text"] = datetime.datetime.now().strftime('%A') elif command == "say": data["text"] = " ".join(split[1:]) elif command == "summon": # TODO: find a neater way to allow a method to delete a message so we can put this somewhere else self.sc.api_call("chat.delete", channel=message["channel"], ts=message["ts"]) data["text"] = "@" + " @".join(split[1:]) data["username"] = "******" data[ "icon_url"] = "http://seriousmovielover.com/wordpress/wp-content/uploads/2009/10/python2.jpg" data["as_user"] = False elif command == "commands": data = self.show_commands() elif command == "refresh": self.get_plugins(True) data["text"] = "successfully refreshed plugins" elif command == "register": self.register(message["user"], params) # Not found in internal commands, look for plugin to call else: data = self.call_plugin(command, params) if isinstance(data, str): data = {"text": data} data.setdefault("name", self.name) data.setdefault("as_user", True) if data: self.sc.api_call("chat.postMessage", channel=message["channel"], **data) def register(self, user, params): password = params if user in self.admins: print(self.admins) if self.verify_password(self.admins[user]["password"], password): print("user verified") else: print("incorrect password") else: self.admins[user]["password"] = self.hash_password(password) self.admins[user]["authed"] = True print("created new user with password {}".format( self.admins[user]["password"])) print(self.admins) def hash_password(self, password): """Hash a password for storing.""" salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii') pwdhash = hashlib.pbkdf2_hmac('sha512', password.encode('utf-8'), salt, 100000) pwdhash = binascii.hexlify(pwdhash) return (salt + pwdhash).decode('ascii') def verify_password(self, stored_password, provided_password): """Verify a stored password against one provided by user""" salt = stored_password[:64] print(stored_password) stored_password = stored_password[64:] pwdhash = hashlib.pbkdf2_hmac('sha512', provided_password.encode('utf-8'), salt.encode('ascii'), 100000) pwdhash = binascii.hexlify(pwdhash).decode('ascii') return pwdhash == stored_password def connect(self): if self.sc.rtm_connect(auto_reconnect=True): self.user_id = self.sc.server.login_data["self"]["id"] while 1: for message in self.sc.rtm_read(): self.handle_message(message) def run(self): self.connect() def save_admins(self): for user in self.admins.keys(): self.admins[user]["authed"] = False with open("admins.json", "w") as fp: json.dump(self.admins, fp)
class SlackEvents(object): def __init__(self, slack_token, ui_url, bot_user, activity_channel, map_user): self.sc = SlackClient(slack_token) self.ui_url = ui_url self.bot_user = bot_user self.activity_channel = activity_channel self.map_user = map_user self.channel_template = "mi-{}" def get_channel_name(self, case_id): return self.channel_template.format(case_id)[:21] def lookup_slack_channel_id(self, channel_name): # TODO: conditional caching based on output response = self.sc.api_call( "channels.list" ) if not response['ok']: raise Exception( "Error while looking up channel ID: {}".format(response)) for channel in response['channels']: if channel['name'] == channel_name: return channel['id'] return None def lookup_slack_user_id(self, username): # TODO: conditional caching based on output response = self.sc.api_call( "users.list" ) if not response['ok']: raise Exception( "Error while looking up user ID: {}".format(response)) for member in response['members']: if member['name'] == username: return member['id'] return None def get_case_url(self, case_id): url_template = "{}/#/cases/{{}}".format(self.ui_url) return url_template.format(case_id) def case_created(self, event): response = self.sc.api_call( "channels.create", name=self.get_channel_name(event['case']['id']) ) if not response['ok']: logging.critical("Error while creating channel: %s", response) return normalized_name = response['channel']['name_normalized'] channel_id = response['channel']['id'] logging.info("Created channel '%s' in Slack", normalized_name) # Set channel purpose response = self.sc.api_call( "channels.setPurpose", channel=channel_id, purpose="Workspace for McAfee Investigator case '{}'. " "Events related to this case will be posted here. ".format( event['case']['name']) ) if not response['ok']: logging.warning("Unable to set channel purpose: %s", response) slack_username = self.map_user(event['user']) slack_user_id = self.lookup_slack_user_id(slack_username) if slack_user_id: response = self.sc.api_call( "channels.invite", channel=channel_id, user=slack_user_id ) if not response['ok']: logging.warning( "Unable to invite user to channel: %s", response) else: logging.warning("User with handle '%s' not found", slack_username) response = self.sc.api_call( "chat.postMessage", channel=channel_id, as_user=False, link_names=True, parse="full", username=self.bot_user, attachments=[ { "title": "Case information", "fallback": "Case URL: {}".format( self.get_case_url(event['case']['id'])), "fields": [ { "title": "Name", "value": event['case']['name'] }, { "title": "ID", "value": event['case']['id'] }, { "title": "Reported by", "value": event['user'] }, ], "actions": [ { "type": "button", "text": "Open in McAfee Investigator", "url": self.get_case_url(event['case']['id']) } ] } ] ) if not response['ok']: logging.warning("Unable to post basic case information: %s", response) # Mention the new case on the general channel activity_channel_id = self.lookup_slack_channel_id( self.activity_channel) if not activity_channel_id: logging.warning("Channel with name '%s' not found", self.activity_channel) return response = self.sc.api_call( "chat.postMessage", channel=activity_channel_id, as_user=False, link_names=True, parse="full", username=self.bot_user, text="New channel #{} created for McAfee Investigator case `{}`. " "@{} has been invited to join it".format( normalized_name, event['case']['name'], slack_username) ) if not response['ok']: logging.warning( "Unable to mention case creation on general channel: %s", response) def case_priority_updated(self, event): channel_id = self.lookup_slack_channel_id( self.get_channel_name(event['case']['id'])) if not channel_id: logging.warning( "Channel with name '%s' not found", event['case']['id']) return response = self.sc.api_call( "chat.postMessage", channel=channel_id, as_user=False, link_names=True, parse="full", username=self.bot_user, attachments=[ { 'title': "Priority update", 'text': priority_mapping[event['case']['priority']], 'color': color_mapping[event['case']['priority']] } ] ) if not response['ok']: logging.warning("Unable to post priority update: %s", response) def case_status_updated(self, event): channel_id = self.lookup_slack_channel_id( self.get_channel_name(event['case']['id'])) if not channel_id: logging.warning( "Channel with name '%s' not found", event['case']['id']) return response = self.sc.api_call( "chat.postMessage", channel=channel_id, as_user=False, link_names=True, parse="full", username=self.bot_user, attachments=[ { 'title': "Status update", 'text': status_mapping[event['case']['status']], 'color': color_mapping['info'] } ] ) if not response['ok']: logging.warning("Unable to post status update: %s", response) def case_selected_ui(self, event): """A user started looking at a case in the UI """ channel_id = self.lookup_slack_channel_id( self.get_channel_name(event['case']['id'])) if not channel_id: logging.warning("Channel with name '%s' not found", event['case']['id']) return response = self.sc.api_call( "chat.postMessage", channel=channel_id, as_user=False, link_names=True, parse="full", username=self.bot_user, attachments=[ { 'title': "Time spent in case", 'text': 'User {} is currently looking at this case'.format( event['user']), 'color': color_mapping['info'] } ] ) if not response['ok']: logging.warning("Unable to post status update: %s", response) def case_unselected_ui(self, event): """A user stopped looking at a case in the UI """ channel_id = self.lookup_slack_channel_id( self.get_channel_name(event['case']['id'])) if not channel_id: logging.warning( "Channel with name '%s' not found", event['case']['id']) return def map_time(seconds): return "{} seconds".format(seconds) response = self.sc.api_call( "chat.postMessage", channel=channel_id, as_user=False, link_names=True, parse="full", username=self.bot_user, attachments=[ { 'title': "Time spent in case", 'text': 'User {} spent {} in this case'.format( event['user'], map_time( event['case'] ['investigation-time']['elapsed-time'])), 'color': color_mapping['info'] } ] ) if not response['ok']: logging.warning("Unable to post status update: %s", response)
class Bot(object): """ Instanciates a Bot object to handle Slack onboarding interactions.""" def __init__(self): super(Bot, self).__init__() self.name = "simplebot" self.targetChannel = "" # When we instantiate a new bot object, we can access the app # credentials we set earlier in our local development environment. self.oauth = { "client_id": os.environ.get("SLACK_CLIENT_ID"), "client_secret": os.environ.get("SLACK_CLIENT_SECRET"), # Scopes provide and limit permissions to what our app # can access. It's important to use the most restricted # scope that your app will need. "scope": "bot" } self.verification = os.environ.get("SLACK_VERIFICATION_TOKEN") # NOTE: Python-slack requires a client connection to generate # an oauth token. We can connect to the client without authenticating # by passing an empty string as a token and then reinstantiating the # client with a valid OAuth token once we have one. # self.client = SlackClient(os.environ.get("SLACK_BOT_TOKEN")) self.client = SlackClient("") # We'll use this dictionary to store the state of each message object. # In a production envrionment you'll likely want to store this more # persistantly in a database. self.messages = {} def auth(self, code): """ Authenticate with OAuth and assign correct scopes. Save a dictionary of authed team information in memory on the bot object. Parameters ---------- code : str temporary authorization code sent by Slack to be exchanged for an OAuth token """ # After the user has authorized this app for use in their Slack team, # Slack returns a temporary authorization code that we'll exchange for # an OAuth token using the oauth.access endpoint auth_response = self.client.api_call( "oauth.access", client_id=self.oauth["client_id"], client_secret=self.oauth["client_secret"], code=code) # Save the bot token to an environmental variable or to your data store # for later use os.environ["SLACK_USER_TOKEN"] = auth_response['access_token'] os.environ["SLACK_BOT_TOKEN"] = auth_response['bot'][ 'bot_access_token'] # To keep track of authorized teams and their associated OAuth tokens, # we will save the team ID and bot tokens to the global # authed_teams object team_id = auth_response["team_id"] authed_teams[team_id] = { "bot_token": auth_response["bot"]["bot_access_token"] } # Then we'll reconnect to the Slack Client with the correct team's # SLACK_BOT_TOKEN self.client = SlackClient(authed_teams[team_id]["bot_token"])
import os from slackclient import SlackClient slack_token = os.environ["SLACK_API_TOKEN"] sc = SlackClient(slack_token) # Get the group channels with the following # for group in sc.api_call("groups.list").get("groups"): # print("{}-{}".format(group['id'],group['name'])) sc.api_call( "chat.postMessage", channel="GDZULG3U0", text="Hello from Python! :tada:" )
class Slack_bot(SlackClient): """slack_bot class.""" def __init__(self, token, twit_channel, home_channel, bot_id=None): self.sc = SlackClient(token) self.channel = twit_channel self.home = home_channel self.start_time = datetime.datetime.now() self.bot_id = bot_id if not self.bot_id and self.sc.rtm_connect(with_team_state=False): response = self.sc.api_call('auth.test') self.name = response.get('user') self.bot_id = response.get('user_id') self.at_bot = '<@' + self.bot_id + '>' def __enter__(self): """returns slack obj and connects to rtm if not.""" mess = "PBJTIME is ONLINE!!!! (with a baseball bat)" if self.sc.server.connected: logger.info('SlackBot connected to rtm stream') else: logger.info('SlackBot connected to rtm stream') self.sc.rtm_connect(with_team_state=False) self.post_command_message(mess, self.home) return self def __exit__(self, type, value, traceback): """lets program know that it is exiting slackbot.""" logger.info('Exiting slack_bot') def read_stream(self): """reads stream from slack_client connections.""" return self.sc.rtm_read() def parse_stream(self, content): """takes in content from stream and looks for pbjtime mentions.""" for item in content: if 'text' in item and item['text'].startswith(self.at_bot): text = item['text'].split(self.at_bot) chan = item['channel'] return (text[1].strip(), chan) return (None, None) def handle_command(self, text, tb): """handles commands that are given and returns message to post.""" global subscr global stats args = text.lower().split() if args: cmd = args[0].lower() logger.info('{} cmd was issued.'.format(cmd)) else: cmd = '' args = args[1:] if cmd == 'raise': logger.info('raise test exception') raise TestException elif cmd == 'help': return 'these commands are possible:\n\ {}'.format(pp.pformat(bot_commands)) elif cmd == 'time': logger.info('bot initialized in slack.') return "IT'S PEANUT BUTTER JELLY TIME!! \n(help for more options)" elif cmd == 'ping': uptime = datetime.datetime.now() - self.start_time logger.info('current uptime: {}'.format(uptime)) return 'Peanut Butter Jelly upTime: {}'.format(uptime) elif cmd == 'exit': tb.close_stream() subscr = [] tb.subscriptions = [] stats = {} logger.info('pbjtime leaving slack.') return "peanut butter jelly time :'( (goodbye)" elif cmd == 'start': subscr = list(set(subscr + args)) if not subscr: logger.info('no subscr. ignoring and not starting stream.') return 'Please add subscriptions so I can find tweets.' tb.init_stream(subscr) logger.info('started stream with subscriptions: {}'.format(subscr)) for subs in subscr: if subs not in stats: stats[subs] = 0 if args: return 'Added subscriptions: {}'.format(args) return 'Started with subcriptions: {}'.format(subscr) elif cmd == 'add': subscr = list(set(subscr + args)) if not subscr or not args: logger.info('no new subscriptions. ignoring') return 'Please add new subscriptions so I can find tweets.' tb.init_stream(subscr) for subs in subscr: if subs not in stats: stats[subs] = 0 logger.info('added new subcriptions; restarting stream.') return 'Added subscriptions: {}'.format(args) elif cmd == 'remove': removed = [] for arg in args: if arg in subscr: subscr.remove(arg) removed.append(arg) if arg in stats: del stats[arg] if removed: logger.info('removed subcriptions: {}'.format(arg)) tb.init_stream(subscr) logger.info('restarted twitter stream.') return 'removed subcriptions: {} and restarted.'.format(arg) else: logger.info('no subscriptions matching input. ignoring') return 'No subscriptions removed. use list to see current.' elif cmd == 'removeall': subscr = [] tb.close_stream() tb.subscriptions = [] stats = {} logger.info('All subscriptions removed') return 'all subscriptions removed!' elif cmd == 'list': logger.info('channel list: {}'.format(subscr)) return 'current subscriptons: \n {}'.format(subscr) elif cmd == 'stop': logger.info('Stopping twitter stream') tb.close_stream() logger.info('stream closed on slack side.') return 'Twitter stream has been stopped.' elif cmd == 'channels': self.channel_list() elif cmd == 'stats': logger.info('stats: {}'.format(pp.pformat(stats))) return 'subscription stats: {}'.format(pp.pformat(stats)) elif cmd not in bot_commands: logger.info('unknown command issued') return 'Peanut Butter Jelly Time??? use help for more options.' else: logger.warning('made it through if else block: {}'.format(cmd)) return None def post_command_message(self, mess, channel): """posts message after command is completed.""" logger.info('Sent response to channel: {}'.format(channel)) self.sc.rtm_send_message(channel, mess) def post_twit_mess(self, mess): """Posts message from twitter bot to initial channel.""" global stats global subscr for scr in stats: if scr in mess.lower(): stats[scr] += 1 self.sc.api_call("chat.postMessage", channel=self.channel, text=mess) def channel_list(self): logger.info('requesting channel list') logger.info(pp.pformat(self.sc.api_call("channels.list")))
from slackclient import SlackClient slack_token = os.environ['xoxp-403424165366-403181525847-421195613985-7594c85ab117e23784fdff76fe3120d0'] sc = SlackClient(slack_token) sc.api_call( "chat.postMessage", channel="#modules-exercise", text="Hello from Python! :tada:" )