~melmon/at-channel

9fff9502efcda25bdda9b04ec66dfaa6105397ac — M3LMON 2 years ago
Initial commit
A  => .gitignore +202 -0
@@ 1,202 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
#   For a library or package, you might want to ignore these files since the code is
#   intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# Coverage reports
htmlcov/
.coverage
.coverage.*
*,cover

# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf

# AWS User-specific
.idea/**/aws.xml

# Generated files
.idea/**/contentModel.xml

# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml

# CMake
cmake-build-*/

# Mongo Explorer plugin
.idea/**/mongoSettings.xml

# File-based project format
*.iws

# IntelliJ
out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Cursive Clojure plugin
.idea/replstate.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

# Editor-based Rest Client
.idea/httpRequests

# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
\ No newline at end of file

A  => .idea/.gitignore +8 -0
@@ 1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

A  => .idea/atchannel.iml +21 -0
@@ 1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
  <component name="Flask">
    <option name="enabled" value="true" />
  </component>
  <component name="NewModuleRootManager">
    <content url="file://$MODULE_DIR$">
      <excludeFolder url="file://$MODULE_DIR$/venv" />
    </content>
    <orderEntry type="inheritedJdk" />
    <orderEntry type="sourceFolder" forTests="false" />
  </component>
  <component name="TemplatesService">
    <option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
    <option name="TEMPLATE_FOLDERS">
      <list>
        <option value="$MODULE_DIR$/atch/templates" />
      </list>
    </option>
  </component>
</module>
\ No newline at end of file

A  => .idea/inspectionProfiles/Project_Default.xml +13 -0
@@ 1,13 @@
<component name="InspectionProjectProfileManager">
  <profile version="1.0">
    <option name="myName" value="Project Default" />
    <inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
      <option name="ignoredErrors">
        <list>
          <option value="N802" />
          <option value="N803" />
        </list>
      </option>
    </inspection_tool>
  </profile>
</component>
\ No newline at end of file

A  => .idea/inspectionProfiles/profiles_settings.xml +6 -0
@@ 1,6 @@
<component name="InspectionProjectProfileManager">
  <settings>
    <option name="USE_PROJECT_PROFILE" value="false" />
    <version value="1.0" />
  </settings>
</component>
\ No newline at end of file

A  => .idea/misc.xml +4 -0
@@ 1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (atchannel)" project-jdk-type="Python SDK" />
</project>
\ No newline at end of file

A  => .idea/modules.xml +8 -0
@@ 1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectModuleManager">
    <modules>
      <module fileurl="file://$PROJECT_DIR$/.idea/atchannel.iml" filepath="$PROJECT_DIR$/.idea/atchannel.iml" />
    </modules>
  </component>
</project>
\ No newline at end of file

A  => .idea/vcs.xml +6 -0
@@ 1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="VcsDirectoryMappings">
    <mapping directory="$PROJECT_DIR$" vcs="Git" />
  </component>
</project>
\ No newline at end of file

A  => atch/__init__.py +27 -0
@@ 1,27 @@
import os
import db
from flask import Flask, g


def create_app(test_config=None):
    app = Flask(__name__, instance_relative_config=True)
    app['SECRET_KEY'] = "nya"
    app['DATABASE'] = os.path.join(app.instance_path, 'atch.sqlite')

    if test_config is None:
        app.config.from_pyfile('config.py', silent=True)
    else:
        app.config.from_mapping(test_config)

    try:
        os.makedirs(app.instance_path)
    except OSError:
        pass

    db.init_db()

    g.boards = db.get_db().execute('SELECT uri FROM boards').fetchall()


def get_boards():
    pass

A  => atch/board.py +46 -0
@@ 1,46 @@
from flask import Blueprint, flash, g, redirect, render_template, request, session, url_for, abort
from atch.db import get_db
from hashlib import sha1

bp = Blueprint('board', __name__)


@bp.route('/<string:uri>/<int:page>')
def browse_board(uri, page):
    db = get_db()
    threads = db.execute(
        'SELECT thread, thread_pos, board, created, op, email, title, body '
        'FROM posts WHERE board == ? '
        'ORDER BY created DESC',
        uri
    ).fetchmany(5 + 5 * page)

    return render_template('board.html', threads=threads)


@bp.route('/<string:uri>/new_thread', methods=['GET', 'POST'])
def new_thread(uri):
    if request.method == 'POST':
        op = ""
        if request.form['op']:
            op = request.form['op']
            if '#' in op:  # process tripcode
                go = op.index('#')
                trip = sha1(op[go:].encode('utf-8'))
                op = op[:go]
                op = op + "◆" + trip.hexdigest()[:10]
        else:
            op = "Anonymous"
        email = request.form['email']
        title = request.form['title']
        body = request.form['body']
        db = get_db()
        thread_number = db.execute('SELECT new_thread_number, uri FROM boards WHERE uri == ?', uri)
        db.execute('UPDATE boards SET new_thread_number = new_thread_number + 1 WHERE uri == ?', uri)
        db.execute(
            'INSERT INTO posts (thread, thread_pos, board, op, email, title, body) VALUES (?, 0, ?, ?, ?, ?, ?)',
            [thread_number[0], uri, op, email, title, body]
        )
        db.commit()

    abort(403)

A  => atch/db.py +38 -0
@@ 1,38 @@
import sqlite3
import click
from flask import current_app, g
from flask.cli import with_appcontext


def get_db():
    if 'db' not in g:
        g.db = sqlite3.connect(
            current_app.config['DATABASE'],
            detect_types=sqlite3.PARSE_DECLTYPES
        )
        g.db.row_factory = sqlite3.Row

    return g.db


def close_db(e=None):
    db = g.pop('db', None)

    if db is not None:
        db.close()


def init_db():
    db = get_db()

    with current_app.open_resource('schema.sql') as f:
        db.executescript(f.read().decode('utf8'))


@click.command('add_board')
@click.argument('uri')
@click.argument('name')
@with_appcontext
def add_board(uri, name):
    get_db().execute('INSERT INTO boards VALUES (?, ?)', [uri, name])
    click.echo('Added board /' + uri + "/ - " + name)
\ No newline at end of file

A  => atch/schema.sql +20 -0
@@ 1,20 @@
CREATE TABLE IF NOT EXISTS boards (
    uri  VARCHAR(7) NOT NULL PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    new_thread_number INTEGER NOT NULL DEFAULT 0
);

CREATE TABLE IF NOT EXISTS posts (
    thread INTEGER NOT NULL,
    thread_pos INTEGER NOT NULL DEFAULT 0,
    board VARCHAR(7) NOT NULL,
    created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    bump_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    op VARCHAR(50) NOT NULL,
    email VARCHAR (50),
    title VARCHAR(50),
    body TEXT NOT NULL,

    PRIMARY KEY (thread, thread_pos, board),
    FOREIGN KEY (board) REFERENCES boards(uri)
);
\ No newline at end of file

A  => atch/static/css/style.css +0 -0
A  => atch/static/js/reply.js +0 -0
A  => atch/templates/base.html +26 -0
@@ 1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}{% endblock %} - @channel</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
</head>
<body>
<nav>
    <h1>Boards </h1>
    <ul>[
        {% for board in g.boards %}
            <li><a href="">{{ board.uri }}</a> </li>
    </ul>]
</nav>
<section class="content">
    <header>
        {% block header %}{% endblock %}
    </header>
    {% for message in get_flashed_messages() %}
        <div class="flash">{{ message }}</div>
    {% endfor %}
    {% block content %}{% endblock %}
</section>
</body>
</html>
\ No newline at end of file

A  => atch/templates/board.html +10 -0
@@ 1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
</html>
\ No newline at end of file

A  => atch/templates/thread.html +10 -0
@@ 1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
</html>
\ No newline at end of file

A  => setup.py +12 -0
@@ 1,12 @@
from setuptools import setup, find_packages


setup(
    name='atchannel',
    packages=find_packages(),
    include_package_data=True,
    zip_safe=False,
    install_requires=[
        'flask', 'werkzeug', 'click'
    ],
)
\ No newline at end of file