mirror of
https://0xacab.org/johnxina/rat.git
synced 2024-12-23 13:09:08 +00:00
add several features
This commit is contained in:
parent
f17e332ee4
commit
9df44d5a72
78
app.py
78
app.py
@ -2,7 +2,6 @@ import asyncio
|
|||||||
import aiotieba
|
import aiotieba
|
||||||
|
|
||||||
from aioflask import render_template, request, escape
|
from aioflask import render_template, request, escape
|
||||||
from flask_caching import Cache
|
|
||||||
from urllib.parse import quote_plus
|
from urllib.parse import quote_plus
|
||||||
from datetime import datetime
|
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.
|
# Convert a timestamp to its simpliest readable date format.
|
||||||
@app.template_filter('simpledate')
|
@app.template_filter('simpledate')
|
||||||
def _jinja2_filter_simpledate(ts):
|
def _jinja2_filter_simpledate(ts):
|
||||||
@ -44,27 +58,43 @@ def _jinja2_filter_trim(text):
|
|||||||
|
|
||||||
# Format fragments to its equiviant HTML.
|
# Format fragments to its equiviant HTML.
|
||||||
@app.template_filter('translate')
|
@app.template_filter('translate')
|
||||||
def _jinja2_filter_translate(frags):
|
async def _jinja2_filter_translate(frags, reply_id=0):
|
||||||
htmlfmt = ''
|
htmlfmt = ''
|
||||||
|
|
||||||
for frag in frags:
|
if reply_id:
|
||||||
|
htmlfmt += f'<a href="/home/main?id={reply_id}">@{ cache.get(reply_id) }</a> '
|
||||||
|
|
||||||
|
for i in range(len(frags)):
|
||||||
|
frag = frags[i]
|
||||||
if isinstance(frag, FragText):
|
if isinstance(frag, FragText):
|
||||||
subfrags = frag.text.split('\n')
|
subfrags = frag.text.split('\n')
|
||||||
for subfrag in subfrags:
|
for subfrag in subfrags:
|
||||||
htmlfmt += '<p>' + str(escape(subfrag)) + '</p>'
|
htmlfmt += str(escape(subfrag)) + '<br>'
|
||||||
elif isinstance(frag, FragImage_p):
|
elif isinstance(frag, FragImage_p):
|
||||||
htmlfmt += \
|
htmlfmt += \
|
||||||
f'<a target="_blank" href="/proxy/pic/{ extract_image_name(frag.origin_src) }">' \
|
f'<a target="_blank" href="/proxy/pic/{ extract_image_name(frag.origin_src) }">' \
|
||||||
f'<img width="{ frag.show_width}" height="{ frag.show_height }" '\
|
f'<img width="{ frag.show_width}" height="{ frag.show_height }" '\
|
||||||
f'src="/proxy/pic/{ extract_image_name(frag.src) }"></a>'
|
f'src="/proxy/pic/{ extract_image_name(frag.src) }"></a>'
|
||||||
elif isinstance(frag, FragEmoji_p):
|
elif isinstance(frag, FragEmoji_p):
|
||||||
clear_leading = False
|
htmlfmt = append_with_leading_clean(htmlfmt,
|
||||||
if htmlfmt.endswith('</p>'):
|
f'<img class="emoticons" alt="[{ frag.desc }]"'
|
||||||
clear_leading = True
|
f'src="/static/emoticons/{ quote_plus(frag.desc) }.png">')
|
||||||
htmlfmt = htmlfmt.rstrip('</p>')
|
if i+1 < len(frags) and isinstance(frags[i+1], FragImage_p):
|
||||||
htmlfmt += f'<img class="emoticons" alt="[{ frag.desc }]" src="/static/emoticons/{ quote_plus(frag.desc) }.png">'
|
htmlfmt += '<br>'
|
||||||
if clear_leading:
|
elif isinstance(frag, FragLink):
|
||||||
htmlfmt += '</p>'
|
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
|
return htmlfmt
|
||||||
|
|
||||||
@ -77,10 +107,25 @@ async def thread_view(tid):
|
|||||||
|
|
||||||
async with aiotieba.Client() as tieba:
|
async with aiotieba.Client() as tieba:
|
||||||
# Default to 15 posts per page, confirm to tieba.baidu.com
|
# 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)
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
for post in thread_info:
|
|
||||||
print(post.comments)
|
|
||||||
|
|
||||||
return await render_template('thread.html', info=thread_info)
|
return await render_template('thread.html', info=thread_info)
|
||||||
|
|
||||||
@ -88,12 +133,13 @@ async def thread_view(tid):
|
|||||||
async def forum_view():
|
async def forum_view():
|
||||||
fname = request.args['kw']
|
fname = request.args['kw']
|
||||||
pn = int(request.args.get('pn') or 1)
|
pn = int(request.args.get('pn') or 1)
|
||||||
|
sort = int(request.args.get('sort') or 0)
|
||||||
|
|
||||||
async with aiotieba.Client() as tieba:
|
async with aiotieba.Client() as tieba:
|
||||||
forum_info, threads = await asyncio.gather(awaitify(find_tieba_info)(fname),
|
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__':
|
if __name__ == '__main__':
|
||||||
app.run(debug=True)
|
app.run(debug=True)
|
||||||
|
6
extra.py
6
extra.py
@ -6,9 +6,13 @@ import re
|
|||||||
|
|
||||||
from shared import *
|
from shared import *
|
||||||
|
|
||||||
|
# TODO: known bug, can't extract from super old editor images.
|
||||||
def extract_image_name(url):
|
def extract_image_name(url):
|
||||||
match = re.search(r'/(\w+)\.jpg', 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')
|
@cache.cached(timeout=60, key_prefix='tieba_info')
|
||||||
def find_tieba_info(tname):
|
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()'
|
# To start this function for testing: python -c 'import main; main.twisted_start()'
|
||||||
def 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'proxy', ReverseProxyResource(b'/proxy'))
|
||||||
flask_res.putChild(b'static', File('static'))
|
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)
|
site = server.Site(flask_res)
|
||||||
reactor.listenTCP(flask_port-1, site)
|
reactor.listenTCP(flask_port-1, site)
|
||||||
@ -154,11 +155,7 @@ def flask_start():
|
|||||||
|
|
||||||
# If we're executed directly, also start the flask daemon.
|
# If we're executed directly, also start the flask daemon.
|
||||||
if __name__ == '__main__':
|
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 = multiprocessing.Process(target=flask_start)
|
||||||
flask_task.daemon = True # Exit the child if the parent was killed :-(
|
flask_task.daemon = True # Exit the child if the parent was killed :-(
|
||||||
flask_task.start()
|
flask_task.start()
|
||||||
|
twisted_start()
|
||||||
|
@ -12,7 +12,13 @@ def awaitify(sync_func):
|
|||||||
|
|
||||||
app = Flask(__name__)
|
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'
|
app.config['CACHE_TYPE'] = 'SimpleCache'
|
||||||
cache = Cache(app)
|
cache = Cache(app)
|
||||||
|
@ -2,6 +2,21 @@
|
|||||||
max-width: 5% !important;
|
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 */
|
/* global styling */
|
||||||
:root {
|
:root {
|
||||||
--bg-color: #eeeecc;
|
--bg-color: #eeeecc;
|
||||||
|
@ -21,6 +21,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</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">
|
<div class="list">
|
||||||
{% for t in threads %}
|
{% for t in threads %}
|
||||||
<div class="thread">
|
<div class="thread">
|
||||||
@ -34,9 +44,12 @@
|
|||||||
%}<span class="tag tag-blue">置顶</span>{%
|
%}<span class="tag tag-blue">置顶</span>{%
|
||||||
endif %}{% if t.is_good
|
endif %}{% if t.is_good
|
||||||
%}<span class="tag tag-red">精</span>{% endif
|
%}<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>
|
</div>
|
||||||
|
|
||||||
|
{% if t.title %}
|
||||||
<div>{{ t.text[(t.title|length):]|trim }}</div>
|
<div>{{ t.text[(t.title|length):]|trim }}</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="participants">
|
<div class="participants">
|
||||||
<div>🧑<a href="">{{ t.user.user_name }}</a></div>
|
<div>🧑<a href="">{{ t.user.user_name }}</a></div>
|
||||||
@ -47,13 +60,13 @@
|
|||||||
|
|
||||||
<div class="paginator">
|
<div class="paginator">
|
||||||
{% if threads.page.current_page > 1 %}
|
{% if threads.page.current_page > 1 %}
|
||||||
<a href="/f?kw={{ info['name'] }}">首页</a>
|
<a href="/f?kw={{ info['name'] }}&sort={{ sort }}">首页</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% for i in range(5) %}
|
{% for i in range(5) %}
|
||||||
{% set np = threads.page.current_page - 5 + i %}
|
{% set np = threads.page.current_page - 5 + i %}
|
||||||
{% if np > 0 %}
|
{% 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 %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
@ -62,12 +75,12 @@
|
|||||||
{% for i in range(5) %}
|
{% for i in range(5) %}
|
||||||
{% set np = threads.page.current_page + 1 + i %}
|
{% set np = threads.page.current_page + 1 + i %}
|
||||||
{% if np <= threads.page.total_page %}
|
{% 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 %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% if threads.page.current_page < threads.page.total_page %}
|
{% 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 %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -30,6 +30,24 @@
|
|||||||
</div>
|
</div>
|
||||||
<small class="date">{{ p.create_time|date }}</small>
|
<small class="date">{{ p.create_time|date }}</small>
|
||||||
<small class="permalink"><a href="#{{ p.floor }}">{{ p.floor }}</a></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>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
Loading…
Reference in New Issue
Block a user