-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
boke0
committed
Mar 7, 2021
0 parents
commit 5af8be8
Showing
122 changed files
with
11,836 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
kyokusui/__pycache__/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from mitama.app import Builder | ||
|
||
from .main import App | ||
|
||
|
||
class AppBuilder(Builder): | ||
app = App |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
from mitama.app import Controller | ||
from mitama.app.http import Response | ||
from mitama.models import User, Node, Role, is_admin | ||
from datetime import datetime | ||
import json | ||
import re | ||
import markdown | ||
import magic | ||
|
||
from .model import db, Board, Thread, Res, Permission | ||
from .forms import CreateBoardForm, CreateThreadForm, SettingForm | ||
from .utils import hiroyuki | ||
|
||
class HomeController(Controller): | ||
def handle(self, request): | ||
template = self.view.get_template("home.html") | ||
threads = list() | ||
for board in Board.list_subscribed(request.user): | ||
for thread in board.threads: | ||
if not thread.closed: | ||
threads.append(thread) | ||
return Response.render(template, { | ||
"is_admin": is_admin, | ||
"threads": threads, | ||
"boards": Board.list_subscribed(request.user) | ||
}) | ||
def settings(self, request): | ||
template = self.view.get_template("settings.html") | ||
error = "" | ||
if request.method == "POST": | ||
form = SettingForm(request.post()) | ||
for permission_screen_name, permission_roles in form['permissions'].items(): | ||
permission = Permission.retrieve(screen_name = permission_screen_name) | ||
permission.roles = [Role.retrieve(screen_name = r) for r in permission_roles] | ||
permission.update() | ||
error = "保存しました" | ||
return Response.render(template, { | ||
"permissions": Permission.list(), | ||
"roles": Role.list(), | ||
"error": error | ||
}) | ||
|
||
class BoardController(Controller): | ||
def create(self, request): | ||
form = CreateBoardForm(request.post()) | ||
board = Board() | ||
board.name = form['name'] | ||
board.owner = Node.retrieve(form['owner']) | ||
board.create() | ||
return Response.json({ | ||
"_id": board._id, | ||
}) | ||
|
||
def retrieve(self, request): | ||
template = self.view.get_template("board.html") | ||
board = Board.retrieve(request.params['board']) | ||
return Response.render(template, { | ||
"board": board, | ||
"boards": Board.list_subscribed(request.user) | ||
}) | ||
|
||
def subscribe(self, request): | ||
board = Board.retrieve(request.params['board']) | ||
board.subscribers.append(request.user) | ||
board.update() | ||
return Response.json({ | ||
"subscribe": True, | ||
"board": { | ||
"_id": board._id | ||
} | ||
}) | ||
|
||
def unsubscribe(self, request): | ||
board = Board.retrieve(request.params['board']) | ||
board.subscribers.remove(request.user) | ||
board.update() | ||
return Response.json({ | ||
"subscribe": False, | ||
"board": { | ||
"_id": board._id | ||
} | ||
}) | ||
|
||
class UserController(Controller): | ||
def icon(self, request): | ||
user = User.retrieve(request.params['user']) | ||
f = magic.Magic(mime=True, uncompress=True) | ||
mime = f.from_buffer(self.icon) | ||
return Response(user._icon, content_type=mime) | ||
|
||
class ThreadController(Controller): | ||
def create(self, request): | ||
form = CreateThreadForm(request.post()) | ||
board = Board.retrieve(request.params['board']) | ||
thread = Thread() | ||
thread.title = form['title'] | ||
thread.board = board | ||
thread.user = request.user | ||
thread.create() | ||
for user in board.subscribers: | ||
user.push({ | ||
"title": res.thread.title + " - Kyokusui", | ||
"body": "スレッドが立ちました", | ||
"icon": self.app.convert_fullurl(request, "/user/{}/icon".format(requst.user._id)) | ||
}) | ||
return Response.json({ | ||
"_id": thread._id, | ||
}) | ||
|
||
def retrieve(self, request): | ||
template = self.view.get_template("thread.html") | ||
board = Board.retrieve(request.params['board']) | ||
thread = Thread.retrieve(request.params['thread']) | ||
return Response.render(template, { | ||
"board": board, | ||
"thread": thread, | ||
"boards": Board.list_subscribed(request.user) | ||
}) | ||
|
||
class WebSocketController(Controller): | ||
streams = {} | ||
def handle(self, request): | ||
ws = request.websocket | ||
board = Board.retrieve(request.params['board']) | ||
thread = Thread.retrieve(request.params['thread']) | ||
if board._id not in self.streams: | ||
self.streams[board._id] = dict() | ||
if thread._id not in self.streams[board._id]: | ||
self.streams[board._id][thread._id] = list() | ||
self.streams[board._id][thread._id].append(ws) | ||
db.manager.close_session() | ||
while True: | ||
received = ws.receive() | ||
if received is None: | ||
continue | ||
try: | ||
db.manager.start_session() | ||
data = json.loads(received) | ||
if data['type'] == "message": | ||
res = Res() | ||
res.data = json.dumps(data['data']) | ||
res.user = request.user | ||
res.thread = Thread.retrieve(request.params['thread']) | ||
res.create() | ||
mentions = re.findall(r"\>\>([0-9a-zA-Z.-_]+)", data['data']['message']) | ||
for mention in mentions: | ||
try: | ||
user = User.retrieve(screen_name = mention) | ||
user.push({ | ||
"title": res.thread.title + " - Kyokusui", | ||
"body": res.user.name+"さんがメンションしました", | ||
"icon": self.app.convert_fullurl(request, "/user/{}/icon".format(res.user._id)) | ||
}) | ||
except Exception as err: | ||
print(err, mention) | ||
pass | ||
remove = list() | ||
for s in self.streams[board._id][thread._id]: | ||
try: | ||
s.send(json.dumps({ | ||
"type": "message", | ||
"_id": res._id, | ||
"data": { | ||
"message": markdown.markdown(hiroyuki(res.parsed_data["message"]), extensions=['fenced_code']), | ||
"images": res.parsed_data["images"] | ||
}, | ||
"user": { | ||
"_id": res.user._id, | ||
"name": res.user.name, | ||
"screen_name": res.user.screen_name | ||
}, | ||
"datetime": res.datetime.strftime("%Y-%m-%d %H:%M:%S"), | ||
"thread": thread._id, | ||
})) | ||
except Exception as err: | ||
print(err) | ||
remove.append(s) | ||
for s in remove: | ||
self.streams[board._id][thread._id].remove(s) | ||
elif data['type'] == "close": | ||
thread = Thread.retrieve(request.params['thread']) | ||
thread.close() | ||
remove = list() | ||
for s in self.streams[board._id][thread._id]: | ||
try: | ||
s.send(json.dumps({ | ||
"type": "close" | ||
})) | ||
except Exception as err: | ||
print(err) | ||
pass | ||
del self.streams[board._id][thread._id] | ||
except Exception as err: | ||
print(err) | ||
db.manager.rollback_session() | ||
finally: | ||
db.manager.close_session() | ||
return Response() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from mitama.app.forms import Form, Field, DictField | ||
|
||
|
||
class CreateBoardForm(Form): | ||
name = Field(label="タイトル", required=True) | ||
owner = Field(label="板主", required=True) | ||
|
||
class CreateThreadForm(Form): | ||
title = Field(label="タイトル", required=True) | ||
|
||
class SettingForm(Form): | ||
permissions = DictField(label="権限", listed=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
from mitama.app import App, Router | ||
from mitama.utils.controllers import static_files | ||
from mitama.utils.middlewares import SessionMiddleware | ||
from mitama.app.method import view, post | ||
|
||
from .controller import HomeController, BoardController, ThreadController, WebSocketController, UserController | ||
from .model import Board, Thread, Res, Permission | ||
from .utils import hiroyuki | ||
|
||
class App(App): | ||
name = 'Kyokusui' | ||
description = '業務用便所の落書き' | ||
router = Router( | ||
[ | ||
view("/", HomeController), | ||
view("/settings", HomeController, 'settings'), | ||
view("/static/<path:path>", static_files()), | ||
view("/<board>", BoardController, 'retrieve'), | ||
view("/<board>/<thread>", ThreadController, 'retrieve'), | ||
view("/user/<user>/icon", UserController, 'icon'), | ||
post("/api/v0/board", BoardController, 'create'), | ||
post("/api/v0/board/<board>", ThreadController, 'create'), | ||
post("/api/v0/board/<board>/subscribe", BoardController, 'subscribe'), | ||
post("/api/v0/board/<board>/unsubscribe", BoardController, 'unsubscribe'), | ||
view("/api/v0/board/<board>/<thread>/socket", WebSocketController), | ||
], | ||
middlewares=[SessionMiddleware] | ||
) | ||
models = [Board, Thread, Res, Permission] | ||
|
||
@property | ||
def view(self): | ||
view = super().view | ||
view.globals['permission'] = Permission.is_accepted | ||
view.filters['hiroyuki'] = hiroyuki | ||
return view |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
from mitama.db import BaseDatabase, Table, relationship | ||
from mitama.db.model import UUID | ||
from mitama.db.types import * | ||
from mitama.models import User, Group, Node, permission | ||
from datetime import datetime | ||
import json | ||
|
||
|
||
class Database(BaseDatabase): | ||
pass | ||
|
||
|
||
db = Database(prefix="kyokusui") | ||
|
||
class Board(db.Model): | ||
name = Column(String(255)) | ||
owner_id = Column(String(64), ForeignKey("mitama_node._id")) | ||
owner = relationship(Node) | ||
created = Column(DateTime, default=datetime.now) | ||
subscribers = relationship(User, secondary="kyokusui_subscribe") | ||
|
||
@classmethod | ||
def list_subscribed(cls, user): | ||
boards_ = cls.list() | ||
boards = list() | ||
for board in boards_: | ||
if board.owner.object == user or (isinstance(board.owner.object, Group) and board.owner.object.is_in(user)) or user in board.subscribers: | ||
boards.append(board) | ||
return boards | ||
|
||
class Thread(db.Model): | ||
board_id = Column(String(64), ForeignKey("kyokusui_board._id")) | ||
board = relationship(Board, backref="threads") | ||
user_id = Column(String(64), ForeignKey("mitama_user._id")) | ||
user = relationship(User) | ||
created = Column(DateTime, default=datetime.now) | ||
title = Column(String(1024)) | ||
closed = Column(Boolean, default=False) | ||
|
||
def close(self): | ||
self.closed = True | ||
self.update() | ||
|
||
class Res(db.Model): | ||
thread_id = Column(String(64), ForeignKey("kyokusui_thread._id")) | ||
thread = relationship(Thread, backref="res") | ||
user_id = Column(String(64), ForeignKey("mitama_user._id")) | ||
user = relationship(User) | ||
datetime = Column(DateTime, default=datetime.now) | ||
data = Column(Text) | ||
|
||
@property | ||
def parsed_data(self): | ||
return json.loads(self.data) | ||
|
||
subscribe_table = Table("kyokusui_subscribe", | ||
db.metadata, | ||
Column("_id", String(64), default=UUID(), primary_key=True), | ||
Column("user_id", String(64), ForeignKey("mitama_user._id", ondelete="CASCADE")), | ||
Column("board_id", String(64), ForeignKey("kyokusui_board._id", ondelete="CASCADE")) | ||
) | ||
|
||
class Subscribe(db.Model): | ||
__table__ = subscribe_table | ||
user_id = subscribe_table.c.user_id, | ||
board_id = subscribe_table.c.board_id, | ||
user = relationship(User) | ||
board = relationship(Board) | ||
|
||
Permission = permission(db, [ | ||
{ | ||
"name": "板を立てる", | ||
"screen_name": "create_board" | ||
}, | ||
{ | ||
"name": "板を閉じる", | ||
"screen_name": "delete_board" | ||
} | ||
]) | ||
|
||
db.create_all() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
Oops, something went wrong.