From 5152283208c015a81749407cd596b08012033470 Mon Sep 17 00:00:00 2001 From: lza_menace Date: Mon, 8 Mar 2021 11:52:20 -0800 Subject: [PATCH] app split refactor, adding db models --- app/app.py | 195 +----------------------------------- app/cli.py | 12 +++ app/factory.py | 31 ++++++ app/{ => library}/gdrive.py | 0 app/library/slack.py | 13 +++ app/{ => library}/ux.py | 0 app/logging.py | 36 +++++++ app/models.py | 19 ++++ app/routes.py | 83 +++++++++++++++ docker-compose.yaml | 5 - requirements.txt | 4 + 11 files changed, 202 insertions(+), 196 deletions(-) create mode 100644 app/cli.py create mode 100644 app/factory.py rename app/{ => library}/gdrive.py (100%) create mode 100644 app/library/slack.py rename app/{ => library}/ux.py (100%) create mode 100644 app/logging.py create mode 100644 app/models.py create mode 100644 app/routes.py diff --git a/app/app.py b/app/app.py index 7364b34..150823a 100644 --- a/app/app.py +++ b/app/app.py @@ -1,197 +1,10 @@ -import logging -import os -import json from logging.config import dictConfig -from slack_sdk import WebClient -from slack_sdk.signature import SignatureVerifier -from slack_sdk.errors import SlackApiError -from slugify import slugify -from flask import Flask, request, make_response -from app.ux import SlackInterface -from app.gdrive import * - - -app = Flask(__name__) -client = WebClient(token=os.getenv('SLACK_API_TOKEN')) -signature_verifier = SignatureVerifier(os.getenv('SLACK_SIGNING_SECRET')) -# client.api_test() - -dictConfig({ - 'version': 1, - 'disable_existing_loggers': True, - 'formatters': { - 'default': { - 'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s', - }, - 'access': { - 'format': '%(message)s', - } - }, - 'handlers': { - 'console': { - 'level': 'INFO', - 'class': 'logging.StreamHandler', - 'formatter': 'default', - 'stream': 'ext://sys.stdout', - } - }, - 'loggers': { - 'gunicorn.error': { - 'handlers': ['console'], - 'level': 'INFO', - 'propagate': False, - }, - 'gunicorn.access': { - 'handlers': ['console'], - 'level': 'INFO', - 'propagate': False, - } - }, - 'root': { - 'level': 'DEBUG', - 'handlers': ['console'], - } -}) - -def verify_slack(req: request): - signature_verifier = SignatureVerifier(os.getenv('SLACK_SIGNING_SECRET')) - if not signature_verifier.is_valid_request(req.get_data(), req.headers): - return make_response('invalid request', 403) - -@app.route('/slack/command', methods=['POST']) -def slack_command(): - verify_slack(request) - logging.info(request.form) - client.views_open( - trigger_id=request.form.get('trigger_id'), - view=SlackInterface().select_action_modal() - ) - return make_response('', 200) - - -@app.route('/slack/events', methods=['POST']) -def slack_events(): - verify_slack(request) - if 'payload' in request.form: - payload = json.loads(request.form.get('payload')) - print('debug: %s \n' % payload) - - if 'actions' in payload: - action_id = payload['actions'][0]['action_id'] - trigger_id = payload['trigger_id'] - - if action_id == 'create_new_channel_modal': - client.views_push( - trigger_id=trigger_id, - view=SlackInterface().new_channel_modal(payload['user']['id']) - ) - elif action_id == 'generate_documents_modal': - client.views_push( - trigger_id=trigger_id, - view=SlackInterface().generate_docs_modal() - ) - elif action_id == 'debug_modal': - print(get_drives()) - - return make_response('', 200) - - elif 'view' in payload: - - callback_id = payload['view']['callback_id'] - - if callback_id == 'submit_new_channel': - users_to_add = list() - values = payload['view']['state']['values'] - cx_name = values['customer_name']['customer_name']['value'] - cx_slug = slugify(cx_name) - selected_users = values['users_to_add']['users_to_add']['selected_users'] - for i in selected_users: - username = client.users_info(user=i) - users_to_add.append(username['user']['name']) - - # Create channel - try: - res = client.conversations_create(name=cx_slug) - # Notify da peeps - client.chat_postMessage( - channel=res['channel']['id'], - text=f'sup bros! {" ".join(["@" + i for i in users_to_add])}' - ) - return make_response('', 200) - except SlackApiError as e: - logging.error("Error creating conversation: {}".format(e)) - return make_response('', 403) - - elif callback_id == 'generate_documents': - pass - else: - return make_response('', 404) - - -# -# // Handle actions based upon user selection and inputs -# if ( body.view.callback_id == 'submit_new_channel' ) { -# console.log('[+] Creating new channel for @' + body.user.username); -# -# // Gather vars and setup slug from customer name -# let cx_name = body.view.state.values.customer_name.customer_name.value; -# let cx_char = cx_name.charAt(0); -# let cx_slug = slugify(cx_name, { -# strict: true, -# lower: true -# }); -# -# // Check if first character is a number so it can go into numeric group -# if ( !isNaN(cx_char) ) { -# var gdrive_prefix = '0-9'; -# } else { -# var gdrive_prefix = cx_char.toUpperCase(); -# } -# -# // Create users array to add to channel -# const users = body.view.state.values.users_to_add.users_to_add.selected_users.map(async function(item) { -# var result = await api.callSlackAPI('users.info', { -# user: item -# }); -# let slack_username = '@' + result.user.name; -# return slack_username -# }); -# -# await Promise.all(users).then(async function(result) { -# // Post to Zapier to run Zap to create new channel -# await api.postZapierWebhook(process.env.ZAPIER_WEBHOOK_submit_new_channel, { -# 'customer_name': cx_name, -# 'gdrive_prefix': gdrive_prefix, -# 'slack_channel': cx_slug, -# 'users': result -# }); -# }) -# } else if ( body.view.callback_id == 'generate_documents' ) { -# console.log('[+] Generating documents for @' + body.user.username); -# -# // Gather vars and setup slug from customer name -# let cx_name = body.view.state.values.customer_name.customer_name.value; -# let opp_name = body.view.state.values.opportunity_name.opportunity_name.value; -# let cx_char = cx_name.charAt(0); -# -# // Check if first character is a number so it can go into numeric group -# if ( !isNaN(cx_char) ) { -# var gdrive_prefix = '0-9'; -# } else { -# var gdrive_prefix = cx_char.toUpperCase(); -# } -# -# // Post to Zapier to run Zap to generate new docs in the channel -# await api.postZapierWebhook(process.env.ZAPIER_WEBHOOK_generate_documents, { -# 'customer_name': cx_name, -# 'gdrive_prefix': gdrive_prefix, -# 'gdrive_item': cx_name + ' - ' + opp_name, -# 'slack_channel': body.view.state.values.channel_to_post_to.channel_to_post_to.selected_channels[0], -# }); -# } - +from app.logging import logging_config +from app.factory import create_app +app = create_app() +dictConfig(logging_config) if __name__ == '__main__': app.run() diff --git a/app/cli.py b/app/cli.py new file mode 100644 index 0000000..9cae1e2 --- /dev/null +++ b/app/cli.py @@ -0,0 +1,12 @@ +import click +from flask import Blueprint +from app.factory import db + + +cli_bp = Blueprint('cli', 'cli', cli_group=None) + + +@cli_bp.cli.command('init') +def init(): + import app.models + db.create_all() diff --git a/app/factory.py b/app/factory.py new file mode 100644 index 0000000..0909085 --- /dev/null +++ b/app/factory.py @@ -0,0 +1,31 @@ +from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from os import getenv + + +db = SQLAlchemy() + + +def setup_db(app: Flask): + uri = 'postgresql+psycopg2://{user}:{pw}@{host}:{port}/{db}'.format( + user=getenv('DB_USER'), + pw=getenv('DB_PASS'), + host=getenv('DB_HOST'), + port=getenv('DB_PORT'), + db=getenv('DB_NAME') + ) + app.config['SQLALCHEMY_DATABASE_URI'] = uri + app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + db = SQLAlchemy(app) + return db + + +def create_app(): + app = Flask(__name__) + setup_db(app) + with app.app_context(): + from app.routes import api_bp + from app.cli import cli_bp + app.register_blueprint(api_bp) + app.register_blueprint(cli_bp) + return app diff --git a/app/gdrive.py b/app/library/gdrive.py similarity index 100% rename from app/gdrive.py rename to app/library/gdrive.py diff --git a/app/library/slack.py b/app/library/slack.py new file mode 100644 index 0000000..c691319 --- /dev/null +++ b/app/library/slack.py @@ -0,0 +1,13 @@ +from os import getenv +from flask import make_response, request +from slack_sdk import WebClient +from slack_sdk.signature import SignatureVerifier +from slack_sdk.errors import SlackApiError + + +client = WebClient(token=getenv('SLACK_API_TOKEN')) + +def verify_slack(req: request): + signature_verifier = SignatureVerifier(getenv('SLACK_SIGNING_SECRET')) + if not signature_verifier.is_valid_request(req.get_data(), req.headers): + return make_response('invalid request', 403) diff --git a/app/ux.py b/app/library/ux.py similarity index 100% rename from app/ux.py rename to app/library/ux.py diff --git a/app/logging.py b/app/logging.py new file mode 100644 index 0000000..97d20cb --- /dev/null +++ b/app/logging.py @@ -0,0 +1,36 @@ +logging_config = { + 'version': 1, + 'disable_existing_loggers': True, + 'formatters': { + 'default': { + 'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s', + }, + 'access': { + 'format': '%(message)s', + } + }, + 'handlers': { + 'console': { + 'level': 'INFO', + 'class': 'logging.StreamHandler', + 'formatter': 'default', + 'stream': 'ext://sys.stdout', + } + }, + 'loggers': { + 'gunicorn.error': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + }, + 'gunicorn.access': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + } + }, + 'root': { + 'level': 'DEBUG', + 'handlers': ['console'], + } +} diff --git a/app/models.py b/app/models.py new file mode 100644 index 0000000..20c4559 --- /dev/null +++ b/app/models.py @@ -0,0 +1,19 @@ +from datetime import datetime +from sqlalchemy import func +from app.factory import db +from app import config + + +class WorkEvent(db.Model): + __tablename__ = 'work_events' + + id = db.Column(db.String(80), primary_key=True) + create_date = db.Column(db.DateTime, server_default=func.now()) + slack_channel = db.Column(db.String(50)) + work_type = db.Column(db.String(50)) + account_idx = db.Column(db.Integer) + start_date = db.Column(db.DateTime) + end_date = db.Column(db.DateTime) + + def __repr__(self): + return self.id diff --git a/app/routes.py b/app/routes.py new file mode 100644 index 0000000..523cf85 --- /dev/null +++ b/app/routes.py @@ -0,0 +1,83 @@ +import logging +import json +from slugify import slugify +from flask import Flask, request, make_response, Blueprint +from flask_sqlalchemy import SQLAlchemy +from app.library.ux import SlackInterface +from app.library.gdrive import * +from app.library.slack import verify_slack, client +from app.logging import logging_config +from app.factory import create_app, db + + +api_bp = Blueprint('api', 'api') + + +@api_bp.route('/slack/command', methods=['POST']) +def slack_command(): + verify_slack(request) + logging.info(request.form) + client.views_open( + trigger_id=request.form.get('trigger_id'), + view=SlackInterface().select_action_modal() + ) + return make_response('', 200) + + +@api_bp.route('/slack/events', methods=['POST']) +def slack_events(): + verify_slack(request) + if 'payload' in request.form: + payload = json.loads(request.form.get('payload')) + print('debug: %s \n' % payload) + + if 'actions' in payload: + action_id = payload['actions'][0]['action_id'] + trigger_id = payload['trigger_id'] + + if action_id == 'create_new_channel_modal': + client.views_push( + trigger_id=trigger_id, + view=SlackInterface().new_channel_modal(payload['user']['id']) + ) + elif action_id == 'generate_documents_modal': + client.views_push( + trigger_id=trigger_id, + view=SlackInterface().generate_docs_modal() + ) + elif action_id == 'debug_modal': + print(get_drives()) + + return make_response('', 200) + + elif 'view' in payload: + + callback_id = payload['view']['callback_id'] + + if callback_id == 'submit_new_channel': + users_to_add = list() + values = payload['view']['state']['values'] + cx_name = values['customer_name']['customer_name']['value'] + cx_slug = slugify(cx_name) + selected_users = values['users_to_add']['users_to_add']['selected_users'] + for i in selected_users: + username = client.users_info(user=i) + users_to_add.append(username['user']['name']) + + # Create channel + try: + res = client.conversations_create(name=cx_slug) + # Notify da peeps + client.chat_postMessage( + channel=res['channel']['id'], + text=f'sup bros! {" ".join(["@" + i for i in users_to_add])}' + ) + return make_response('', 200) + except SlackApiError as e: + logging.error("Error creating conversation: {}".format(e)) + return make_response('', 403) + + elif callback_id == 'generate_documents': + pass + else: + return make_response('', 404) diff --git a/docker-compose.yaml b/docker-compose.yaml index 4716299..46621b3 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -11,8 +11,3 @@ services: POSTGRES_DB: ${DB_NAME:-salesbot} volumes: - ${DATA_DIR:-./data/postgresql}:/var/lib/postgresql/data - cache: - image: redis:latest - container_name: salesbot_cache - ports: - - 127.0.0.1:6379:6379 diff --git a/requirements.txt b/requirements.txt index d31b876..05f8a93 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,8 @@ Flask +flask_sqlalchemy +sqlalchemy +arrow +psycopg2-binary gunicorn slack_sdk python-slugify