Compare commits

..

13 Commits

18 changed files with 462 additions and 95 deletions

View File

@ -3,4 +3,7 @@ deployment:
tasks:
- export DEPLOYPATH=/home/benjamyntesting/chatbot/
- cp -Rfv * $DEPLOYPATH
- touch $DEPLOYPATH/tmp/restart.txt
- touch $DEPLOYPATH/tmp/restart.txt
- source /home/benjamyntesting/virtualenv/chatbot/3.8/bin/activate && cd /home/benjamyntesting/chatbot
- export FLASK_APP=priceybot2
- flask db upgrade

24
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,24 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Flask",
"type": "python",
"request": "launch",
"module": "flask",
"env": {
"FLASK_APP": "priceybot2",
"FLASK_DEBUG": "1"
},
"args": [
"run",
"--host=0.0.0.0"
],
"jinja": true,
"justMyCode": true
}
]
}

View File

@ -1,22 +1,20 @@
This has died, replaced with https://git.lovelynet.net/PriceyBot/PriceyBot3
I need to update this
# Setup
Set the ENV variable `GOOGLE_APPLICATION_CREDENTIALS` to the location of the google service account key
Run the python script
## compiling sass
cd into priceybot2 subfolder
````
npn run dev
npm run css-build
````
Or if you're working on it
````
npm start
I need to update this
# Setup
Set the ENV variable `GOOGLE_APPLICATION_CREDENTIALS` to the location of the google service account key
Run the python script
## compiling sass
cd into priceybot2 subfolder
````
npn run dev
npm run css-build
````
Or if you're working on it
````
npm start
````

View File

@ -30,35 +30,35 @@ def login_post():
return redirect(url_for('main.profile'))
@auth.route('/signup')
def signup():
return render_template('signup.html')
# @auth.route('/signup')
# def signup():
# return render_template('signup.html')
@auth.route('/signup', methods=['POST'])
def signup_post():
email = request.form.get('email')
name = request.form.get('name')
password = request.form.get('password')
google_id = request.form.get('google_id')
# @auth.route('/signup', methods=['POST'])
# def signup_post():
# email = request.form.get('email')
# name = request.form.get('name')
# password = request.form.get('password')
# google_id = request.form.get('google_id')
user = User.query.filter_by(email=email).first()
# user = User.query.filter_by(email=email).first()
if user:
flash('Email already exists for user')
return redirect(url_for('auth.signup'))
# if user:
# flash('Email already exists for user')
# return redirect(url_for('auth.signup'))
user = User.query.filter_by(google_id=google_id).first()
# user = User.query.filter_by(google_id=google_id).first()
if user:
flash('Google ID already in use')
return redirect(url_for('auth.signup'))
# if user:
# flash('Google ID already in use')
# return redirect(url_for('auth.signup'))
new_user = User(email=email, name=name, password=generate_password_hash(password, method='sha256'), google_id=google_id)
# new_user = User(email=email, name=name, password=generate_password_hash(password, method='sha256'), google_id=google_id)
db.session.add(new_user)
db.session.commit()
# Code to validate and add the user to the database
return redirect(url_for('auth.login'))
# db.session.add(new_user)
# db.session.commit()
# # Code to validate and add the user to the database
# return redirect(url_for('auth.login'))
@auth.route('/logout')
@login_required

View File

@ -21,22 +21,29 @@ Hangouts Chat bot that responds to events and messages from a room asynchronousl
import logging
import random
from flask import Blueprint, render_template, request, json
from google.oauth2 import service_account
from googleapiclient.discovery import build
from priceybot2 import models, db
logging.basicConfig(filename='example.log', level=logging.DEBUG)
chatbot = Blueprint('chatbot', __name__)
scopes = ['https://www.googleapis.com/auth/chat.bot']
scopes = [
'https://www.googleapis.com/auth/chat.messages',
'https://www.googleapis.com/auth/chat.bot',
]
credentials = service_account.Credentials.from_service_account_file('./priceybot2/config/service-acct.json')
# credentials, project_id = google.auth.default()
credentials = credentials.with_scopes(scopes=scopes)
chat = build('chat', 'v1', credentials=credentials)
@chatbot.route('/bot/', methods=['POST'])
def home_post():
"""Respond to POST requests to this endpoint.
@ -130,3 +137,65 @@ def home_get():
"""
return render_template('home.html')
@chatbot.route('/bot/members/<space>', methods=['GET'])
def members(space):
if space == "":
return {'text': "Please specify a space"}
try:
members = chat.spaces().members().list(parent=f'spaces/{space}').execute()
except Exception:
return {'text': "Space not found"}
for member in members['memberships']:
uid = int(member['member']['name'].split('/')[-1])
display_name = member['member']['displayName']
user = models.User.query.filter_by(google_id=uid).first()
if user:
print(f"User with UID of {uid} already exists in the DB")
continue
new_user = models.User(name=display_name, google_id=uid)
db.session.add(new_user)
print(f"Adding user {display_name}")
db.session.commit()
return {'text': "x"}
@chatbot.route('/bot/messages/<space>')
def get_messages(space):
messages = dir(chat.spaces().messages())
print(messages)
return "x"
@chatbot.route('/bot/randomquote/<space>', methods=['GET'])
def send_random_quote(space):
if space == "":
return {'text': "Please specify a space"}
try:
quote = random.choice(models.Quote.query.all())
except Exception:
return "Failed to get random quote"
chat.spaces().messages().create(
parent=f'spaces/{space}',
body={'text': quote.quote}
).execute()
return f"Sent random quote to space {space}"
@chatbot.route('/bot/sendquote/<space>', methods=['POST'])
def send_quote(space):
if request.json.get('quote_id') is None:
return "Fuck off"
quote = models.Quote.query.filter_by(id=request.json.get('quote_id')).first()
chat.spaces().messages().create(
parent=f'spaces/{space}',
body={'text': quote.quote}
).execute()
return "Sent quote"

View File

@ -1,5 +1,7 @@
from flask import Blueprint, render_template
from flask_login import login_required, current_user
from .models import Quote
from .chatbot import chat as bot
main = Blueprint('main', __name__)
@ -13,8 +15,20 @@ def index():
@login_required
def profile():
return render_template(
'profile.html',
name=current_user.name,
google_id=current_user.google_id,
is_admin=current_user.administrator
'profile.html'
)
@main.route('/quotes')
@login_required
def quotes():
spaces = bot.spaces().list().execute()['spaces']
space_list = list()
for space in spaces:
if space.get('singleUserBotDm'):
continue
else:
space_list.append([space['name'].split('/')[1], space['displayName']])
quotes = [[q.quote, q.id] for q in Quote.query.all()]
return render_template("quotes.html", quotes=quotes, space_list=space_list)

View File

@ -1,7 +1,7 @@
from datetime import datetime
from flask_login import UserMixin
from datetime import datetime
from . import db
@ -13,10 +13,19 @@ class User(UserMixin, db.Model):
google_id = db.Column(db.String(30), unique=True)
administrator = db.Column(db.Boolean, default=False)
class Quote(db.Model):
id = db.Column(db.Integer, primary_key=True)
quote = db.Column(db.String(2000))
date_added = db.Column(db.DateTime(), default=datetime.now())
date_last_used = db.Column(db.DateTime())
times_used = db.Column(db.Integer, default=0)
class Credit(db.Model):
id = db.Column(db.Integer, primary_key=True)
amount = db.Column(db.Integer)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
class Transactions(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
quote_id = db.Column(db.Integer, db.ForeignKey('quote.id'))

View File

@ -5,8 +5,9 @@
"requires": true,
"packages": {
"": {
"name": "mybulma",
"version": "1.0.0",
"license": "ISC",
"license": "MIT",
"devDependencies": {
"bulma": "^0.9.4",
"node-sass": "^8.0.0"

View File

@ -70,3 +70,14 @@ $link-focus-border: $nexi-darker-red;
//$link-active: $grey-darker !default
//$link-active-border: $grey-dark !default
$box-background-color: $nexi-grey;
$input-background-color: $nexi-darker-grey;
$input-color: $nexi-black;
// $table-background-color: $primary;
// $table-striped-row-even-background-color: $nexi-lighter-black;
// $table-color: $nexi-white;
$button-border-color: $nexi-darker-red;
$button-border-width: 1px;

View File

@ -36,4 +36,11 @@
.dark-toggle-animation {
transition: background 500ms;
}
.dark-position {
position: fixed;
bottom: 2em;
right: 2em;
}

49
priceybot2/sass/flex.scss Normal file
View File

@ -0,0 +1,49 @@
.fcontainer {
display: flex;
flex-direction: column;
}
.innercontainer {
display: flex;
flex-direction: row;
background-color: #E4E4E4;
border-radius: 1em;
}
.innercontainer-dark {
display: flex;
flex-direction: row;
background-color: #353535;
border-radius: 1em;
}
.fquote {
display: flex;
flex-grow: 8;
}
.fbuttons {
display: flex;
align-self: flex-end;
}
.del-button {
border-color: #F44336 !important;
color: #F44336 !important;
}
.sel-button {
border-color: #353535 !important;
color: #353535 !important;
}
.del-button-dark {
border-color: #F44336 !important;
color: #F44336 !important;
}
.sel-button-dark {
border-color: #f8fafb !important;
color: #f8fafb !important;
}

View File

@ -3,6 +3,7 @@
// Our scss files
@import "branding";
@import "dark-mode";
@import "flex";
// Import after our branding so colors work.
@import "../node_modules/bulma/bulma.sass";

View File

@ -20,6 +20,51 @@
.dark-toggle-animation {
transition: background 500ms; }
.dark-position {
position: fixed;
bottom: 2em;
right: 2em; }
.fcontainer {
display: flex;
flex-direction: column; }
.innercontainer {
display: flex;
flex-direction: row;
background-color: #E4E4E4;
border-radius: 1em; }
.innercontainer-dark {
display: flex;
flex-direction: row;
background-color: #353535;
border-radius: 1em; }
.fquote {
display: flex;
flex-grow: 8; }
.fbuttons {
display: flex;
align-self: flex-end; }
.del-button {
border-color: #F44336 !important;
color: #F44336 !important; }
.sel-button {
border-color: #353535 !important;
color: #353535 !important; }
.del-button-dark {
border-color: #F44336 !important;
color: #F44336 !important; }
.sel-button-dark {
border-color: #f8fafb !important;
color: #f8fafb !important; }
/*! bulma.io v0.9.4 | MIT License | github.com/jgthms/bulma */
/* Bulma Utilities */
.button, .input, .textarea, .select select, .file-cta,
@ -413,7 +458,7 @@ table th {
/* Bulma Elements */
.box {
background-color: white;
background-color: #E4E4E4;
border-radius: 6px;
box-shadow: 0 0.5em 1em -0.125em rgba(10, 10, 10, 0.1), 0 0px 0 1px rgba(10, 10, 10, 0.02);
color: #181818;
@ -428,7 +473,7 @@ a.box:active {
.button {
background-color: white;
border-color: #D2D2D2;
border-color: #EB302D;
border-width: 1px;
color: #363636;
cursor: pointer;
@ -3660,18 +3705,18 @@ a.tag:hover {
/* Bulma Form */
.input, .textarea, .select select {
background-color: white;
background-color: #D2D2D2;
border-color: #D2D2D2;
border-radius: 4px;
color: #363636; }
color: #181818; }
.input::-moz-placeholder, .textarea::-moz-placeholder, .select select::-moz-placeholder {
color: rgba(54, 54, 54, 0.3); }
color: rgba(24, 24, 24, 0.3); }
.input::-webkit-input-placeholder, .textarea::-webkit-input-placeholder, .select select::-webkit-input-placeholder {
color: rgba(54, 54, 54, 0.3); }
color: rgba(24, 24, 24, 0.3); }
.input:-moz-placeholder, .textarea:-moz-placeholder, .select select:-moz-placeholder {
color: rgba(54, 54, 54, 0.3); }
color: rgba(24, 24, 24, 0.3); }
.input:-ms-input-placeholder, .textarea:-ms-input-placeholder, .select select:-ms-input-placeholder {
color: rgba(54, 54, 54, 0.3); }
color: rgba(24, 24, 24, 0.3); }
.input:hover, .textarea:hover, .select select:hover, .is-hovered.input, .is-hovered.textarea, .select select.is-hovered {
border-color: #EB302D; }
.input:focus, .textarea:focus, .select select:focus, .is-focused.input, .is-focused.textarea, .select select.is-focused, .input:active, .textarea:active, .select select:active, .is-active.input, .is-active.textarea, .select select.is-active {

View File

@ -55,18 +55,27 @@ function applyTheme(theme) {
// Find elements and update class.
switch (theme) {
case 'dark':
primaryElements = Array.from(document.querySelectorAll('.is-primary-invert'));
primaryElements.forEach((element) => {
element.classList.remove('is-primary-invert');
element.classList.add('is-primary');
});
test = Array('.is-primary-invert|is-primary', '.sel-button|sel-button-dark' , '.innercontainer|innercontainer-dark')
test.forEach((t) => {
let c = t.split('|')
primaryElements = Array.from(document.querySelectorAll(c[0]));
primaryElements.forEach((element) => {
element.classList.remove(c[0].split('.')[1]);
element.classList.add(c[1]);
});
})
break;
case 'light':
primaryElements = Array.from(document.querySelectorAll('.is-primary'));
primaryElements.forEach((element) => {
element.classList.remove('is-primary');
element.classList.add('is-primary-invert');
});
test = Array('.is-primary|is-primary-invert', '.sel-button-dark|sel-button' , '.innercontainer-dark|innercontainer')
test.forEach((t) => {
let c = t.split('|')
primaryElements = Array.from(document.querySelectorAll(c[0]));
primaryElements.forEach((element) => {
element.classList.remove(c[0].split('.')[1]);
element.classList.add(c[1]);
});
})
break;
}
}

View File

@ -1,10 +1,11 @@
<!DOCTYPE html>
<html class="is-clipped" xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pricey Bot</title>
<script src="https://kit.fontawesome.com/4b444ef337.js" crossorigin="anonymous"></script>
<script type="application/javascript" src="{{ url_for('static', filename='scripts/dark-mode.js', version='0.1') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css', version='0.1') }}">
</head>
@ -15,28 +16,49 @@
<nav class="navbar">
<div class="container">
<div id="navbarMenuHeroA" class="navbar-menu">
<div class="navbar-start">
{% if current_user.is_authenticated %}
<span class="navbar-item">{{ current_user.name }} is logged in{% if current_user.administrator %}<br/><span class="navbar-item icon p-4">
<i class="fas fa-screwdriver-wrench"></i>
</span></span>
{% endif %}
{% endif %}
</div>
<div class="navbar-end">
{% if space_list is defined %}
<div class="select is-primary is-normal is-rounded m-4" >
<select name="space" id="space_option">
<option selected="selected" value="NaN">Please selelct a space!</option>
{% for i in space_list %}
<option value="{{ i[0] }}">{{ i[1] }}</option>
{% endfor %}
</select>
</div>
{% endif %}
<a href="{{ url_for('main.index') }}" class="navbar-item">
Home
</a>
{% if current_user.is_authenticated %}
<a href="{{ url_for('main.profile') }}" class="navbar-item">
Profile
</a>
{% endif %}
{% if not current_user.is_authenticated %}
<a href="{{ url_for('auth.login') }}" class="navbar-item">
Login
</a>
<a href="{{ url_for('auth.signup') }}" class="navbar-item">
Sign Up
</a>
{% endif %}
{% if current_user.is_authenticated %}
<a href="{{ url_for('auth.logout') }}" class="navbar-item">
Logout
</a>
{% endif %}
{% if current_user.is_authenticated %}
<a href="{{ url_for('main.profile') }}" class="navbar-item">
Profile
</a>
<a href="{{ url_for('main.quotes') }}" class="navbar-item">
Quotes
</a>
{% endif %}
{% if not current_user.is_authenticated %}
<a href="{{ url_for('auth.login') }}" class="navbar-item">
Login
</a>
{% endif %}
{% if current_user.is_authenticated %}
<a href="{{ url_for('auth.logout') }}" class="navbar-item">
Logout
</a>
{% endif %}
</div>
</div>
</div>
@ -52,9 +74,10 @@
<div class="hero-foot">
<label class="is-pulled-right p-4">
<input name="dark-mode-toggle" class="dark-toggle" onclick="darkLight()" type="checkbox">
<input name="dark-mode-toggle" class="dark-toggle dark-position" onclick="darkLight()" type="checkbox">
</label>
</div>
</section>
</body>
</html>

View File

@ -1,9 +1,23 @@
{% extends "base.html" %}
{% block content %}
<h1 class="title">
Welcome, {{ name }}! <p/>
Your google ID is {{ google_id }} <p/>
Admin?: {{ "yes" if is_admin else "no" }} <p/>
</h1>
<div class="fcontainer is-primary has-text-left" style="flex-direction: column; align-content: flex-start">
<div style="display: flex; flex-grow: 2;">
<img src="https://benjamyn.love/frannodders.gif" width="256" height="256">
</div>
<div style="display: flex; flex-grow: 8; flex-direction: column;">
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
</div>
<!-- <p class="fquote">Welcome, {{ current_user.name }}!</p>
<p class="fquote">Your google ID is {{ current_user.google_id }}</p>
<p class="fquote">Admin?: {{ "yes" if current_user.administrator else "no" }}</p> -->
</div>
{% endblock %}

View File

@ -0,0 +1,40 @@
{% extends "base.html" %}
{% block content %}
<script>
function sendquote(id) {
select = document.getElementById("space_option");
space_id = select[select.selectedIndex].value;
space_name = select[select.selectedIndex].text;
if (space_id === "NaN") {
alert("Please select a space");
return
}
let confirmation = confirm(`Send quote to ${space_name}`)
if (confirmation === true) {
let send_url = "{{ url_for('chatbot.send_quote', space='AAAAAA', _external=True) }}"
send_url = send_url.replace("AAAAAA",space_id)
fetch(send_url, {method: "POST",headers: {"Content-Type": "application/json"},body: JSON.stringify({"quote_id": id})})
.then((response) => console.log(response))
}
}
</script>
<div class="fcontainer" >
{% for i in quotes %}
<div class=" has-text-left m-1 innercontainer p-4">
<div class="fquote" id="quote_{{ i[1] }}">
{{ i[0] }}
</div>
<div class="fbuttons">
<button class="button is-primary is-outlined m-1 sel-button" {% if not current_user.administrator %} disabled title="Administrator Privs required" {% endif %} onclick="sendquote({{ i[1] }})">Send</button>
<button class="button is-primary is-outlined m-1 del-button" {% if not current_user.administrator %} disabled title="Administrator Privs required" {% endif %} onclick="sendquote({{ i[1] }})">Delete</button>
</div>
</div>
{% endfor %}
</div>
{% endblock %}

View File

@ -0,0 +1,50 @@
{% extends "base.html" %}
{% block content %}
<script>
function sendquote(id) {
select = document.getElementById("space_option");
space_id = select[select.selectedIndex].value;
space_name = select[select.selectedIndex].text;
if (space_id === "NaN") {
alert("Please select a space");
return
}
let confirmation = confirm(`Send quote to ${space_name}`)
if (confirmation === true) {
let send_url = "{{ url_for('chatbot.send_quote', space='AAAAAA', _external=True) }}"
send_url = send_url.replace("AAAAAA",space_id)
fetch(send_url, {method: "POST",headers: {"Content-Type": "application/json"},body: JSON.stringify({"quote_id": id})})
.then((response) => console.log(response))
}
}
</script>
<div >
<h3 class="title">Quotes</h3>
<div >
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="notification is-danger">
{{ messages[0] }}
</div>
{% endif %}
{% endwith %}
<table class="table">
{% for quote in quotes %}
<tr>
<td class=" is-primary" style="text-align: left">
{{ quote[0] }}
</td>
{% if current_user.administrator %}
<td class="is-primary"><button class="is-primary button" onclick='sendquote({{ quote[1] }})' type="submit">Send</button></td>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
</div>
{% endblock %}