Last active
October 19, 2018 16:54
-
-
Save called-d/8eaf4ed6088beae01a91c506ecc83ff0 to your computer and use it in GitHub Desktop.
ネタで作った安価bot (https://mstdn-workers.com/@anchor_bot で稼働中)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from mastodon import Mastodon | |
import os | |
import re | |
import html | |
from textwrap import dedent | |
from mastodon import StreamListener | |
param_envs = "CLIENT_ID", "CLIENT_SECRET", "ACCESS_TOKEN", "API_BASE_URL" | |
mstdn = Mastodon(**{name.lower(): os.getenv(name) for name in param_envs}) | |
DEBUG=False | |
class Target: | |
def __init__(self, n): | |
self.n = n | |
self.n_original = n | |
self.status = None | |
def hit_status(self, status): | |
self.status = status | |
class AnchorContainer: | |
def __init__(self, status, num_list): | |
self.status = status | |
self.targets = [Target(n) for n in num_list] | |
class WaitingRemove: | |
def __init__(self, status, n=100): | |
self.status = status | |
self.n = n | |
if 'anchors' not in globals(): | |
anchors = [] | |
if 'remove_list' not in globals(): | |
remove_list = [] | |
def remove_tag(html): | |
return re.sub(r"<[^>]+?>", '', html) | |
def to_oneline(html): | |
return html.replace("<br />", ' ').replace("</p><p>", ' ').replace('\n', '\\n') | |
def remove_mention(content): | |
return content.replace("@", "@") | |
def remove_hashtag(content): | |
return content.replace("#", "#") | |
def remove_image(content, status): | |
for media in status.media_attachments: | |
content = content.replace(media.text_url, "[画像]") | |
return content | |
def get_anchor(content): | |
return [int(n_str) for n_str in re.findall(r">>(-?\d+)", content)] | |
def get_anchor_from_status(status): | |
content = html.unescape(remove_tag(status['content'])) | |
return get_anchor(content) | |
def append_anchor_from_status(status): | |
anchor_nums = sorted(set(get_anchor_from_status(status))) | |
if anchor_nums: | |
if all(0 < n <= 1000 for n in anchor_nums): | |
anchors.append(AnchorContainer(status, anchor_nums)) | |
post_you_got_it(status, anchor_nums) | |
else: | |
post_anchor_out_of_range(status) | |
def decrement_anchors(): | |
for a in anchors: | |
for t in a.targets: | |
t.n -= 1 | |
def decrement_remove(): | |
for r in remove_list: | |
r.n -= 1 | |
def get_status_str(status): | |
content = html.unescape(remove_tag(to_oneline(status.content))) | |
content = remove_mention(content) | |
content = remove_hashtag(content) | |
content = remove_image(content, status) | |
return f'> "{content}"' if len(content) < 20 else status.url | |
def post_you_re_anchored_and_anchor_hit(anchor, display_all=False): | |
lines = [get_status_str(anchor.status) + " の"] | |
for t in anchor.targets: | |
if t.status: | |
target_is_near = t.n > -15 | |
if target_is_near or display_all: | |
lines.extend([ | |
f">>{t.n_original}は次の投稿となりました", | |
get_status_str(t.status) | |
]) | |
if len(lines) < 4: | |
status = mstdn.status_post("\n".join(lines), visibility="private" if DEBUG else "public") | |
else: | |
status = mstdn.status_post( | |
spoiler_text="\n".join(lines[:3]) + "...", | |
status="\n".join(lines[3:]), | |
visibility="private" if DEBUG else "public" | |
) | |
remove_list.append(WaitingRemove( | |
status, | |
100)) | |
def post_anchor_out_of_range(status): | |
remove_list.append(WaitingRemove( | |
mstdn.status_post(dedent(f"""\ | |
@{status['account']['username']} {status.url} 安価は 0 < n <= 1000 で指定してください"""), | |
visibility="private"), | |
100)) | |
def post_you_got_it(status, anchor_nums): | |
remove_list.append(WaitingRemove( | |
mstdn.status_post(dedent(f""" | |
{status.url} の{', '.join(map(str, anchor_nums))}個後の投稿をピックアップします | |
(幾つかのbotの投稿はスキップされます)"""), | |
visibility="private" if DEBUG else "public"), | |
10)) | |
def post_for_zero(status): | |
for a in anchors: | |
for t in a.targets: | |
if t.n == 0: | |
t.hit_status(status) | |
if all(t.n <= 0 for t in a.targets): | |
anchors.remove(a) | |
post_you_re_anchored_and_anchor_hit(a, display_all=True) | |
elif any(t.n == 0 for t in a.targets): | |
done_targets = [t for t in a.targets if t.n < 0] | |
undone_targets = [t for t in a.targets if t.n > 0] | |
prev_target_n = max(t.n for t in done_targets) if done_targets else -20 | |
next_target_n = min(t.n for t in undone_targets) if undone_targets else 20 | |
target_is_near = prev_target_n > -15 or next_target_n < 15 | |
if target_is_near: continue | |
post_you_re_anchored_and_anchor_hit(a) | |
def remove_bot_statuses(): | |
for r in [r for r in remove_list if r.n == 0]: | |
remove_list.remove(r) | |
status_current = mstdn.status(r.status.id) | |
if status_current.favourites_count or status_current.reblogs_count: | |
pass | |
else: | |
mstdn.status_delete(r.status.id) | |
def remove_anchor_by_status_id(status_id): | |
for a in [a for a in anchors if a.status.id == status_id]: | |
anchors.remove(a) | |
def is_some_bots(status): | |
app = status['application'] | |
if not app: return False | |
bot_names = [ | |
'オフ会カレンダー', | |
'off_bot', | |
'安価bot', | |
'不適切bot', | |
"トレンドbot", | |
'nekonyanApp', | |
'色bot', | |
'ダイスbot', | |
] | |
return app['name'] in bot_names | |
class AnchorListener(StreamListener): | |
def on_update(self, status): | |
if is_some_bots(status): return | |
decrement_anchors() | |
decrement_remove() | |
post_for_zero(status) | |
append_anchor_from_status(status) | |
remove_bot_statuses() | |
def on_delete(self, status_id): | |
remove_anchor_by_status_id(status_id) | |
listener = AnchorListener() | |
mstdn.stream_local(listener) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
convert -size 300x300 canvas:"#d9e1e8" -fill "#9baec8" -gravity Center -pointsize 50 -annotate 0 ">>42" icon.png |
廃止要求意図の聞き取りと改善要望を募り
2018/02/06 15:33 誰かを呼び出さないよう改修し再開
廃止要求あるいはLTLが再び地雷原になるようであれば今度こそ完全に廃止する
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
安価bot
当botの運用とコードは現状有姿にて提供される
2018/02/05 16:46 廃止要求により停止。再開は様子を見る