Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src/common/webapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from werkzeug.middleware.proxy_fix import ProxyFix

# local imports
from src.common.common import app_dir, colors
from src.common.common import app_dir, colors, version
from src.common import crypto
from src.common import globals
from src.common import time
Expand Down Expand Up @@ -77,7 +77,20 @@ def html_to_md(html: str) -> str:

@app.route('/status')
def status():
return "LizardByte-bot is live!"
degraded_checks = [
getattr(globals.DISCORD_BOT, 'DEGRADED', True),
getattr(globals.REDDIT_BOT, 'DEGRADED', True),
]

s = 'ok'
if any(degraded_checks):
s = 'degraded'

result = {
"status": s,
"version": version,
}
return jsonify(result)


@app.route("/favicon.ico")
Expand Down
11 changes: 10 additions & 1 deletion src/discord/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ def __init__(self, *args, **kwargs):
kwargs['auto_sync_commands'] = True
super().__init__(*args, **kwargs)

self.DEGRADED = False

self.bot_thread = threading.Thread(target=lambda: None)
self.token = os.environ['DISCORD_BOT_TOKEN']
self.db = Database(db_path=os.path.join(data_dir, 'discord_bot_database'))
Expand Down Expand Up @@ -122,7 +124,12 @@ async def async_send_message(
embed.description = embed.description[:-cut_length] + "..."

channel = await self.fetch_channel(channel_id)
return await channel.send(content=message, embed=embed)

try:
return await channel.send(content=message, embed=embed)
except Exception as e:
print(f"Error sending message: {e}")
self.DEGRADED = True

def send_message(
self,
Expand Down Expand Up @@ -273,10 +280,12 @@ def start_threaded(self):
self.bot_thread.start()
except KeyboardInterrupt:
print("Keyboard Interrupt Detected")
self.DEGRADED = True
self.stop()

def stop(self, future: asyncio.Future = None):
print("Attempting to stop tasks")
self.DEGRADED = True
self.daily_task.stop()
self.role_update_task.stop()
self.clean_ephemeral_cache.stop()
Expand Down
8 changes: 6 additions & 2 deletions src/reddit/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
class Bot:
def __init__(self, **kwargs):
self.STOP_SIGNAL = False
self.DEGRADED = False

# threads
self.bot_thread = threading.Thread(target=lambda: None)
Expand Down Expand Up @@ -57,8 +58,7 @@ def __init__(self, **kwargs):
self.migrate_shelve()
self.migrate_last_online()

@staticmethod
def validate_env() -> bool:
def validate_env(self) -> bool:
required_env = [
'DISCORD_REDDIT_CHANNEL_ID',
'PRAW_CLIENT_ID',
Expand All @@ -69,6 +69,7 @@ def validate_env() -> bool:
for env in required_env:
if env not in os.environ:
sys.stderr.write(f"Environment variable ``{env}`` must be defined\n")
self.DEGRADED = True
return False
return True

Expand Down Expand Up @@ -165,6 +166,7 @@ def discord(self, submission: models.Submission):
try:
redditor = self.reddit.redditor(name=submission.author)
except Exception:
self.DEGRADED = True
return

# create the discord embed
Expand Down Expand Up @@ -266,11 +268,13 @@ def start_threaded(self):
self.bot_thread.start()
except KeyboardInterrupt:
print("Keyboard Interrupt Detected")
self.DEGRADED = True
self.stop()

def stop(self):
print("Attempting to stop reddit bot")
self.STOP_SIGNAL = True
self.DEGRADED = True
if self.bot_thread is not None and self.bot_thread.is_alive():
self.comment_thread.join()
self.submission_thread.join()
Expand Down
50 changes: 50 additions & 0 deletions tests/unit/common/test_common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# standard imports
import os
import re

# lib imports
import pytest

# local imports
from src.common import common


@pytest.fixture(scope='module')
def github_email():
return 'octocat@github.com'


def test_colors():
for color in common.colors.values():
assert 0x000000 <= color <= 0xFFFFFF, f"{color} is not a valid hex color"


def test_get_bot_avatar(github_email):
url = common.get_bot_avatar(gravatar=github_email)
print(url)
assert url.startswith('https://www.gravatar.com/avatar/')


def test_get_avatar_bytes(github_email, mocker):
mocker.patch('src.common.common.avatar', common.get_bot_avatar(gravatar=github_email))
avatar_bytes = common.get_avatar_bytes()
assert avatar_bytes
assert isinstance(avatar_bytes, bytes)


def test_get_app_dirs():
app_dir, data_dir = common.get_app_dirs()
assert app_dir
assert data_dir
assert os.path.exists(app_dir)
assert os.path.exists(data_dir)
assert os.path.isdir(app_dir)
assert os.path.isdir(data_dir)
assert app_dir == (os.getcwd() or '/app')
assert data_dir == (os.path.join(os.getcwd(), 'data') or '/data')


def test_version():
assert common.version
assert isinstance(common.version, str)
assert re.match(r'^\d+\.\d+\.\d+$', common.version)
19 changes: 18 additions & 1 deletion tests/unit/common/test_webapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,31 @@ def test_client():
yield test_client # this is where the testing happens!


def test_status(test_client):
@pytest.mark.parametrize("degraded", [
False,
True,
])
def test_status(test_client, discord_bot, degraded, mocker):
"""
WHEN the '/status' page is requested (GET)
THEN check that the response is valid
"""
# patch reddit bot, since we're not using its fixture
mocker.patch('src.common.globals.REDDIT_BOT', Mock(DEGRADED=False))

if degraded:
mocker.patch('src.common.globals.DISCORD_BOT.DEGRADED', True)

response = test_client.get('/status')
assert response.status_code == 200

if not degraded:
assert response.json['status'] == 'ok'
else:
assert response.json['status'] == 'degraded'

assert response.json['version']


def test_favicon(test_client):
"""
Expand Down