123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- from flask import Flask, render_template, make_response
- from flask_paginate import Pagination, get_page_parameter, get_page_args
- from flask_caching import Cache
- from flask import request
- import pendulum
- import subprocess
- import base64
- import os
- import textwrap
- import sys
- base_path = "/messagebase"
- app = Flask(__name__, static_url_path=base_path + "/static")
- @app.template_filter("datefmt")
- def format_datetime(value):
- # dt = pendulum.from_timestamp(value, tz=pendulum.tz.local_timezone())
- dt = pendulum.from_timestamp(value)
- return dt.to_datetime_string()
- # Check Configuring Flask-Caching section for more details
- # cache = Cache(app, config={"CACHE_TYPE": "filesystem", "CACHE_DIR": "cache"})
- cache = Cache(
- app, config={"CACHE_TYPE": "redis", "CACHE_REDIS_HOST": "redis"}
- )
- # cache = Cache(app, config={"CACHE_TYPE": "redis", "CACHE_REDIS_HOST": "olympus"})
- # import jammin
- import sqlite3
- dbconnect = sqlite3.connect("db/message.sqlite3")
- dbc = dbconnect.cursor()
- bases = {
- "FSXNET-General": "fsx_gen",
- "FSXNET-Ads": "fsx_ads",
- "FSXNET-BBS": "fsx_bbs",
- "FSXNET-BOT": "fsx_bot",
- # "FSXNET-Encryption": "msgs/fsx_cry",
- "FSXNET-Ham Radio": "fsx_ham",
- # "FSXNET-Magicka": "msgs/fsx_mag",
- "FSXNET-Magicka": "fsx_mag",
- "FSXNET-Mystic": "fsx_mys",
- "FSXNET-Enigma": "fsx_eng",
- # "HappyNet-General": "msgs/hpy_gen",
- }
- def bbs_get_messages(area):
- global dbc
- messages = []
- for row in dbc.execute(
- # "SELECT message_id, to_user_name, from_user_name, subject, modified_timestamp from message WHERE area_tag=?",
- "SELECT message_id, to_user_name, from_user_name, subject, modified_timestamp from message WHERE area_tag=? ORDER BY message_id;",
- (area,),
- ):
- stamp = pendulum.parse(row[4]).timestamp()
- messages.append(
- {
- "MsgNum": row[0],
- "number": row[0],
- "to": row[1],
- "from": row[2],
- "subject": row[3],
- "written": stamp,
- # // written
- # // received
- # // processed
- }
- )
- return messages
- def bbs_message(area, msgno):
- global dbc
- messages = []
- dbc.execute(
- "SELECT message_id, to_user_name, from_user_name, subject, modified_timestamp, message from message WHERE message_id=?",
- (msgno,),
- )
- row = dbc.fetchone()
- stamp = pendulum.parse(row[4]).timestamp()
- return {
- "MsgNum": row[0],
- "number": row[0],
- "to": row[1],
- "from": row[2],
- "subject": row[3],
- "written": stamp,
- "received": stamp,
- "processed": stamp,
- "text": row[5], # .decode("cp437"),
- "bytes": row[5].encode("cp437")
- # // written
- # // received
- # // processed
- }
- # bases = {"FSX_BOT": "fsx_bot"}
- # @cache.memoize(timeout=5 * 60, key_prefix="messages")
- @cache.memoize(timeout=5 * 60)
- def get_messages(base):
- messages = bbs_get_messages(base)
- messages.reverse()
- return messages
- @cache.memoize(timeout=60)
- def get_message(base, msgno):
- # message = jammin.read_message(base, msgno)
- message = bbs_message(base, msgno)
- return message
- @app.errorhandler(404)
- def not_found(e):
- return render_template("404.html")
- @app.route(base_path + "/list")
- def list_bases():
- return render_template(
- "list.html", bases=bases, base_path=base_path, title="Message Areas"
- )
- # return 'Here would be a listing of message bases'
- @app.route(base_path + "/clear")
- def clear_cache():
- cache.clear()
- return "Cache Cleared. Back to hitting refresh!"
- @app.route(base_path + "/messages/<area>")
- def display_messages(area):
- if area not in bases:
- return render_template(
- "missing-area.html", base_path=base_path, title="Missing Area"
- )
- # messages = jammin.get_messages(bases[area])
- messages = get_messages(bases[area])
- # messages.reverse() # cached.reverse()
- page = request.args.get(get_page_parameter(), type=int, default=1)
- # get_page_arg defaults to page 1, per_page of 10
- PER_PAGE = 50
- total = len(messages)
- pagination = Pagination(
- page=page,
- total=total,
- css_framework="foundation",
- record_name="messages",
- per_page=PER_PAGE,
- )
- page, per_page, offset = get_page_args()
- start = (page - 1) * PER_PAGE
- end = start + PER_PAGE
- # messages = messages[(page-1) * PER_PAGE:offset+PER_PAGE]
- messages = messages[start:end]
- return render_template(
- "messages.html",
- messages=messages,
- area=area,
- pagination=pagination,
- base_path=base_path,
- title="Messages for " + bases[area],
- )
- @cache.memoize(timeout=60)
- def ansi_to_png(raw_ansi_bytes, idx):
- pid = os.getppid()
- ansifile = "{0}-{1}.ans".format(idx, pid)
- pngfile = "{0}-{1}.png".format(idx, pid)
- with open(ansifile, "wb") as fp:
- fp.write(raw_ansi_bytes)
- subprocess.run(["./ansilove", "-d", "-o", pngfile, ansifile])
- with open(pngfile, "rb") as fp:
- png = fp.read()
- os.unlink(ansifile)
- os.unlink(pngfile)
- return png
- def ansi_to_png64(raw_ansi_bytes, idx):
- png = ansi_to_png(raw_ansi_bytes, idx)
- return base64.b64encode(png).decode("utf-8")
- @app.route(base_path + "/image/<area>/<int:msgno>.png")
- def display_ansi(area, msgno):
- if area not in bases:
- return "RATS", 404
- message = get_message(bases[area], msgno)
- if not message:
- return "RATS", 404
- if not "text" in message:
- return "RATS", 404
- # png = ansi_to_png(message["bytes"].replace(b"\r", b"\n"), msgno)
- png = ansi_to_png(message["bytes"], msgno)
- # png = ansi_to_png(message["bytes"].replace("\r", "\n"), msgno)
- response = make_response(png)
- response.headers.set("Content-Type", "image/png")
- return response
- # <img alt="My Image" src="data:image/png;base64,
- @app.route(base_path + "/read/<area>/<int:msgno>")
- def display_message(area, msgno):
- if area not in bases:
- return render_template(
- "missing-area.html", base_path=base_path, title="Missing Area"
- )
- # message = jammin.read_message(bases[area], msgno)
- message = get_message(bases[area], msgno)
- if not message:
- return render_template(
- "missing-message.html",
- base_path=base_path,
- area=area,
- title="Missing Message",
- )
- messages = get_messages(bases[area])
- # prevmsg and nextmsg are completely different now.
- prevmsg = None
- nextmsg = None
- total = len(messages)
- for idx, msg in enumerate(messages):
- if msg["MsgNum"] == msgno:
- # Ok, found what we're looking for
- if idx > 0:
- prevmsg = messages[idx - 1]["MsgNum"]
- if idx + 1 < total:
- nextmsg = messages[idx + 1]["MsgNum"]
- # prevmsg = None
- # nextmsg = None
- # if msgno > 1:
- # prevmsg = msgno - 1
- # if msgno < total:
- # nextmsg = msgno + 1
- if "text" in message:
- if "\x1b" in message["text"]:
- # Ok, the message contains ANSI CODES -- Convert
- message["png"] = True
- # message["png"] = ansi_to_png64(
- # message["bytes"].replace(b"\r", b"\n"), msgno
- # )
- else:
- text = message["text"].replace("\r", "\n")
- # Ok, latest changes aren't doing word-wrap for us, so do it here.
- text = "\n".join(
- [
- textwrap.fill(txt, width=78, replace_whitespace=False)
- for txt in text.splitlines()
- ]
- )
- message["text"] = text
- # message["text"].replace("\r", "\n") # <br >\n")
- return render_template(
- "message.html",
- message=message,
- area=area,
- msgnumber=msgno,
- prevmsg=prevmsg,
- nextmsg=nextmsg,
- base_path=base_path,
- title="Message {0}".format(msgno),
- )
|