mirror of
https://0xacab.org/johnxina/rat.git
synced 2024-12-23 13:09:08 +00:00
Add terminal web browser support from Hatsuharu
This commit is contained in:
parent
33baaeb89b
commit
6821384f4b
55
app.py
55
app.py
@ -2,6 +2,9 @@ import asyncio
|
|||||||
import aiotieba
|
import aiotieba
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
|
import re
|
||||||
|
import textwrap
|
||||||
|
|
||||||
from aioflask import render_template, request, escape
|
from aioflask import render_template, request, escape
|
||||||
from urllib.parse import quote_plus
|
from urllib.parse import quote_plus
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@ -33,6 +36,25 @@ async def cache_name_from_id(c, i):
|
|||||||
def normalize_utf8(s):
|
def normalize_utf8(s):
|
||||||
return s.encode('unicode_escape').decode('ascii').replace('\\', '')
|
return s.encode('unicode_escape').decode('ascii').replace('\\', '')
|
||||||
|
|
||||||
|
# Render the template with compatibility detection.
|
||||||
|
def render_template_c(tmpl, **kwargs):
|
||||||
|
text_browsers = ['w3m', 'Lynx', 'ELinks', 'Links', 'URL/Emacs', 'Emacs']
|
||||||
|
ua = request.headers.get('User-Agent')
|
||||||
|
|
||||||
|
for text_ua in text_browsers:
|
||||||
|
if ua.startswith(text_ua):
|
||||||
|
return render_template(f'{tmpl}.text', **kwargs)
|
||||||
|
return render_template(tmpl, **kwargs)
|
||||||
|
|
||||||
|
# Wrap the text to the given width.
|
||||||
|
def wrap_text(i, width=70, join='\n', rws=False):
|
||||||
|
i = str(i)
|
||||||
|
def add_whitespace_to_chinese(match):
|
||||||
|
return '\uFFFD' + match.group(0)
|
||||||
|
pattern = r'[\u4e00-\u9fff\uFF00-\uFFEF]'
|
||||||
|
aft = re.sub(pattern, add_whitespace_to_chinese, i)
|
||||||
|
return join.join(textwrap.wrap(aft, width=width, replace_whitespace=rws)).replace('\uFFFD', '')
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
# Convert a timestamp to its simpliest readable date format.
|
# Convert a timestamp to its simpliest readable date format.
|
||||||
@ -63,6 +85,15 @@ def _jinja2_filter_intsep(i):
|
|||||||
def _jinja2_filter_trim(text):
|
def _jinja2_filter_trim(text):
|
||||||
return text[:78] + '……' if len(text) > 78 else text
|
return text[:78] + '……' if len(text) > 78 else text
|
||||||
|
|
||||||
|
# Format comments to its equiviant text HTML.
|
||||||
|
@app.template_filter('tcomments')
|
||||||
|
async def _jinja2_filter_tcomments(coms):
|
||||||
|
buf = ' | '
|
||||||
|
for com in coms:
|
||||||
|
buf += wrap_text(f'{ com.user.show_name }:{ com.text }', width=60, join="\n | ")
|
||||||
|
buf += '\n \---\n | '
|
||||||
|
return buf[:-4]
|
||||||
|
|
||||||
# Format fragments to its equiviant HTML.
|
# Format fragments to its equiviant HTML.
|
||||||
@app.template_filter('translate')
|
@app.template_filter('translate')
|
||||||
async def _jinja2_filter_translate(frags, reply_id=0):
|
async def _jinja2_filter_translate(frags, reply_id=0):
|
||||||
@ -105,6 +136,10 @@ async def _jinja2_filter_translate(frags, reply_id=0):
|
|||||||
|
|
||||||
return htmlfmt
|
return htmlfmt
|
||||||
|
|
||||||
|
@app.template_filter('twrap')
|
||||||
|
async def _jinja2_filter_translate(text, width=70, join='\n', rws=False):
|
||||||
|
return wrap_text(text, width, join, rws)
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
@app.route('/p/<tid>')
|
@app.route('/p/<tid>')
|
||||||
@ -134,7 +169,7 @@ async def thread_view(tid):
|
|||||||
|
|
||||||
await asyncio.gather(*(cache_name_from_id(tieba, i) for i in all_users))
|
await asyncio.gather(*(cache_name_from_id(tieba, i) for i in all_users))
|
||||||
|
|
||||||
return await render_template('thread.html', info=thread_info, ao=ao)
|
return await render_template_c('thread.html', info=thread_info, ao=ao)
|
||||||
|
|
||||||
@app.route('/f')
|
@app.route('/f')
|
||||||
async def forum_view():
|
async def forum_view():
|
||||||
@ -156,10 +191,10 @@ async def forum_view():
|
|||||||
'member': forum_info.member_num, 'desc': '贴吧描述暂不可用', 'name': forum_info.fname }
|
'member': forum_info.member_num, 'desc': '贴吧描述暂不可用', 'name': forum_info.fname }
|
||||||
|
|
||||||
if threads.page.current_page > threads.page.total_page or pn < 1:
|
if threads.page.current_page > threads.page.total_page or pn < 1:
|
||||||
return await render_template('error.html', msg = \
|
return await render_template_c('error.html', msg = \
|
||||||
f'请求越界,本贴吧共有 { threads.page.total_page } 页'
|
f'请求越界,本贴吧共有 { threads.page.total_page } 页'
|
||||||
f'而您查询了第 { threads.page.current_page} 页')
|
f'而您查询了第 { threads.page.current_page} 页')
|
||||||
return await render_template('bar.html', info=forum_info, threads=threads, sort=sort,
|
return await render_template_c('bar.html', info=forum_info, threads=threads, sort=sort,
|
||||||
tp = ((115 if threads.page.total_page > 115 else threads.page.total_page) if sort == 0 else threads.page.total_page))
|
tp = ((115 if threads.page.total_page > 115 else threads.page.total_page) if sort == 0 else threads.page.total_page))
|
||||||
|
|
||||||
@app.route('/home/main')
|
@app.route('/home/main')
|
||||||
@ -175,28 +210,28 @@ async def user_view():
|
|||||||
try:
|
try:
|
||||||
hp = await tieba.get_homepage(i, pn)
|
hp = await tieba.get_homepage(i, pn)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return await render_template('error.html', msg='您已超过最后页')
|
return await render_template_c('error.html', msg='您已超过最后页')
|
||||||
|
|
||||||
if len(hp[1]) == 0 and pn > 1:
|
if len(hp[1]) == 0 and pn > 1:
|
||||||
return await render_template('error.html', msg='您已超过最后页')
|
return await render_template_c('error.html', msg='您已超过最后页')
|
||||||
|
|
||||||
return await render_template('user.html', hp=hp, pn=pn)
|
return await render_template_c('user.html', hp=hp, pn=pn)
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
async def main_view():
|
async def main_view():
|
||||||
return await render_template('index.html')
|
return await render_template_c('index.html')
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
@app.errorhandler(RuntimeError)
|
@app.errorhandler(RuntimeError)
|
||||||
async def runtime_error_view(e):
|
async def runtime_error_view(e):
|
||||||
if hasattr(e, 'msg'):
|
if hasattr(e, 'msg'):
|
||||||
return await render_template('error.html', msg=e.msg)
|
return await render_template_c('error.html', msg=e.msg)
|
||||||
return await render_template('error.html', msg='错误信息不可用')
|
return await render_template_c('error.html', msg='错误信息不可用')
|
||||||
|
|
||||||
@app.errorhandler(Exception)
|
@app.errorhandler(Exception)
|
||||||
async def general_error_view(e):
|
async def general_error_view(e):
|
||||||
return await render_template('error.html', msg=e)
|
return await render_template_c('error.html', msg=e)
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
|
54
templates/bar.html.text
Normal file
54
templates/bar.html.text
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>{{ info['name'] }}吧 - RAT</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre>
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
欢迎来到{{info['name']}}吧! {{info['desc']}}
|
||||||
|
关注 {{ info['member']|intsep }} 主题 {{ info['topic']|intsep }} 帖子 {{ info['thread']|intsep }}
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
<a {% if sort == 0 %} class="current-sel" {% endif %} href="/f?kw={{ info['name'] }}&pn={{ threads.page.current_page }}&sort=0">[时下热门]</a> <a {% if sort == 5 %} class="current-sel" {% endif %} href="/f?kw={{ info['name'] }}&sort=5">[最新回复]</a> <a {% if sort == 1 %} class="current-sel" {% endif %} href="/f?kw={{ info['name'] }}&sort=1">[最新发布]</a></pre>
|
||||||
|
{% for t in threads %}
|
||||||
|
<pre>----- {{ '%4s'|format(t.reply_num) }} {{ '/置顶/ ' if t.is_top or t.is_livepost else '' }}{{ '/精/ ' if t.is_good else '' }}
|
||||||
|
+ 发帖人:{{ t.user.show_name }}
|
||||||
|
+ 最近回复时间: {{ t.last_time|date }}
|
||||||
|
|
||||||
|
<a href="/p/{{ t.tid }}">{{ t.title if t.title else t.text|twrap }}</a>
|
||||||
|
{% if t.title %}
|
||||||
|
{{ t.text[(t.title|length)+1:]|twrap(rws=True) }}{% endif %}</pre>
|
||||||
|
{% endfor %}
|
||||||
|
<pre>
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
</pre>
|
||||||
|
<a href="/f?kw={{ info['name'] }}&sort={{ sort }}">首页</a> |
|
||||||
|
|
||||||
|
{% for i in range(5) %}
|
||||||
|
{% set np = threads.page.current_page - 5 + i %}
|
||||||
|
{% if np > 0 %}
|
||||||
|
<a href="/f?kw={{ info['name'] }}&pn={{ np }}&sort={{ sort }}">{{ np }}</a> |
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<strong>{{ threads.page.current_page }}</strong> |
|
||||||
|
|
||||||
|
{% for i in range(5) %}
|
||||||
|
{% set np = threads.page.current_page + 1 + i %}
|
||||||
|
{% if np <= tp %}
|
||||||
|
<a href="/f?kw={{ info['name'] }}&pn={{ np }}&sort={{ sort }}">{{ np }}</a> |
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<a href="/f?kw={{ info['name'] }}&pn={{ tp }}&sort={{ sort }}">尾页</a>
|
||||||
|
<pre>
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
<a href="/">RAT Ain't Tieba</a> <a href="https://0xacab.org/johnxina/rat/-/raw/no-masters/LICENSE">自豪地以 AGPLv3 释出</a> <a href="https://0xacab.org/johnxina/rat">源代码</a>
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
41
templates/error.html.text
Normal file
41
templates/error.html.text
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>错误 - RAT</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre>
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
Guru Meditation c'est la vie
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
██░▀██████████████▀░██
|
||||||
|
█▌▒▒░████████████░▒▒▐█
|
||||||
|
█░▒▒▒░██████████░▒▒▒░█
|
||||||
|
▌░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▐
|
||||||
|
░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░
|
||||||
|
███▀▀▀██▄▒▒▒▒▒▒▒▄██▀▀▀██
|
||||||
|
██░░░▐█░▀█▒▒▒▒▒█▀░█▌░░░█
|
||||||
|
▐▌░░░▐▄▌░▐▌▒▒▒▐▌░▐▄▌░░▐▌
|
||||||
|
█░░░▐█▌░░▌▒▒▒▐░░▐█▌░░█
|
||||||
|
▒▀▄▄▄█▄▄▄▌░▄░▐▄▄▄█▄▄▀▒
|
||||||
|
░░░░░░░░░░└┴┘░░░░░░░░░
|
||||||
|
██▄▄░░░░░░░░░░░░░░▄▄██
|
||||||
|
████████▒▒▒▒▒▒████████
|
||||||
|
█▀░░███▒▒░░▒░░▒▀██████
|
||||||
|
█▒░███▒▒╖░░╥░░╓▒▐█████
|
||||||
|
█▒░▀▀▀░░║░░║░░║░░█████
|
||||||
|
██▄▄▄▄▀▀┴┴╚╧╧╝╧╧╝┴┴███
|
||||||
|
██████████████████████`
|
||||||
|
|
||||||
|
Cat says, * {{ msg|twrap }} *.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
<a href="/">RAT Ain't Tieba</a> <a href="https://0xacab.org/johnxina/rat/-/blob/no-masters/LICENSE">自豪地以 AGPLv3 释出</a> <a href="https://0xacab.org/johnxina/rat">源代码</a>
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
49
templates/index.html.text
Normal file
49
templates/index.html.text
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>首页 - RAT</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre>
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
欢迎来到 RAT! RAT Ain't Tieba 是一款自由的百度贴吧前端。
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
______ ___ _____ ___ _ _ _ _____ _ _
|
||||||
|
| ___ \/ _ |_ _| / _ \(_) ( | | |_ _(_) | |
|
||||||
|
| |_/ / /_\ \| | / /_\ \_ _ __ |/| |_ | | _ ___| |__ __ _
|
||||||
|
| /| _ || | | _ | | '_ \ | __| | | | |/ _ | '_ \ / _` |
|
||||||
|
| |\ \| | | || | | | | | | | | | | |_ | | | | __| |_) | (_| |
|
||||||
|
\_| \_\_| |_/\_/ \_| |_|_|_| |_| \__| \_/ |_|\___|_.__/ \__,_|
|
||||||
|
|
||||||
|
|
||||||
|
您正在查看的是为终端浏览器优化过的前端页面。如果您认为您的浏览器不应该
|
||||||
|
使用此页面,请于下方报告缺陷。如果您的终端浏览器不能很好地处理页面,您
|
||||||
|
也可以报告缺陷。此前端页面由 *初春トワ* 打造,祝您有个愉快网上冲浪体验。
|
||||||
|
|
||||||
|
RAT Ain't Tieba 是以 AGPLv3 分发的自由软件,它尊重您的自由。更多关于
|
||||||
|
自由软件运动的内容,请见 <a href="https://writefreesoftware.org">writefreesoftware</a> 与 <a href="https://gnu.org">GNU</a>。
|
||||||
|
|
||||||
|
源代码 :<a href="https://0xacab.org/johnxina/rat">0xacab(对文本浏览器不友好)</a>
|
||||||
|
源代码仓库:https://0xacab.org/johnxina/rat.git
|
||||||
|
缺陷追踪器:<a href="https://0xacab.org/johnxina/rat/-/issues">0xacab(对文本浏览器不友好)</a>
|
||||||
|
|
||||||
|
当然,您也可以使用电子邮件报告缺陷:<a href="mailto:bingchilling@riseup.net">bingchilling@riseup.net</a>。
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<form action="/f" method="get">
|
||||||
|
<label for="kw">进入贴吧:</label>
|
||||||
|
<input type="text" name="kw" required />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
<a href="/">RAT Ain't Tieba</a> <a href="https://0xacab.org/johnxina/rat/-/raw/no-masters/LICENSE">自豪地以 AGPLv3 释出</a> <a href="https://0xacab.org/johnxina/rat">源代码</a>
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
49
templates/thread.html.text
Normal file
49
templates/thread.html.text
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>{{ info.thread.title }} - {{ info.thread.fname }}吧 - RAT</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre>
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
<a href="/f?kw={{ info.forum.fname }}">[{{ info.forum.fname }}吧]</a> {{ info.thread.title|twrap(width=60) }}
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
<a {% if not ao %} class="current-sel" {% endif %} href="/p/{{ info.thread.tid }}?ao=0">[全部回复]</a> <a {% if ao %} class="current-sel" {% endif %} href="/p/{{ info.thread.tid }}?ao=1">[仅看楼主]</a></pre>
|
||||||
|
{% for p in info %}
|
||||||
|
<pre>
|
||||||
|
<a href="#{{ p.floor }}">#{{ p.floor }}</a> | <a href="/home/main?id={{ p.user.user_id }}">{{ p.user.show_name }}</a>:
|
||||||
|
|
||||||
|
{{ p.text|twrap(rws=True) }}
|
||||||
|
{% if p.comments %}{{ '\n' + p.comments|tcomments }}{% endif %}</pre>
|
||||||
|
{% endfor %}
|
||||||
|
<pre>------------------------------------------------------------------------</pre>
|
||||||
|
<a href="/p/{{ info.thread.tid }}&ao={{ao}}">首页</a> |
|
||||||
|
|
||||||
|
{% for i in range(5) %}
|
||||||
|
{% set np = info.page.current_page - 5 + i %}
|
||||||
|
{% if np > 0 %}
|
||||||
|
<a href="/p/{{ info.thread.tid }}?pn={{ np }}&ao={{ao}}">{{ np }}</a> |
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<strong>{{ info.page.current_page }}</strong> |
|
||||||
|
|
||||||
|
{% for i in range(5) %}
|
||||||
|
{% set np = info.page.current_page + 1 + i %}
|
||||||
|
{% if np <= info.page.total_page %}
|
||||||
|
<a href="/p/{{ info.thread.tid }}?pn={{ np }}&ao={{ao}}">{{ np }}</a> |
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<a href="/p/{{ info.thread.tid }}?pn={{ info.page.total_page }}&ao={{ao}}">尾页</a>
|
||||||
|
<pre>
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
<a href="/">RAT Ain't Tieba</a> <a href="https://0xacab.org/johnxina/rat/-/raw/no-masters/LICENSE">自豪地以 AGPLv3 释出</a> <a href="https://0xacab.org/johnxina/rat">源代码</a>
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
37
templates/user.html.text
Normal file
37
templates/user.html.text
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>{{ hp[0].show_name }}的个人资料 - RAT</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre>
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
{{ hp[0].show_name }} ({{ hp[0].user_name }})
|
||||||
|
关注 {{ hp[0].follow_num|intsep}} 粉丝 {{ hp[0].fan_num|intsep }} 发帖 {{ hp[0].post_num|intsep }} 关注贴吧 {{ hp[0].forum_num|intsep }}
|
||||||
|
------------------------------------------------------------------------</pre>
|
||||||
|
{% for t in hp[1] %}
|
||||||
|
<pre><a href="/p/{{ t.tid }}">{{ t.text|twrap(rws=True) }}</a></pre>
|
||||||
|
{% endfor %}
|
||||||
|
<pre>------------------------------------------------------------------------</pre>
|
||||||
|
<a href="/home/main?id={{ hp[0].user_id }}&pn={{ 1 }}">首页</a> |
|
||||||
|
|
||||||
|
{% for i in range(5) %}
|
||||||
|
{% set np = pn - 5 + i %}
|
||||||
|
{% if np > 0 %}
|
||||||
|
<a href="/home/main?id={{ hp[0].user_id }}&pn={{ np }}">{{ np }}</a> |
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<strong>{{ pn }}</strong> |
|
||||||
|
|
||||||
|
<a href="/home/main?id={{ hp[0].user_id }}&pn={{ pn+1 }}">下一页</a>
|
||||||
|
<pre>
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
<a href="/">RAT Ain't Tieba</a> <a href="https://0xacab.org/johnxina/rat/-/raw/no-masters/LICENSE">自豪地以 AGPLv3 释出</a> <a href="https://0xacab.org/johnxina/rat">源代码</a>
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user