Skip to content
This repository has been archived by the owner on Jun 19, 2019. It is now read-only.

Commit

Permalink
Merge pull request #1 from AllenAnthes/app-migration
Browse files Browse the repository at this point in the history
App migration
  • Loading branch information
AllenAnthes authored Dec 15, 2017
2 parents 6009711 + 8ee71f2 commit 0b30a2e
Show file tree
Hide file tree
Showing 13 changed files with 694 additions and 68 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
credentials.json
creds.py


# remove pycharm ide files
.idea/

Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions archived/working_simple_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import websocket
from urllib3 import disable_warnings, exceptions # allow to disable InsecureRequestWarning, not sure if needed

from src.creds import TOKEN # locally saved file "creds.py" this is added to .gitignore
from src.message import new_join
# from src.creds import TOKEN # locally saved file "creds.py" this is added to .gitignore
from archived.message import new_join
from utils.log_manager import setup_logging

# any file can get instance of logger.
Expand Down
Binary file modified requirements.txt
Binary file not shown.
5 changes: 3 additions & 2 deletions run.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
from src.app import run_bot

from src.flask_endpoint import start_server

if __name__ == '__main__':
run_bot(delay=1)
# run_bot(delay=1)
start_server()
52 changes: 52 additions & 0 deletions src/airtable_handling.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import json
from pprint import pprint

import requests
from airtable import Airtable
from decouple import config

BASE_KEY = config('PERSONAL_AIRTABLE_BASE_KEY')
API_KEY = config('PERSONAL_AIRTABLE_TOKEN')
TABLE_NAME = 'Mentor Request'



def get_table(table):
airtable = Airtable(BASE_KEY, table, api_key=API_KEY)
res = airtable.get_all()
pprint(res)




def test():
params = {
'fields': {
'Slack User': 'Allen2',
'Email': 'email@email.com',
'Service': ['recBxmDasLXwmVB78'],
'Skillsets': ["Web (Frontend Development)"],
'Additional Details': 'Details'}
}

headers = {
'authorization': "Bearer " + API_KEY
}
res = requests.post("https://api.airtable.com/v0/app2p4KgQKx35WoCm/Mentor%20Request", json=params,
headers=headers)
print(res.content)


if __name__ == '__main__':
# test()
get_table('Services')


services_records = {
'General Guidance - Slack Chat': 'recBxmDasLXwmVB78',
'General Guidance - Voice Chat': 'recDyu4PMbPl7Ti58',
'Pair Programming': 'recHCFAO9uNSy1WDs',
'Code Review': 'recUK55xJXOfAaYNb',
'Resume Review': 'recXZzUduWfaxWvSF',
'Mock Interview': 'recdY4XLeN1CPz1l8'
}
227 changes: 179 additions & 48 deletions src/app.py
Original file line number Diff line number Diff line change
@@ -1,99 +1,223 @@
import json
import logging
import time
from slackclient import SlackClient
from utils.log_manager import setup_logging
# from src.creds import TOKEN, PROXY
from decouple import config
import traceback
from pprint import pprint
import requests

from src.help_menu import HELP_MENU_RESPONSES
from src.messages import *

# from src.airtable_handling import airtable

logger = logging.getLogger(__name__)
new_event_logger = logging.getLogger(f'{__name__}.new_member')
all_event_logger = logging.getLogger(f'{__name__}.all_events')

# constants
MESSAGE = (
"Hi {real_name},\n\n Welcome to Operation Code! I'm a bot designed to help answer questions and get you on your way in our community.\n\n"
"Please take a moment to review our <https://op.co.de/code-of-conduct|Code of Conduct.>\n\n"
"Our goal here at Operation Code is to get veterans and their families started on the path to a career in programming. "
"We do that through providing you with scholarships, mentoring, career development opportunities, conference tickets, and more!\n\n"
"You're currently in Slack, a chat application that serves as the hub of Operation Code. "
"If you're currently visiting us via your browser, Slack provides a stand alone program to make staying in touch even more convenient. "
"You can download it <https://slack.com/downloads|here.>\n\n"
"Want to make your first change to a program right now? "
"All active Operation Code Projects are located on our source control repository. "
"Our projects can be viewed on <https://github.com/OperationCode/START_HERE|Github.>")

PROXY = config('PROXY')
TOKEN = config('TOKEN')
COMMUNITY_CHANNEL = config('OPCODE_COMMUNITY_ID')

PROXY = PROXY if PROXY else None
PROXY = config('PROXY', default=None)

TOKEN = config('PERSONAL_APP_TOKEN')
COMMUNITY_CHANNEL = config('PERSONAL_PRIVATE_CHANNEL')

# TOKEN = config('OPCODE_APP_TOKEN')
# COMMUNITY_CHANNEL = config('OPCODE_REWRITE_CHANNEL')
# PROJECTS_CHANNEL = config('OPCODE_OC_PROJECTS_CHANNEL')
# COMMUNITY_CHANNEL = config('OPCODE_COMMUNITY_ID')
# COMMUNITY_CHANNEL = config('OPCODE_BOT_TESTING_CHANNEL')

"""Airtable configs"""
AIRTABLE_BASE_KEY = config('PERSONAL_AIRTABLE_BASE_KEY')
AIRTABLE_API_KEY = config('PERSONAL_AIRTABLE_TOKEN')
AIRTABLE_TABLE_NAME = 'Mentor Request'

slack_client = SlackClient(TOKEN, proxies=PROXY)


def build_message(message_template, **kwargs):
# TODO: Do something with all of the return values here

def build_message(message_template: str, **kwargs: dict) -> str:
return message_template.format(**kwargs)


def event_handler(event_dict):
# all_event_logger.info(event_dict)
def event_handler(event_dict: dict) -> None:
"""
Handles routing all of the received subscribed events to the correct method
:param event_dict:
"""
all_event_logger.info(event_dict)
if event_dict['type'] == 'team_join':
new_event_logger.info('New member event recieved')
new_member(event_dict)

if event_dict['type'] == 'presence_change':
# all_event_logger.info('User {} changed state to {}'.format(user_name_from_id(event_dict['user']), event_dict['presence']))
pass
# can be used for development to trigger the event instead of the team_join
if event_dict['type'] == 'message' and 'user' in event_dict.keys():
pass
# Will need to be removed. Currently for testing
# logger.info('Message event')
if event_dict['type'] == 'message' and 'user' in event_dict.keys() and event_dict['text'] == 'test4611':
""" Trigger for testing team_join event """
if event_dict['type'] == 'message' and 'user' in event_dict.keys() and event_dict['text'] == 'testgreet':
event_dict['user'] = {'id': event_dict['user']}
new_member(event_dict)


def new_member(event_dict):
def help_menu_interaction(data: dict) -> None:
"""
Receives help menu selection from the user and dynamically updates
displayed message
:param data:
"""

response = data['actions'][0]['value']

if response == 'suggestion':
trigger_id = data['trigger_id']
res = slack_client.api_call('dialog.open', trigger_id=trigger_id, dialog=SUGGESTION_MODAL)

# Disabled while airtable integration is still in development
# elif response == 'mentor':
# trigger_id = data['trigger_id']
# res = slack_client.api_call('dialog.open', trigger_id=trigger_id, dialog=MENTOR_REQUEST_MODAL)
# pprint(res)

else:
params = {'text': HELP_MENU_RESPONSES[data['actions'][0]['value']],
'channel': data['channel']['id'],
'ts': data['message_ts'],
'as_user': True
}
slack_client.api_call('chat.update', **params)


def greeted_interaction(data: dict) -> dict:
"""
Handles the interactive message sent to the #community channel
when a new member joins.
Displays the user that claimed the greeting along with the option
to un-claim
"""
if data['actions'][0]['value'] == 'greeted':
clicker = data['user']['id']
params = {'text': data['original_message']['text'],
"attachments": greeted_response_attachments(clicker),
'channel': data['channel']['id'],
'ts': data['message_ts'],
'as_user': True
}
res = slack_client.api_call("chat.update", **params)
return res
elif data['actions'][0]['value'] == 'reset_greet':
params = {'text': data['original_message']['text'],
"attachments": needs_greet_button(),
'channel': data['channel']['id'],
'ts': data['message_ts'],
'as_user': True
}
res = slack_client.api_call("chat.update", **params)


def suggestion_submission(data: dict) -> None:
"""
Receives the event when a user submits a suggestion for a new help topic and
posts it to the #community channel
:param data:
"""
suggestion = data['submission']['suggestion']
user_id = data['user']['id']
message = f":exclamation:<@{user_id}> just submitted a suggestion for a help topic:exclamation:\n-- {suggestion}"
res = slack_client.api_call('chat.postMessage', channel=COMMUNITY_CHANNEL, text=message)


def mentor_submission(data):
"""
Parses the mentor request dialog form and pushes the data to Airtable.
:param data:
:return:
"""

# Temporary hack. Change this to getting the record ID's from the table itself
services_records = {
'General Guidance - Slack Chat': 'recBxmDasLXwmVB78',
'General Guidance - Voice Chat': 'recDyu4PMbPl7Ti58',
'Pair Programming': 'recHCFAO9uNSy1WDs',
'Code Review': 'recUK55xJXOfAaYNb',
'Resume Review': 'recXZzUduWfaxWvSF',
'Mock Interview': 'recdY4XLeN1CPz1l8'
}

form = data['submission']
params = {
'fields': {
'Slack User': form['Slack User'],
'Email': form['Email'],
'Service': [services_records[form['service']]],
'Skillsets': [form['skillset']],
'Additional Details': form['Additional Details']
}
}

headers = {
'authorization': "Bearer " + AIRTABLE_API_KEY
}
res = requests.post(f"https://api.airtable.com/v0/{AIRTABLE_BASE_KEY}/{AIRTABLE_TABLE_NAME}", json=params,
headers=headers)


def new_member(event_dict: dict) -> None:
"""
Invoked when a new user joins and a team_join event is received.
DMs the new user with the welcome message and help menu as well as pings
the #community channel with a new member notification
:param event_dict:
"""
new_event_logger.info('Recieved json event: {}'.format(event_dict))

user_id = event_dict['user']['id']
# user_id = event_dict['user']
logging.info('team_join message')

real_name = user_name_from_id(user_id)

custom_message = build_message(MESSAGE, real_name=real_name)
custom_message = MESSAGE.format(real_name=real_name)

new_event_logger.info('Built message: {}'.format(custom_message))
response = slack_client.api_call('chat.postMessage',
channel=user_id,
text=custom_message,
as_user=True)
# channel=COMMUNITY_CHANNEL, # testing option
as_user=True, # Currently not working. DM comes from my account
text=custom_message)

r2 = slack_client.api_call('chat.postMessage',
channel=user_id,
# channel=COMMUNITY_CHANNEL, # testing option
as_user=True,
**HELP_MENU)

# Notify #community
# slack_client.api_call('chat.postMessage', channel=COMMUNITY_CHANNEL,
# text=f"New Member -- <@{user_id}>. Automated greeting sent.")
text = f":tada: <@{user_id}> has joined the Slack team :tada:"
slack_client.api_call('chat.postMessage', channel=COMMUNITY_CHANNEL,
text=text, attachments=needs_greet_button())

if response['ok']:
new_event_logger.info('New Member Slack response: {}'.format(response))
if response['ok'] and r2['ok']:
new_event_logger.info('New Member Slack response: Response 1: {} \nResponse2: {}'.format(response, r2))
else:
new_event_logger.error('FAILED -- Message to new member returned error: {}'.format(response))
new_event_logger.error('FAILED -- Message to new member returned error: {}\n{}'.format(response, r2))


def parse_slack_output(slack_rtm_output):
def parse_slack_output(slack_rtm_output: list) -> None:
"""
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.
Method for parsing slack events when using the RTM API instead
of the Events/App APIs
"""
for output in slack_rtm_output:
# process a single item in list at a time
event_handler(output)


def user_name_from_id(user_id):
# get detailed user info
def user_name_from_id(user_id: str) -> str:
"""
Queries the Slack workspace for the users real name
to personalize messages. Prioritizes real_name -> name -> 'New Member'
:param user_id:
"""
response = slack_client.api_call('users.info', user=user_id)

if response['user']['real_name']:
Expand All @@ -105,12 +229,19 @@ def user_name_from_id(user_id):


def join_channels():
"""
Utility function for joining channels. Move to utils?
"""
response = slack_client.api_call('channels.join', name='general')
print(response)


# set the defalt to a 1 second delay
def run_bot(delay=1):
def run_bot(delay: int = 1) -> None:
"""
Runs the bot using the Slack Real Time Messaging API.
**Doesn't provide events or interactive functionality
:param delay:
"""
setup_logging()
if slack_client.rtm_connect():
print(f"StarterBot connected and running with a {delay} second delay")
Expand Down
Loading

0 comments on commit 0b30a2e

Please sign in to comment.