mirror of
				https://github.com/TitanEmbeds/Titan.git
				synced 2025-11-03 23:37:09 +01:00 
			
		
		
		
	Show global header message when bot loses connection with server
This commit is contained in:
		@@ -17,6 +17,9 @@ class Titan(discord.Client):
 | 
				
			|||||||
        self.http.user_agent += ' TitanEmbeds-Bot'
 | 
					        self.http.user_agent += ' TitanEmbeds-Bot'
 | 
				
			||||||
        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:
 | 
				
			||||||
@@ -31,6 +34,14 @@ class Titan(discord.Client):
 | 
				
			|||||||
            gathered.exception()
 | 
					            gathered.exception()
 | 
				
			||||||
        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:
 | 
				
			||||||
@@ -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()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								discordbot/titanembeds/database/keyvalue_properties.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								discordbot/titanembeds/database/keyvalue_properties.py
									
									
									
									
									
										Normal 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
 | 
				
			||||||
@@ -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}
 | 
				
			||||||
@@ -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">
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								webapp/titanembeds/templates/nobot_header.html.j2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								webapp/titanembeds/templates/nobot_header.html.j2
									
									
									
									
									
										Normal 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 %}
 | 
				
			||||||
@@ -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">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -72,5 +73,19 @@ def guild_accepts_visitors(guild_id):
 | 
				
			|||||||
def guild_query_unauth_users_bool(guild_id):
 | 
					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
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user