mirror of
https://0xacab.org/johnxina/rat.git
synced 2024-12-23 04:59:09 +00:00
add several features
This commit is contained in:
parent
f17e332ee4
commit
9df44d5a72
82
app.py
82
app.py
@ -2,7 +2,6 @@ import asyncio
|
||||
import aiotieba
|
||||
|
||||
from aioflask import render_template, request, escape
|
||||
from flask_caching import Cache
|
||||
from urllib.parse import quote_plus
|
||||
from datetime import datetime
|
||||
|
||||
@ -14,6 +13,21 @@ from extra import *
|
||||
|
||||
######################################################################
|
||||
|
||||
# Clean a leading part and append the text.
|
||||
def append_with_leading_clean(orig, content):
|
||||
if orig.endswith('<br>'):
|
||||
return orig[:-4] + content
|
||||
else:
|
||||
return orig + content
|
||||
|
||||
# Return the corresponding user name for an id.
|
||||
async def cache_name_from_id(c, i):
|
||||
if not cache.get(i):
|
||||
r = await c.get_user_info(i, require=aiotieba.enums.ReqUInfo.USER_NAME)
|
||||
cache.set(i, r)
|
||||
|
||||
######################################################################
|
||||
|
||||
# Convert a timestamp to its simpliest readable date format.
|
||||
@app.template_filter('simpledate')
|
||||
def _jinja2_filter_simpledate(ts):
|
||||
@ -44,28 +58,44 @@ def _jinja2_filter_trim(text):
|
||||
|
||||
# Format fragments to its equiviant HTML.
|
||||
@app.template_filter('translate')
|
||||
def _jinja2_filter_translate(frags):
|
||||
async def _jinja2_filter_translate(frags, reply_id=0):
|
||||
htmlfmt = ''
|
||||
|
||||
if reply_id:
|
||||
htmlfmt += f'<a href="/home/main?id={reply_id}">@{ cache.get(reply_id) }</a> '
|
||||
|
||||
for frag in frags:
|
||||
for i in range(len(frags)):
|
||||
frag = frags[i]
|
||||
if isinstance(frag, FragText):
|
||||
subfrags = frag.text.split('\n')
|
||||
for subfrag in subfrags:
|
||||
htmlfmt += '<p>' + str(escape(subfrag)) + '</p>'
|
||||
htmlfmt += str(escape(subfrag)) + '<br>'
|
||||
elif isinstance(frag, FragImage_p):
|
||||
htmlfmt += \
|
||||
f'<a target="_blank" href="/proxy/pic/{ extract_image_name(frag.origin_src) }">' \
|
||||
f'<img width="{ frag.show_width}" height="{ frag.show_height }" '\
|
||||
f'src="/proxy/pic/{ extract_image_name(frag.src) }"></a>'
|
||||
elif isinstance(frag, FragEmoji_p):
|
||||
clear_leading = False
|
||||
if htmlfmt.endswith('</p>'):
|
||||
clear_leading = True
|
||||
htmlfmt = htmlfmt.rstrip('</p>')
|
||||
htmlfmt += f'<img class="emoticons" alt="[{ frag.desc }]" src="/static/emoticons/{ quote_plus(frag.desc) }.png">'
|
||||
if clear_leading:
|
||||
htmlfmt += '</p>'
|
||||
|
||||
htmlfmt = append_with_leading_clean(htmlfmt,
|
||||
f'<img class="emoticons" alt="[{ frag.desc }]"'
|
||||
f'src="/static/emoticons/{ quote_plus(frag.desc) }.png">')
|
||||
if i+1 < len(frags) and isinstance(frags[i+1], FragImage_p):
|
||||
htmlfmt += '<br>'
|
||||
elif isinstance(frag, FragLink):
|
||||
markup = '<a '; url = frag.raw_url
|
||||
if frag.is_external:
|
||||
markup += 'style="text-color: #ff0000;" '
|
||||
else:
|
||||
url = frag.raw_url.lstrip('https://tieba.baidu.com')
|
||||
markup += f'href="{ url }">{ frag.title }</a>'
|
||||
htmlfmt = append_with_leading_clean(htmlfmt, markup)
|
||||
elif isinstance(frag, FragAt):
|
||||
htmlfmt = append_with_leading_clean(htmlfmt,
|
||||
f'<a href="/home/main?id={ frag.user_id }">{ frag.text }</a>')
|
||||
else:
|
||||
print('Unhandled: ', type(frag))
|
||||
print(frag)
|
||||
|
||||
return htmlfmt
|
||||
|
||||
######################################################################
|
||||
@ -77,23 +107,39 @@ async def thread_view(tid):
|
||||
|
||||
async with aiotieba.Client() as tieba:
|
||||
# Default to 15 posts per page, confirm to tieba.baidu.com
|
||||
thread_info = await tieba.get_posts(tid, rn=15, pn=pn)
|
||||
thread_info = await tieba.get_posts(tid, rn=15, pn=pn,
|
||||
with_comments=should_fetch_comments)
|
||||
|
||||
for post in thread_info:
|
||||
print(post.comments)
|
||||
|
||||
available_users = []
|
||||
for floor in thread_info:
|
||||
for comment in floor.comments:
|
||||
available_users.append(comment.author_id)
|
||||
cache.set(comment.author_id, comment.user.user_name)
|
||||
|
||||
all_users = {}
|
||||
for floor in thread_info:
|
||||
for comment in floor.comments:
|
||||
if not comment.reply_to_id in available_users:
|
||||
all_users[comment.reply_to_id] = ''
|
||||
all_users.pop(0, None)
|
||||
all_users = list(all_users.keys())
|
||||
|
||||
await asyncio.gather(*(cache_name_from_id(tieba, i) for i in all_users))
|
||||
|
||||
|
||||
return await render_template('thread.html', info=thread_info)
|
||||
|
||||
@app.route('/f')
|
||||
async def forum_view():
|
||||
fname = request.args['kw']
|
||||
pn = int(request.args.get('pn') or 1)
|
||||
sort = int(request.args.get('sort') or 0)
|
||||
|
||||
async with aiotieba.Client() as tieba:
|
||||
forum_info, threads = await asyncio.gather(awaitify(find_tieba_info)(fname),
|
||||
tieba.get_threads(fname, rn=50, pn=pn))
|
||||
tieba.get_threads(fname, rn=50, pn=pn, sort=sort))
|
||||
|
||||
return await render_template('bar.html', info=forum_info, threads=threads)
|
||||
return await render_template('bar.html', info=forum_info, threads=threads, sort=sort)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
||||
|
6
extra.py
6
extra.py
@ -6,9 +6,13 @@ import re
|
||||
|
||||
from shared import *
|
||||
|
||||
# TODO: known bug, can't extract from super old editor images.
|
||||
def extract_image_name(url):
|
||||
match = re.search(r'/(\w+)\.jpg', url)
|
||||
return match.group(1) + '.jpg'
|
||||
try:
|
||||
return match.group(1) + '.jpg'
|
||||
except:
|
||||
return '404.jpg'
|
||||
|
||||
@cache.cached(timeout=60, key_prefix='tieba_info')
|
||||
def find_tieba_info(tname):
|
||||
|
11
main.py
11
main.py
@ -138,11 +138,12 @@ class ReverseProxyResource(Resource):
|
||||
|
||||
# To start this function for testing: python -c 'import main; main.twisted_start()'
|
||||
def twisted_start():
|
||||
flask_res = proxy.ReverseProxyResource('127.0.0.1', 5000, b'')
|
||||
flask_port = int(app.config['SERVER_NAME'].split(':')[1])
|
||||
flask_res = proxy.ReverseProxyResource('127.0.0.1', flask_port, b'')
|
||||
flask_res.putChild(b'proxy', ReverseProxyResource(b'/proxy'))
|
||||
flask_res.putChild(b'static', File('static'))
|
||||
|
||||
flask_port = int(app.config['SERVER_NAME'].split(':')[1])
|
||||
print(f' *** SERVER IS RUNNING ON PORT {flask_port-1} ***')
|
||||
|
||||
site = server.Site(flask_res)
|
||||
reactor.listenTCP(flask_port-1, site)
|
||||
@ -154,11 +155,7 @@ def flask_start():
|
||||
|
||||
# If we're executed directly, also start the flask daemon.
|
||||
if __name__ == '__main__':
|
||||
flask_port = int(app.config['SERVER_NAME'].split(':')[1])
|
||||
print(f' *** SERVER IS RUNNING ON PORT {flask_port-1} ***')
|
||||
|
||||
twisted_start()
|
||||
|
||||
flask_task = multiprocessing.Process(target=flask_start)
|
||||
flask_task.daemon = True # Exit the child if the parent was killed :-(
|
||||
flask_task.start()
|
||||
twisted_start()
|
||||
|
@ -12,7 +12,13 @@ def awaitify(sync_func):
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
app.config['SERVER_NAME'] = ':6666'
|
||||
######################################################################
|
||||
|
||||
app.config['SERVER_NAME'] = '127.0.0.1:8886'
|
||||
|
||||
should_fetch_comments = True
|
||||
|
||||
######################################################################
|
||||
|
||||
app.config['CACHE_TYPE'] = 'SimpleCache'
|
||||
cache = Cache(app)
|
||||
|
@ -2,6 +2,21 @@
|
||||
max-width: 5% !important;
|
||||
}
|
||||
|
||||
.vlist {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.vlist > div {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
.current-sel {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/* global styling */
|
||||
:root {
|
||||
--bg-color: #eeeecc;
|
||||
|
@ -21,6 +21,16 @@
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="list">
|
||||
<div class="vlist">
|
||||
<div><a {% if sort == 0 %} class="current-sel" {% endif %}
|
||||
href="/f?kw={{ info['name'] }}&pn={{ threads.page.current_page }}&sort=0">时下热门</a></div>
|
||||
<div><a {% if sort == 1 %} class="current-sel" {% endif %}
|
||||
href="/f?kw={{ info['name'] }}&pn={{ threads.page.current_page }}&sort=1">最新发布</a></div>
|
||||
<div><a {% if sort == 5 %} class="current-sel" {% endif %}
|
||||
href="/f?kw={{ info['name'] }}&pn={{ threads.page.current_page }}&sort=5">最新回复</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list">
|
||||
{% for t in threads %}
|
||||
<div class="thread">
|
||||
@ -34,9 +44,12 @@
|
||||
%}<span class="tag tag-blue">置顶</span>{%
|
||||
endif %}{% if t.is_good
|
||||
%}<span class="tag tag-red">精</span>{% endif
|
||||
%}<a href="/p/{{ t.tid }}">{{ t.title }} </a>
|
||||
%}<a href="/p/{{ t.tid }}">{{ t.title if t.title else t.text|trim }} </a>
|
||||
</div>
|
||||
|
||||
{% if t.title %}
|
||||
<div>{{ t.text[(t.title|length):]|trim }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="participants">
|
||||
<div>🧑<a href="">{{ t.user.user_name }}</a></div>
|
||||
@ -47,13 +60,13 @@
|
||||
|
||||
<div class="paginator">
|
||||
{% if threads.page.current_page > 1 %}
|
||||
<a href="/f?kw={{ info['name'] }}">首页</a>
|
||||
<a href="/f?kw={{ info['name'] }}&sort={{ sort }}">首页</a>
|
||||
{% endif %}
|
||||
|
||||
{% for i in range(5) %}
|
||||
{% set np = threads.page.current_page - 5 + i %}
|
||||
{% if np > 0 %}
|
||||
<a href="/f?kw={{ info['name'] }}&pn={{ np }}">{{ np }}</a>
|
||||
<a href="/f?kw={{ info['name'] }}&pn={{ np }}&sort={{ sort }}">{{ np }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
@ -62,12 +75,12 @@
|
||||
{% for i in range(5) %}
|
||||
{% set np = threads.page.current_page + 1 + i %}
|
||||
{% if np <= threads.page.total_page %}
|
||||
<a href="/f?kw={{ info['name'] }}&pn={{ np }}">{{ np }}</a>
|
||||
<a href="/f?kw={{ info['name'] }}&pn={{ np }}&sort={{ sort }}">{{ np }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if threads.page.current_page < threads.page.total_page %}
|
||||
<a href="/f?kw={{ info['name'] }}&pn={{ threads.page.total_page }}">尾页</a>
|
||||
<a href="/f?kw={{ info['name'] }}&pn={{ threads.page.total_page }}&sort={{ sort }}">尾页</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -30,6 +30,24 @@
|
||||
</div>
|
||||
<small class="date">{{ p.create_time|date }}</small>
|
||||
<small class="permalink"><a href="#{{ p.floor }}">{{ p.floor }}</a></small>
|
||||
{% if p.comments %}
|
||||
<div class="replies">
|
||||
{% for comment in p.comments %}
|
||||
<div class="post">
|
||||
<img class="avatar" src="/proxy/avatar/{{ comment.user.portrait }}">
|
||||
<div>
|
||||
<div class="userinfo">
|
||||
<a href="/home/main?id={{ comment.user.user_id }}">{{ comment.user.user_name }}</a>
|
||||
</div>
|
||||
<div class="content">
|
||||
{{ comment.contents|translate(comment.reply_to_id)|safe }}
|
||||
</div>
|
||||
<small class="date">{{ comment.create_time|date }}</small>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
Loading…
Reference in New Issue
Block a user