Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add page to view log list and several fixes #68

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2689902
update for dpy2
lorenzo132 Nov 21, 2021
7227653
FIx for old-timezone naive dates
fourjr Nov 22, 2021
f4c181f
Bump all requirements versions to latest (#56)
Taaku18 Sep 2, 2022
ef746f1
Merge branch 'dev'
Taaku18 Sep 2, 2022
8b41ff4
Merge pull request #44 from lorenzo132/master
fourjr Sep 20, 2022
d62b25d
Added load_dontenv, changed mongo_uri load logic (#59)
sebkuip Sep 23, 2022
4bf7edb
Fixed typo from previous commit
Taaku18 Sep 23, 2022
13502fc
Updated requirements.txt to the same as pipfile.lock
Taaku18 Sep 23, 2022
9744541
Add page to view logs and several fixes
raidensakura Dec 16, 2022
bd7b477
Some new shinies ✨
raidensakura Dec 17, 2022
29214ae
Add quote block formatting
raidensakura Dec 17, 2022
6c9d074
Add pagination to loglist page
raidensakura Dec 18, 2022
8dc8fe2
Only project last message in array
raidensakura Dec 18, 2022
93d0399
Create docker-image.yml (#67)
Saturn745 Dec 19, 2022
f98ad64
Fix dead system avatar, formatting
raidensakura Dec 23, 2022
3266a02
Fix incorrect message count
raidensakura Dec 27, 2022
858517c
Fix padding
raidensakura Dec 29, 2022
3604e80
Add open and closed log filter
raidensakura Dec 29, 2022
5d07b75
Multiple changes
raidensakura Dec 29, 2022
7346337
correction lol
raidensakura Dec 29, 2022
8816ade
Improve filters
raidensakura Dec 30, 2022
f83f783
Convert str to int first before subtracting
raidensakura Dec 30, 2022
3d09c29
Fix nsfw tag not showing
raidensakura Dec 30, 2022
ad6c9dc
Change to floor division
raidensakura Jan 1, 2023
cb3e2b8
Enable search on Enter keypress
raidensakura Jan 4, 2023
00da944
Fix formatting
raidensakura Jan 4, 2023
0ceed6e
Merge branch 'kyb3r:master' into loglist
raidensakura Jan 6, 2023
5b0a49d
Fix error 500 when a thread contains no message
raidensakura Mar 19, 2023
dc9b06f
Update links
raidensakura Apr 9, 2023
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
42 changes: 42 additions & 0 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

name: Create and publish a Docker image

on:
push:
branches: ['master']

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ motor = "~=3.0"
natural = "~=0.2.0"
pymongo = {version = "*", extras = ['srv']} # Required by motor
python-dateutil = "~=2.8.2"
python-dotenv = "~=0.18.0"
sanic = "~=22.6.0"

[scripts]
Expand Down
261 changes: 153 additions & 108 deletions Pipfile.lock

Large diffs are not rendered by default.

26 changes: 22 additions & 4 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

import os

from dotenv import load_dotenv
from motor.motor_asyncio import AsyncIOMotorClient
from sanic import Sanic, response
from sanic.exceptions import NotFound
from jinja2 import Environment, FileSystemLoader

from core.models import LogEntry
from core.models import LogEntry, LogList
from core.utils import loglist

load_dotenv()

if "URL_PREFIX" in os.environ:
print("Using the legacy config var `URL_PREFIX`, rename it to `LOG_URL_PREFIX`")
Expand All @@ -19,9 +22,11 @@
if prefix == "NONE":
prefix = ""

MONGO_URI = os.getenv("MONGO_URI")
MONGO_URI = os.getenv("MONGO_URI") or os.getenv("CONNECTION_URI")
if not MONGO_URI:
MONGO_URI = os.environ['CONNECTION_URI']
print("No CONNECTION_URI config var found. "
"Please enter your MongoDB connection URI in the configuration or .env file.")
exit(1)

app = Sanic(__name__)
app.static("/static", "./static")
Expand Down Expand Up @@ -49,7 +54,20 @@ async def not_found(request, exc):

@app.get("/")
async def index(request):
return render_template("index")
return render_template("index", prefix=prefix)


@app.get(prefix)
@loglist()
async def get_log_list(request, document, page, max_page, status_open, count_all):
"""Returns the html rendered log list"""

if document is None:
raise NotFound

log_list = LogList(app, document, prefix, page, max_page, status_open, count_all)

return log_list.render_html()


@app.get(prefix + "/raw/<key>")
Expand Down
4 changes: 4 additions & 0 deletions core/formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ def encode_inline_codeblock(m):
# Encode inline codeblocks (`text`)
content = re.sub(r"`([^`]+)`", encode_inline_codeblock, content)

# Encode inline blockquotes (> test)
# Multiline blockquotes (>>> test) are saved as single in Mongo (> test)
content = re.sub(r"(&gt; )([^\n]+)", r"<blockquote>\2</blockquote>", content)

# Encode links
if allow_links:

Expand Down
31 changes: 22 additions & 9 deletions core/models.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
from datetime import datetime
from datetime import datetime, timezone
import dateutil.parser

from sanic import response
from natural.date import duration

from .formatter import format_content_html
from core.formatter import format_content_html


class LogEntry:
def __init__(self, app, data):
self.app = app
self.key = data["key"]
self.open = data["open"]
self.created_at = dateutil.parser.parse(data["created_at"])
self.human_created_at = duration(self.created_at, now=datetime.utcnow())
self.created_at = dateutil.parser.parse(data["created_at"]).astimezone(timezone.utc)
self.human_created_at = duration(self.created_at, now=datetime.now(timezone.utc))
self.closed_at = (
dateutil.parser.parse(data["closed_at"]) if not self.open else None
dateutil.parser.parse(data["closed_at"]).astimezone(timezone.utc) if not self.open else None
)
self.channel_id = int(data["channel_id"])
self.guild_id = int(data["guild_id"])
Expand All @@ -31,11 +31,11 @@ def __init__(self, app, data):

@property
def system_avatar_url(self):
return "https://discordapp.com/assets/" "f78426a064bc9dd24847519259bc42af.png"
return "/static/img/avatar_self.png"

@property
def human_closed_at(self):
return duration(self.closed_at, now=datetime.utcnow())
return duration(self.closed_at, now=datetime.now(timezone.utc))

@property
def message_groups(self):
Expand Down Expand Up @@ -110,6 +110,19 @@ def render_plain_text(self):

return response.text(out)

class LogList:
def __init__(self, app, data, prefix, page, max_page, status_open, count_all):
self.app = app
self.logs = data
self.prefix = prefix
self.page = page
self.max_page = max_page
self.status_open = status_open
self.count_all = count_all

def render_html(self):
return self.app.ctx.render_template("loglist", data=self)


class User:
def __init__(self, data):
Expand Down Expand Up @@ -165,8 +178,8 @@ def __init__(self, data):
class Message:
def __init__(self, data):
self.id = int(data["message_id"])
self.created_at = dateutil.parser.parse(data["timestamp"])
self.human_created_at = duration(self.created_at, now=datetime.utcnow())
self.created_at = dateutil.parser.parse(data["timestamp"]).astimezone(timezone.utc)
self.human_created_at = duration(self.created_at, now=datetime.now(timezone.utc))
self.raw_content = data["content"]
self.content = self.format_html_content(self.raw_content)
self.attachments = [Attachment(a) for a in data["attachments"]]
Expand Down
108 changes: 108 additions & 0 deletions core/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
from functools import wraps

from datetime import datetime, timezone
import dateutil.parser
from natural.date import duration

import os
from dotenv import load_dotenv

load_dotenv()

def loglist():
def decorator(func):
@wraps(func)
async def wrapper(request):
app = request.app

config = await app.ctx.db.config.find_one()

logs_per_page = 25

if "PAGINATION" in os.environ:
try:
logs_per_page = int(os.environ["PAGINATION"])
except ValueError:
print("Invalid PAGINATION config var (must be a number). Defaulting to 25.")

collection = app.ctx.db.logs

try:
page = int(request.args.get("page", 1))
if page < 1: page = 1
except ValueError:
page = 1

def parse_date(date):
date = dateutil.parser.parse(date).astimezone(timezone.utc)
timestamp = duration(date, datetime.now(timezone.utc))
return timestamp

async def find():

filter_ = {"bot_id": str(config["bot_id"])}

count_all = await collection.count_documents(filter=filter_)

status_open = request.args.get("open")

if status_open == "false":
filter_["open"] = False
elif status_open == "true":
filter_["open"] = True
else: status_open = None

if request.args.get("search"):
search = request.args.get("search")
filter_["$text"] = { "$search": search }

projection_ = {
"key": 1,
"open": 1,
"created_at": 1,
"closed_at": 1,
"recipient": 1,
"creator": 1,
"title": 1,
"last_message": { "$arrayElemAt": [ "$messages", -1 ] },
"message_count": { "$size": "$messages" },
"nsfw": 1
}

cursor = collection.find(filter=filter_, projection=projection_, skip=(page-1)*logs_per_page).sort(
"created_at", -1
)

count = await collection.count_documents(filter=filter_)

max_page = count // logs_per_page
if (count % logs_per_page) > 0: max_page += 1

items = await cursor.to_list(length=logs_per_page)

# iterate over list to change timestamps to readable format
for index, item in enumerate(items):
creation_date = item.get('created_at')
items[index].update(created_at=parse_date(creation_date))
close_date = item.get('closed_at')

if close_date is not None:
items[index].update(closed_at=parse_date(close_date))

try:
last_message = items[index].get("last_message")
last_message_duration = parse_date(
last_message.get("timestamp")
)
items[index]["last_message_time"] = last_message_duration
except Exception:
pass

return items, max_page, status_open, count_all

document, max_page, status_open, count_all = await find()
return await func(request, document, page, max_page, status_open, count_all)

return wrapper

return decorator
9 changes: 5 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
-i https://pypi.org/simple
aiofiles==0.8.0; python_version >= '3.6' and python_version < '4.0'
aiofiles==22.1.0; python_version >= '3.7' and python_version < '4.0'
dnspython==2.2.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
httptools==0.4.0; python_full_version >= '3.5.0'
httptools==0.5.0; python_full_version >= '3.5.0'
jinja2==3.1.2
markupsafe==2.1.1; python_version >= '3.7'
motor==3.0.0
multidict==6.0.2; python_version >= '3.7'
natural==0.2.0
pymongo[srv]==4.2.0
python-dateutil==2.8.2
python-dotenv==0.18.0
sanic==22.6.2
sanic-routing==22.3.0
six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
ujson==5.4.0; sys_platform != 'win32' and implementation_name == 'cpython'
uvloop==0.16.0; sys_platform != 'win32' and implementation_name == 'cpython'
ujson==5.5.0; sys_platform != 'win32' and implementation_name == 'cpython'
uvloop==0.17.0; sys_platform != 'win32' and implementation_name == 'cpython'
websockets==10.3; python_version >= '3.7'
Loading