diff --git a/app.py b/app.py index 840cfc426607611998508a1a08b5e28e9ce20023..7e8f32b7dabc0226dcc5eac6620f26b8746d9e7c 100644 --- a/app.py +++ b/app.py @@ -2,6 +2,9 @@ import asyncio import aiotieba import uvicorn +import re +import textwrap + from aioflask import render_template, request, escape from urllib.parse import quote_plus from datetime import datetime @@ -33,6 +36,25 @@ async def cache_name_from_id(c, i): def normalize_utf8(s): 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. @@ -63,6 +85,15 @@ def _jinja2_filter_intsep(i): def _jinja2_filter_trim(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. @app.template_filter('translate') async def _jinja2_filter_translate(frags, reply_id=0): @@ -105,6 +136,10 @@ async def _jinja2_filter_translate(frags, reply_id=0): 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>') @@ -134,7 +169,7 @@ async def thread_view(tid): 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') async def forum_view(): @@ -156,10 +191,10 @@ async def forum_view(): 'member': forum_info.member_num, 'desc': '贴吧描述暂不可用', 'name': forum_info.fname } 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.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)) @app.route('/home/main') @@ -175,28 +210,28 @@ async def user_view(): try: hp = await tieba.get_homepage(i, pn) 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: - 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('/') async def main_view(): - return await render_template('index.html') + return await render_template_c('index.html') ###################################################################### @app.errorhandler(RuntimeError) async def runtime_error_view(e): if hasattr(e, 'msg'): - return await render_template('error.html', msg=e.msg) - return await render_template('error.html', msg='错误信息不可用') + return await render_template_c('error.html', msg=e.msg) + return await render_template_c('error.html', msg='错误信息不可用') @app.errorhandler(Exception) async def general_error_view(e): - return await render_template('error.html', msg=e) + return await render_template_c('error.html', msg=e) ###################################################################### diff --git a/templates/bar.html.text b/templates/bar.html.text new file mode 100644 index 0000000000000000000000000000000000000000..960b1ee8b5532b7a78e0165d68fbf4458e3a6e20 --- /dev/null +++ b/templates/bar.html.text @@ -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> diff --git a/templates/error.html.text b/templates/error.html.text new file mode 100644 index 0000000000000000000000000000000000000000..db04d3cdfc512503ecb5b00385f3c1ac64c7974c --- /dev/null +++ b/templates/error.html.text @@ -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> diff --git a/templates/index.html.text b/templates/index.html.text new file mode 100644 index 0000000000000000000000000000000000000000..6f072340460d82fb48e4375cd90ffa695f7fa661 --- /dev/null +++ b/templates/index.html.text @@ -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> diff --git a/templates/thread.html.text b/templates/thread.html.text new file mode 100644 index 0000000000000000000000000000000000000000..5a78c4c962337b86ccad6f80795469541c9dc0af --- /dev/null +++ b/templates/thread.html.text @@ -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> diff --git a/templates/user.html.text b/templates/user.html.text new file mode 100644 index 0000000000000000000000000000000000000000..8548a01dd5ccac202e68060b7a432d4895d844f2 --- /dev/null +++ b/templates/user.html.text @@ -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>