Show global header message when bot loses connection with server

This commit is contained in:
Jeremy Zhang 2017-06-13 01:39:49 +00:00
parent 926d4d14a9
commit 2647753007
8 changed files with 78 additions and 2 deletions

View File

@ -18,6 +18,9 @@ class Titan(discord.Client):
self.database = DatabaseInterface(self) self.database = DatabaseInterface(self)
self.command = Commands(self, self.database) self.command = Commands(self, self.database)
self.database_connected = False
self.loop.create_task(self.send_webserver_heartbeat())
def _cleanup(self): def _cleanup(self):
try: try:
self.loop.run_until_complete(self.logout()) self.loop.run_until_complete(self.logout())
@ -32,6 +35,14 @@ class Titan(discord.Client):
except: # Can be ignored except: # Can be ignored
pass pass
async def send_webserver_heartbeat(self):
await self.wait_until_ready()
while not self.database_connected:
await asyncio.sleep(1) # Wait until db is connected
while not self.is_closed:
await self.database.send_webserver_heartbeat()
await asyncio.sleep(60)
def run(self): def run(self):
try: try:
self.loop.run_until_complete(self.start(config["bot-token"])) self.loop.run_until_complete(self.start(config["bot-token"]))
@ -57,6 +68,7 @@ class Titan(discord.Client):
try: try:
await self.database.connect(config["database-uri"] + "?charset=utf8mb4") await self.database.connect(config["database-uri"] + "?charset=utf8mb4")
self.database_connected = True
except Exception: except Exception:
self.logger.error("Unable to connect to specified database!") self.logger.error("Unable to connect to specified database!")
traceback.print_exc() traceback.print_exc()

View File

@ -7,6 +7,7 @@ from sqlalchemy.ext.declarative import declarative_base
import json import json
import discord import discord
import time
Base = declarative_base() Base = declarative_base()
@ -15,6 +16,7 @@ from titanembeds.database.messages import Messages
from titanembeds.database.guild_members import GuildMembers from titanembeds.database.guild_members import GuildMembers
from titanembeds.database.unauthenticated_users import UnauthenticatedUsers from titanembeds.database.unauthenticated_users import UnauthenticatedUsers
from titanembeds.database.unauthenticated_bans import UnauthenticatedBans from titanembeds.database.unauthenticated_bans import UnauthenticatedBans
from titanembeds.database.keyvalue_properties import KeyValueProperties
class DatabaseInterface(object): class DatabaseInterface(object):
# Courtesy of https://github.com/SunDwarf/Jokusoramame # Courtesy of https://github.com/SunDwarf/Jokusoramame
@ -363,3 +365,15 @@ class DatabaseInterface(object):
dbuser.revoked = True dbuser.revoked = True
session.commit() session.commit()
return "Successfully kicked **{}#{}**!".format(dbuser.username, dbuser.discriminator) return "Successfully kicked **{}#{}**!".format(dbuser.username, dbuser.discriminator)
async def send_webserver_heartbeat(self):
async with threadpool():
with self.get_session() as session:
key = "bot_heartbeat"
q = session.query(KeyValueProperties).filter(KeyValueProperties.key == key)
if q.count() == 0:
session.add(KeyValueProperties(key=key, value=time.time()))
else:
firstobj = q.first()
firstobj.value = time.time()
session.commit()

View File

@ -0,0 +1,17 @@
from titanembeds.database import db, Base
import datetime
class KeyValueProperties(Base):
__tablename__ = "keyvalue_properties"
id = db.Column(db.Integer, primary_key=True) # Auto incremented id
key = db.Column(db.String(255), nullable=False) # Property Key
value = db.Column(db.Text()) # Property value
expiration = db.Column(db.TIMESTAMP) # Suggested Expiration for value (None = no expire) in secs
def __init__(self, key, value, expiration=None):
self.key = key
self.value = value
if expiration:
self.expiration = datetime.now() + timedelta(seconds = expiration)
else:
self.expiration = None

View File

@ -2,7 +2,7 @@ from config import config
from database import db from database import db
from flask import Flask, render_template, request, session, url_for, redirect, jsonify from flask import Flask, render_template, request, session, url_for, redirect, jsonify
from flask_sslify import SSLify from flask_sslify import SSLify
from titanembeds.utils import rate_limiter, discord_api from titanembeds.utils import rate_limiter, discord_api, bot_alive
import blueprints.api import blueprints.api
import blueprints.user import blueprints.user
import blueprints.embed import blueprints.embed
@ -38,3 +38,8 @@ def about():
def before_request(): def before_request():
db.create_all() db.create_all()
discord_api.init_discordrest() discord_api.init_discordrest()
@app.context_processor
def context_processor():
bot_status = bot_alive()
return {"bot_status": bot_status}

View File

@ -21,6 +21,7 @@
{% endif %} {% endif %}
</head> </head>
<body> <body>
{% include 'nobot_header.html.j2' %}
<div class="navbar-fixed"> <div class="navbar-fixed">
<nav> <nav>
<div class="nav-wrapper"> <div class="nav-wrapper">
@ -76,6 +77,7 @@
<div id="loginmodal" class="modal"> <div id="loginmodal" class="modal">
<div class="modal-content"> <div class="modal-content">
{% include 'nobot_header.html.j2' %}
<h4>{{ login_greeting }}</h4> <h4>{{ login_greeting }}</h4>
<div id="loginmodal-maincontent" class="row valign-wrap"> <div id="loginmodal-maincontent" class="row valign-wrap">
<div id="modal_guildinfobox" class="col m3 s12 center-align"> <div id="modal_guildinfobox" class="col m3 s12 center-align">

View File

@ -0,0 +1,10 @@
{% if not bot_status["status"] %}
<div style="border: solid 3px red; background-color: yellow; color: black;">
<p>
<strong>NOTICE!</strong>
The bot is <strong>currently not online</strong> or has <strong>lost the connection</strong> to the webserver.
If you see this header, please <a href="https://discord.io/titan" target="_blank" style="background-color: orange; color: blue;">notify us</a> as soon as possible for us to fix this issue.
Down since approximately <code>{{ bot_status["formatted_utc"] }}</code> UTC (<code>{{ bot_status["epoch_seconds"] }} epoch seconds</code>).
</p>
</div>
{% endif %}

View File

@ -19,6 +19,7 @@
{% include 'google_analytics.html.j2' %} {% include 'google_analytics.html.j2' %}
</head> </head>
<body> <body>
{% include 'nobot_header.html.j2' %}
<main> <main>
{% if session['unauthenticated'] is defined and not session['unauthenticated'] %} {% if session['unauthenticated'] is defined and not session['unauthenticated'] %}
<ul id="menu_dropdown" class="dropdown-content"> <ul id="menu_dropdown" class="dropdown-content">

View File

@ -1,10 +1,11 @@
from titanembeds.database import db, Guilds, KeyValueProperties from titanembeds.database import db, Guilds, KeyValueProperties, get_keyvalproperty
from flask import request, session from flask import request, session
from flask_limiter import Limiter from flask_limiter import Limiter
from config import config from config import config
import random import random
import string import string
import hashlib import hashlib
import time
from titanembeds.discordrest import DiscordREST from titanembeds.discordrest import DiscordREST
@ -73,4 +74,18 @@ def guild_query_unauth_users_bool(guild_id):
dbGuild = db.session.query(Guilds).filter(Guilds.guild_id==guild_id).first() dbGuild = db.session.query(Guilds).filter(Guilds.guild_id==guild_id).first()
return dbGuild.unauth_users return dbGuild.unauth_users
def bot_alive():
results = {"status": False, "formatted_utc": "Never", "epoch_seconds": None}
epoch = get_keyvalproperty("bot_heartbeat")
if not epoch:
return results
epoch = float(epoch)
utc = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(epoch))
results["formatted_utc"] = utc
results["epoch_seconds"] = epoch
now = time.time()
if now - epoch < 60 * 5:
results["status"] = True
return results
rate_limiter = Limiter(key_func=get_client_ipaddr) # Default limit by ip address rate_limiter = Limiter(key_func=get_client_ipaddr) # Default limit by ip address