Add terminal web browser support from Hatsuharu

This commit is contained in:
John Xina 2023-07-24 14:45:05 +08:00
parent 33baaeb89b
commit 6821384f4b
6 changed files with 275 additions and 10 deletions

55
app.py
View File

@ -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
View 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
View 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
View 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>

View 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
View 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>