diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..060b59b --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# config files +config.json + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class diff --git a/Timer/__init__.py b/Timer/__init__.py new file mode 100644 index 0000000..5da5b41 --- /dev/null +++ b/Timer/__init__.py @@ -0,0 +1,21 @@ +import asyncio +import logging + +class Timer: + def __init__(self, callback, timeout): + logging.info("Created a schedule interval as " + str(timeout) + " seconds.") + loop = asyncio.get_event_loop() + self.callback = callback + self.timeout = timeout + self.task = loop.create_task(self.wait()) + + async def wait(self): + await asyncio.sleep(self.timeout) + logging.info("Successfully executed a timer schedule.") + await self.callback + + def stop(self): + try: + self.task.cancel() + except asyncio.CancelledError: + pass \ No newline at end of file diff --git a/bot.session b/bot.session new file mode 100644 index 0000000..89e55e9 Binary files /dev/null and b/bot.session differ diff --git a/bot.session-journal b/bot.session-journal new file mode 100644 index 0000000..4215a0f Binary files /dev/null and b/bot.session-journal differ diff --git a/car.service b/car.service new file mode 100644 index 0000000..34bcbbc --- /dev/null +++ b/car.service @@ -0,0 +1,17 @@ +[Unit] +Description=captcha +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +WorkingDirectory=/root/CAR +ExecStart=/usr/bin/python3 /root/CAR/main.py +Restart=always +PrivateTmp=True +KillSignal=SIGINT +TimeoutStopSec=10s +StartLimitInterval=400 + +[Install] +WantedBy=multi-user.target diff --git a/challenge.py b/challenge.py new file mode 100644 index 0000000..a3304d3 --- /dev/null +++ b/challenge.py @@ -0,0 +1,80 @@ +import random +import configparser + +class Challenge: + """ + 如果你是 Python 高手,请继续;如果你根本不会Python或者语法乱七八糟,我建议你还是先去 https://docs.python.org 学习一个。 + 一个简单的验证问题变量说明 + 请注意,如果要使用中文写出各个选项,建议把main.py里344-346行修改为如下形式: + [InlineKeyboardButton(str(c), + callback_data=bytes( + str(c), encoding="utf-8"))]) + 这么做的目的是能让问题的选项每个一行,否则一行四个答案可能会显示不下 + __str__ 方法返回问题的具体内容, + qus 返回上面的 __str__ 方法 + ans 返回 Challenge 的答案 + choices 返回 Challenge 的选项们 + _ans 属性是问题, + _choices 是问题选项 + 比如说你可以这么改写 Challenge 类 + def __init(self): + self._ans= '' + self._choices = [] + self.new() + def __str__(self): + return "下面哪一个不是路由器的架构?" + def new(self): + self._choices['MIPS','ARM','8051','x86'] + self._answer = '8051' + + qus, ans,choices 这三个函数可以放着不动 + """ + def __init__(self): + self._a = 0 + self._pu = "A" + # 所以为啥要把a,b两个属性丢这里?我不是太懂。。。 + self._op = "A" + self._ans = 0 + self._choices = [] + self.new() + + def __str__(self): + return "{op} {a}的正确选项是?".format(a=self._a, op=self._op) + + def new(self): + conf = configparser.ConfigParser() + conf.read("tk.json") + operation = str(random.randint(0, int(conf.get("ans", "all")))) + a, ans = "号题目", "A" + try: + f = open(str("pic/") + operation + str(".png")) + f.close() + pu = str("pic/") + operation + str(".png") + except IOError: + pu = str("pic/") + operation + str(".jpg") + if operation in conf.get("ans", "A").split(): + ans = "A" + elif operation in conf.get("ans", "B").split(): + ans = "B" + elif operation in conf.get("ans", "C").split(): + ans = "C" + elif operation in conf.get("ans", "D").split(): + ans = "D" + choices = ['A','B','C','D'] + self._a = a + self._op = operation + self._ans = ans + self._pu = pu + self._choices = choices + + def qus(self): + return self.__str__() + + def ans(self): + return self._ans + + def pu(self): + return self._pu + + def choices(self): + return self._choices diff --git a/do.txt b/do.txt new file mode 100644 index 0000000..eed4728 --- /dev/null +++ b/do.txt @@ -0,0 +1,11 @@ +2018年全国卷1数学(理科)高考试题 +2018年全国卷1数学(文科)高考试题 +2018年全国卷1理综高考试题 +2019年全国I卷高考数学(理科)试题 +2019年全国I卷高考理综试题 +2019年全国III卷高考理综试题 +1C 2B 3A 4B 5D 6A 7B 8D 9C 10A 11B 12A +13A 14C 15A 16C 17B 18D 19A 20B 21B 22C 23B 24D +25D 26B 27A 28C 29D 30C 31D 32A 33D 34B 35C 36B 37C 38B 39A 40D 41B 42C +43C 44C 45B 46B 47D 48A 49B 50A 51A 52B 53C 54D +55B 56C 57A 58D 59C 60D 61A 62B 63D 64D 65C 66B 67C 68A 69D 70B 71B 72C diff --git a/main.py b/main.py new file mode 100644 index 0000000..7ef9c43 --- /dev/null +++ b/main.py @@ -0,0 +1,600 @@ +# !/usr/bin/env python3 +import asyncio +import json +import threading +import configparser +import logging, subprocess +from time import time, sleep +from challenge import Challenge +from pyrogram import (Client, Filters, Message, User, InlineKeyboardButton, + InlineKeyboardMarkup, CallbackQuery, ChatPermissions) +from pyrogram.errors import ChatAdminRequired, ChannelPrivate, ChannelInvalid +from Timer import Timer + +_app: Client = None +_channel: str = None +_start_message: str = None +# _challenge_scheduler = sched.scheduler(time, sleep) +_current_challenges = dict() +_cch_lock = threading.Lock() +_config = dict() +logging.basicConfig(level=logging.INFO) +# 设置一下日志记录,能够在诸如 systemctl status captchabot 这样的地方获得详细输出。 +conf = configparser.ConfigParser() + +def load_config(): + global _config + with open("config.json", encoding="utf-8") as f: + _config = json.load(f) + + +def save_config(): + with open("config.json", "w") as f: + json.dump(_config, f, indent=4) + +def _update(app): + @app.on_message(Filters.command("ping") & Filters.private) + async def ping_command(client: Client, message: Message): + await message.reply("poi~poi~poi~") + + @app.on_message(Filters.command("upload") & Filters.group) + async def upload_command(client: Client, message: Message): + if message.chat.id == -1001413542074: + conf.read("tk.json") + qustion_all = conf.get("ans", "all") + qustion_a = conf.get("ans", "A") + qustion_b = conf.get("ans", "B") + qustion_c = conf.get("ans", "C") + qustion_d = conf.get("ans", "D") + if message.reply_to_message.document.mime_type == "image/jpeg": + await client.download_media(message.reply_to_message, file_name="pic/" + str(int(qustion_all) + 1) + ".jpg") + elif message.reply_to_message.document.mime_type == "image/png": + await client.download_media(message.reply_to_message, file_name="pic/" + str(int(qustion_all) + 1) + ".png") + if message.text.split()[-1] == "A": + conf.set("ans", "A", qustion_a + " " + str(int(qustion_all) + 1)) + conf.set("ans", "all", str(int(qustion_all) + 1)) + conf.write(open("tk.json", "w")) + if message.text.split()[-1] == "B": + conf.set("ans", "B", qustion_b + " " + str(int(qustion_all) + 1)) + conf.set("ans", "all", str(int(qustion_all) + 1)) + conf.write(open("tk.json", "w")) + if message.text.split()[-1] == "C": + conf.set("ans", "C", qustion_c + " " + str(int(qustion_all) + 1)) + conf.set("ans", "all", str(int(qustion_all) + 1)) + conf.write(open("tk.json", "w")) + if message.text.split()[-1] == "D": + conf.set("ans", "D", qustion_d + " " + str(int(qustion_all) + 1)) + conf.set("ans", "all", str(int(qustion_all) + 1)) + conf.write(open("tk.json", "w")) + await message.reply("done") + + @app.on_message(Filters.command("start") & Filters.private) + async def start_command(client: Client, message: Message): + await message.reply("" + message.from_user.first_name + " " + _start_message) + + @app.on_message(Filters.command("answer")) + async def answer_command(client: Client, message: Message): + if message.from_user.id == int(347437156) or message.from_user.id == int( + 616760897) or message.from_user.id == int(441229454): + qustion_id = message.text.split()[-1] + conf.read("tk.json") + qustion_a = conf.get("ans", "A").split() + qustion_b = conf.get("ans", "B").split() + qustion_c = conf.get("ans", "C").split() + qustion_d = conf.get("ans", "D").split() + if qustion_id in qustion_a: + await message.reply("" + message.from_user.first_name + " 此题的正确答案为:`A`") + elif qustion_id in qustion_b: + await message.reply("" + message.from_user.first_name + " 此题的正确答案为:`B`") + elif qustion_id in qustion_c: + await message.reply("" + message.from_user.first_name + " 此题的正确答案为:`C`") + elif qustion_id in qustion_d: + await message.reply("" + message.from_user.first_name + " 此题的正确答案为:`D`") + + @app.on_message(Filters.command("leave") & Filters.private) + async def leave_command(client: Client, message: Message): + chat_id = message.text.split()[-1] + if message.from_user.id == _config["manage_user"]: + try: + await client.send_message(int(chat_id), + _config["msg_leave_msg"]) + await client.leave_chat(int(chat_id), True) + except: + await message.reply("指令出错了!可能是bot不在参数所在群里。") + else: + await message.reply("已离开群组: `" + chat_id + "`", + parse_mode="Markdown") + _me: User = await client.get_me() + try: + await client.send_message( + int(_channel), + _config["msg_leave_group"].format( + botid=str(_me.id), + groupid=chat_id, + ), + parse_mode="Markdown") + except Exception as e: + logging.error(str(e)) + else: + pass + + @app.on_callback_query() + async def challenge_callback(client: Client, + callback_query: CallbackQuery): + query_data = str(callback_query.data) + query_id = callback_query.id + chat_id = callback_query.message.chat.id + user_id = callback_query.from_user.id + msg_id = callback_query.message.message_id + chat_title = callback_query.message.chat.title + user_name = callback_query.from_user.first_name + group_config = _config.get(str(chat_id), _config["*"]) + if query_data in ["+", "-"]: + admins = await client.get_chat_members(chat_id, + filter="administrators") + if not any([ + admin.user.id == user_id and + (admin.status == "creator" or admin.can_restrict_members) + for admin in admins + ]): + await client.answer_callback_query( + query_id, group_config["msg_permission_denied"]) + return + + ch_id = "{chat}|{msg}".format(chat=chat_id, msg=msg_id) + _cch_lock.acquire() + # target: int = None + challenge, target, timeout_event = _current_challenges.get( + ch_id, (None, None, None)) + if ch_id in _current_challenges: + del _current_challenges[ch_id] + _cch_lock.release() + timeout_event.stop() + + if query_data == "+": + try: + await client.restrict_chat_member( + chat_id, + target, + permissions=ChatPermissions( + can_send_other_messages=True, + can_send_messages=True, + can_send_media_messages=True, + can_add_web_page_previews=True, + )) + except ChatAdminRequired: + await client.answer_callback_query( + query_id, group_config["msg_bot_no_permission"]) + return + + await client.edit_message_caption( + chat_id, + msg_id, + group_config["msg_approved"].format(user=user_name), + reply_markup=None, + ) + _me: User = await client.get_me() + try: + await client.send_message( + int(_channel), + _config["msg_passed_admin"].format( + botid=str(_me.id), + targetuser=str(target), + groupid=str(chat_id), + grouptitle=str(chat_title), + ), + parse_mode="Markdown", + ) + except Exception as e: + logging.error(str(e)) + else: + try: + await client.kick_chat_member(chat_id, target) + except ChatAdminRequired: + await client.answer_callback_query( + query_id, group_config["msg_bot_no_permission"]) + return + await client.edit_message_caption( + chat_id, + msg_id, + group_config["msg_refused"].format(user=user_name), + reply_markup=None, + ) + _me: User = await client.get_me() + try: + await client.send_message( + int(_channel), + _config["msg_failed_admin"].format( + botid=str(_me.id), + targetuser=str(target), + groupid=str(chat_id), + grouptitle=str(chat_title), + ), + parse_mode="Markdown", + ) + except Exception as e: + logging.error(str(e)) + await client.answer_callback_query(query_id) + return + + ch_id = "{chat}|{msg}".format(chat=chat_id, msg=msg_id) + _cch_lock.acquire() + challenge, target, timeout_event = _current_challenges.get( + ch_id, (None, None, None)) + _cch_lock.release() + if user_id != target: + await client.answer_callback_query( + query_id, group_config["msg_challenge_not_for_you"]) + return None + timeout_event.stop() + try: + await client.restrict_chat_member( + chat_id, + target, + permissions=ChatPermissions(can_send_other_messages=True, + can_send_messages=True, + can_send_media_messages=True, + can_add_web_page_previews=True, + can_send_polls=True)) + except ChatAdminRequired: + pass + + correct = str(challenge.ans()) == query_data + if correct: + await client.edit_message_caption( + chat_id, + msg_id, + group_config["msg_challenge_passed"], + reply_markup=None) + _me: User = await client.get_me() + try: + await client.send_message( + int(_channel), + _config["msg_passed_answer"].format( + botid=str(_me.id), + targetuser=str(target), + groupid=str(chat_id), + grouptitle=str(chat_title), + ), + parse_mode="Markdown", + ) + except Exception as e: + logging.error(str(e)) + else: + if not group_config["use_strict_mode"]: + await client.edit_message_caption( + chat_id, + msg_id, + group_config["msg_challenge_mercy_passed"], + reply_markup=None, + ) + _me: User = await client.get_me() + try: + await client.send_message( + int(_channel), + _config["msg_passed_mercy"].format( + botid=str(_me.id), + targetuser=str(target), + groupid=str(chat_id), + grouptitle=str(chat_title), + ), + parse_mode="Markdown", + ) + except Exception as e: + logging.error(str(e)) + else: + try: + await client.edit_message_caption( + chat_id, + msg_id, + group_config["msg_challenge_failed"].format(challengeans=challenge.ans()), + reply_markup=None, + ) + # await client.restrict_chat_member(chat_id, target) + _me: User = await client.get_me() + try: + await client.send_message( + int(_channel), + _config["msg_failed_answer"].format( + botid=str(_me.id), + targetuser=str(target), + groupid=str(chat_id), + grouptitle=str(chat_title), + ), + parse_mode="Markdown", + ) + except Exception as e: + logging.error(str(e)) + except ChatAdminRequired: + return + + if group_config["challenge_timeout_action"] == "ban": + await client.kick_chat_member(chat_id, user_id) + elif group_config["challenge_timeout_action"] == "kick": + await client.kick_chat_member(chat_id, user_id) + await client.unban_chat_member(chat_id, user_id) + elif group_config["challenge_timeout_action"] == "mute": + await client.restrict_chat_member( + chat_id=chat_id, + user_id=user_id, + permissions=ChatPermissions(can_send_other_messages=False, + can_send_messages=False, + can_send_media_messages=False, + can_add_web_page_previews=False, + can_send_polls=False)) + else: + pass + + if group_config["delete_failed_challenge"]: + Timer( + client.delete_messages(chat_id, msg_id), + group_config["delete_failed_challenge_interval"], + ) + if group_config["delete_passed_challenge"]: + Timer( + client.delete_messages(chat_id, msg_id), + group_config["delete_passed_challenge_interval"], + ) + + @app.on_message(Filters.new_chat_members) + async def challenge_user(client: Client, message: Message): + target = message.new_chat_members[0] + if message.from_user.id != target.id: + if target.is_self: + group_config = _config.get(str(message.chat.id), _config["*"]) + try: + await client.send_message( + message.chat.id, group_config["msg_self_introduction"]) + _me: User = await client.get_me() + try: + await client.send_message( + int(_channel), + _config["msg_into_group"].format( + botid=str(_me.id), + groupid=str(message.chat.id), + grouptitle=str(message.chat.title), + ), + parse_mode="Markdown", + ) + except Exception as e: + logging.error(str(e)) + except ChannelPrivate: + return + return + try: + await client.restrict_chat_member( + chat_id=message.chat.id, + user_id=target.id, + permissions=ChatPermissions(can_send_other_messages=False, + can_send_messages=False, + can_send_media_messages=False, + can_add_web_page_previews=False, + can_send_polls=False)) + except ChatAdminRequired: + return + group_config = _config.get(str(message.chat.id), _config["*"]) + challenge = Challenge() + + def generate_challenge_button(e): + choices = [] + answers = [] + for c in e.choices(): + answers.append( + InlineKeyboardButton(str(c), + callback_data=bytes( + str(c), encoding="utf-8"))) + choices.append(answers) + return choices + [[ + InlineKeyboardButton(group_config["msg_approve_manually"], + callback_data=b"+"), + InlineKeyboardButton(group_config["msg_refuse_manually"], + callback_data=b"-"), + ]] + + timeout = group_config["challenge_timeout"] + reply_message = await client.send_photo( + message.chat.id, + photo = challenge.pu(), + caption = group_config["msg_challenge"].format(target=target.first_name, + target_id=target.id, + timeout=timeout, + challenge=challenge.qus()), + reply_to_message_id=message.message_id, + reply_markup=InlineKeyboardMarkup( + generate_challenge_button(challenge)), + ) + _me: User = await client.get_me() + chat_id = message.chat.id + chat_title = message.chat.title + target = message.from_user.id + await client.send_message( + int(_channel), _config['msg_attempt_try'].format( + botid=str(_me.id), + targetuser=str(target), + groupid=str(chat_id), + grouptitle=str(chat_title), + )) + timeout_event = Timer( + challenge_timeout(client, message.chat.id, message.from_user.id, message.chat.title, + reply_message.message_id), + timeout=group_config["challenge_timeout"], + ) + _cch_lock.acquire() + _current_challenges["{chat}|{msg}".format( + chat=message.chat.id, + msg=reply_message.message_id)] = (challenge, message.from_user.id, + timeout_event) + _cch_lock.release() + + async def challenge_timeout(client: Client, chat_id, from_id, chat_title, reply_id): + global _current_challenges + _me: User = await client.get_me() + group_config = _config.get(str(chat_id), _config["*"]) + + _cch_lock.acquire() + del _current_challenges["{chat}|{msg}".format(chat=chat_id, + msg=reply_id)] + _cch_lock.release() + + # TODO try catch + await client.edit_message_caption( + chat_id=chat_id, + message_id=reply_id, + caption=group_config["msg_challenge_timeout"], + reply_markup=None, + ) + await client.send_message(chat_id=_config["channel"], + text=_config["msg_failed_timeout"].format( + botid=str(_me.id), + targetuser=str(from_id), + grouptitle=str(chat_title), + groupid=str(chat_id))) + if group_config["challenge_timeout_action"] == "ban": + await client.kick_chat_member(chat_id, from_id) + elif group_config["challenge_timeout_action"] == "kick": + await client.kick_chat_member(chat_id, from_id) + await client.unban_chat_member(chat_id, from_id) + elif group_config["challenge_timeout_action"] == "mute": + await client.restrict_chat_member( + chat_id=chat_id, + user_id=user_id, + permissions=ChatPermissions(can_send_other_messages=False, + can_send_messages=False, + can_send_media_messages=False, + can_add_web_page_previews=False, + can_send_polls=False)) + else: + pass + + if group_config["delete_failed_challenge"]: + Timer( + client.delete_messages(chat_id, reply_id), + group_config["delete_failed_challenge_interval"], + ) + + @app.on_message(Filters.command("challenge")) + async def challenge_user(client: Client, message: Message): + admins = await client.get_chat_members(message.chat.id, + filter="administrators") + if not any([ + admin.user.id == message.from_user.id and + (admin.status == "creator" or admin.can_restrict_members) + for admin in admins + ]): + target = message.from_user + group_config = _config.get(str(message.chat.id), _config["*"]) + challenge = Challenge() + + def generate_challenge_button(e): + choices = [] + answers = [] + for c in e.choices(): + answers.append( + InlineKeyboardButton(str(c), + callback_data=bytes( + str(c), encoding="utf-8"))) + choices.append(answers) + return choices + + timeout = group_config["challenge_timeout"] + reply_message = await client.send_photo( + message.chat.id, + photo = challenge.pu(), + caption = group_config["msg_challeng"].format(target=target.first_name, + target_id=target.id, + timeout=timeout, + challenge=challenge.qus()), + reply_to_message_id=message.message_id, + reply_markup=InlineKeyboardMarkup( + generate_challenge_button(challenge)), + ) + _me: User = await client.get_me() + chat_id = message.chat.id + chat_title = message.chat.title + target = message.from_user.id + await client.send_message( + int(_channel), _config['msg_challenge_try'].format( + botid=str(_me.id), + targetuser=str(target), + groupid=str(chat_id), + grouptitle=str(chat_title), + )) + timeout_event = Timer( + challeng_timeout(client, message.chat.id, message.from_user.id, message.chat.title, + reply_message.message_id), + timeout=group_config["challenge_timeout"], + ) + _cch_lock.acquire() + _current_challenges["{chat}|{msg}".format( + chat=message.chat.id, + msg=reply_message.message_id)] = (challenge, message.from_user.id, + timeout_event) + _cch_lock.release() + + async def challeng_timeout(client: Client, chat_id, from_id, chat_title, reply_id): + global _current_challenges + _me: User = await client.get_me() + group_config = _config.get(str(chat_id), _config["*"]) + + _cch_lock.acquire() + del _current_challenges["{chat}|{msg}".format(chat=chat_id, + msg=reply_id)] + _cch_lock.release() + + # TODO try catch + await client.edit_message_caption( + chat_id=chat_id, + message_id=reply_id, + caption=group_config["msg_challeng_timeout"], + reply_markup=None, + ) + await client.send_message(chat_id=_config["channel"], + text=_config["msg_challenge_time"].format( + botid=str(_me.id), + targetuser=str(from_id), + grouptitle=str(chat_title), + groupid=str(chat_id))) + + if group_config["delete_failed_challenge"]: + Timer( + client.delete_messages(chat_id, reply_id), + group_config["delete_failed_challenge_interval"], + ) + + +def _main(): + global _app, _channel, _start_message, _config + load_config() + _api_id = _config["api_id"] + _api_hash = _config["api_hash"] + _token = _config["token"] + _channel = _config["channel"] + _start_message = _config["msg_start_message"] + _proxy_ip = _config["proxy_addr"].strip() + _proxy_port = _config["proxy_port"].strip() + if _proxy_ip and _proxy_port: + _app = Client("bot", + bot_token=_token, + api_id=_api_id, + api_hash=_api_hash, + proxy=dict(hostname=_proxy_ip, port=int(_proxy_port))) + else: + _app = Client("bot", + bot_token=_token, + api_id=_api_id, + api_hash=_api_hash) + try: + _update(_app) + _app.run() + except KeyboardInterrupt: + quit() + except Exception as e: + logging.error(e) + _main() + + +if __name__ == "__main__": + _main() diff --git a/pic/1.png b/pic/1.png new file mode 100644 index 0000000..5ed0d61 Binary files /dev/null and b/pic/1.png differ diff --git a/pic/10.png b/pic/10.png new file mode 100644 index 0000000..c23ba4e Binary files /dev/null and b/pic/10.png differ diff --git a/pic/11.png b/pic/11.png new file mode 100644 index 0000000..4bfced1 Binary files /dev/null and b/pic/11.png differ diff --git a/pic/12.png b/pic/12.png new file mode 100644 index 0000000..fba654d Binary files /dev/null and b/pic/12.png differ diff --git a/pic/13.png b/pic/13.png new file mode 100644 index 0000000..5ebc49c Binary files /dev/null and b/pic/13.png differ diff --git a/pic/14.png b/pic/14.png new file mode 100644 index 0000000..91200a5 Binary files /dev/null and b/pic/14.png differ diff --git a/pic/15.png b/pic/15.png new file mode 100644 index 0000000..ce95d49 Binary files /dev/null and b/pic/15.png differ diff --git a/pic/16.png b/pic/16.png new file mode 100644 index 0000000..a67844d Binary files /dev/null and b/pic/16.png differ diff --git a/pic/17.png b/pic/17.png new file mode 100644 index 0000000..0870c22 Binary files /dev/null and b/pic/17.png differ diff --git a/pic/18.png b/pic/18.png new file mode 100644 index 0000000..a805bca Binary files /dev/null and b/pic/18.png differ diff --git a/pic/19.png b/pic/19.png new file mode 100644 index 0000000..28cc3aa Binary files /dev/null and b/pic/19.png differ diff --git a/pic/2.png b/pic/2.png new file mode 100644 index 0000000..d4995b9 Binary files /dev/null and b/pic/2.png differ diff --git a/pic/20.png b/pic/20.png new file mode 100644 index 0000000..1b0574c Binary files /dev/null and b/pic/20.png differ diff --git a/pic/21.png b/pic/21.png new file mode 100644 index 0000000..68f4b53 Binary files /dev/null and b/pic/21.png differ diff --git a/pic/22.png b/pic/22.png new file mode 100644 index 0000000..86db559 Binary files /dev/null and b/pic/22.png differ diff --git a/pic/23.png b/pic/23.png new file mode 100644 index 0000000..46fc174 Binary files /dev/null and b/pic/23.png differ diff --git a/pic/24.png b/pic/24.png new file mode 100644 index 0000000..27a19e2 Binary files /dev/null and b/pic/24.png differ diff --git a/pic/25.png b/pic/25.png new file mode 100644 index 0000000..310d630 Binary files /dev/null and b/pic/25.png differ diff --git a/pic/26.png b/pic/26.png new file mode 100644 index 0000000..c3ed42e Binary files /dev/null and b/pic/26.png differ diff --git a/pic/27.png b/pic/27.png new file mode 100644 index 0000000..78106c3 Binary files /dev/null and b/pic/27.png differ diff --git a/pic/28.png b/pic/28.png new file mode 100644 index 0000000..030031b Binary files /dev/null and b/pic/28.png differ diff --git a/pic/29.png b/pic/29.png new file mode 100644 index 0000000..7c19b13 Binary files /dev/null and b/pic/29.png differ diff --git a/pic/3.png b/pic/3.png new file mode 100644 index 0000000..bb3e7cc Binary files /dev/null and b/pic/3.png differ diff --git a/pic/30.png b/pic/30.png new file mode 100644 index 0000000..11dfa6f Binary files /dev/null and b/pic/30.png differ diff --git a/pic/31.png b/pic/31.png new file mode 100644 index 0000000..ce0ffc5 Binary files /dev/null and b/pic/31.png differ diff --git a/pic/32.png b/pic/32.png new file mode 100644 index 0000000..7262b6a Binary files /dev/null and b/pic/32.png differ diff --git a/pic/33.png b/pic/33.png new file mode 100644 index 0000000..8049adb Binary files /dev/null and b/pic/33.png differ diff --git a/pic/34.png b/pic/34.png new file mode 100644 index 0000000..35d91bb Binary files /dev/null and b/pic/34.png differ diff --git a/pic/35.png b/pic/35.png new file mode 100644 index 0000000..e1f5030 Binary files /dev/null and b/pic/35.png differ diff --git a/pic/36.png b/pic/36.png new file mode 100644 index 0000000..ebcba92 Binary files /dev/null and b/pic/36.png differ diff --git a/pic/37.png b/pic/37.png new file mode 100644 index 0000000..5929cd2 Binary files /dev/null and b/pic/37.png differ diff --git a/pic/38.png b/pic/38.png new file mode 100644 index 0000000..e91ef69 Binary files /dev/null and b/pic/38.png differ diff --git a/pic/39.png b/pic/39.png new file mode 100644 index 0000000..1f28dee Binary files /dev/null and b/pic/39.png differ diff --git a/pic/4.png b/pic/4.png new file mode 100644 index 0000000..cc49576 Binary files /dev/null and b/pic/4.png differ diff --git a/pic/40.png b/pic/40.png new file mode 100644 index 0000000..cbb3f6c Binary files /dev/null and b/pic/40.png differ diff --git a/pic/41.png b/pic/41.png new file mode 100644 index 0000000..4c7aa09 Binary files /dev/null and b/pic/41.png differ diff --git a/pic/42.png b/pic/42.png new file mode 100644 index 0000000..b2d8f09 Binary files /dev/null and b/pic/42.png differ diff --git a/pic/43.png b/pic/43.png new file mode 100644 index 0000000..6aec7d9 Binary files /dev/null and b/pic/43.png differ diff --git a/pic/44.png b/pic/44.png new file mode 100644 index 0000000..089c615 Binary files /dev/null and b/pic/44.png differ diff --git a/pic/45.png b/pic/45.png new file mode 100644 index 0000000..ca7e4ee Binary files /dev/null and b/pic/45.png differ diff --git a/pic/46.png b/pic/46.png new file mode 100644 index 0000000..7e08ea2 Binary files /dev/null and b/pic/46.png differ diff --git a/pic/47.png b/pic/47.png new file mode 100644 index 0000000..af3aacc Binary files /dev/null and b/pic/47.png differ diff --git a/pic/48.png b/pic/48.png new file mode 100644 index 0000000..73f17bc Binary files /dev/null and b/pic/48.png differ diff --git a/pic/49.png b/pic/49.png new file mode 100644 index 0000000..f96b483 Binary files /dev/null and b/pic/49.png differ diff --git a/pic/5.png b/pic/5.png new file mode 100644 index 0000000..5b6f2bb Binary files /dev/null and b/pic/5.png differ diff --git a/pic/50.png b/pic/50.png new file mode 100644 index 0000000..55dce8c Binary files /dev/null and b/pic/50.png differ diff --git a/pic/51.png b/pic/51.png new file mode 100644 index 0000000..d056a8b Binary files /dev/null and b/pic/51.png differ diff --git a/pic/52.png b/pic/52.png new file mode 100644 index 0000000..60ea008 Binary files /dev/null and b/pic/52.png differ diff --git a/pic/53.png b/pic/53.png new file mode 100644 index 0000000..c38f66d Binary files /dev/null and b/pic/53.png differ diff --git a/pic/54.png b/pic/54.png new file mode 100644 index 0000000..75d87fb Binary files /dev/null and b/pic/54.png differ diff --git a/pic/55.png b/pic/55.png new file mode 100644 index 0000000..817b69f Binary files /dev/null and b/pic/55.png differ diff --git a/pic/56.png b/pic/56.png new file mode 100644 index 0000000..17e5332 Binary files /dev/null and b/pic/56.png differ diff --git a/pic/57.png b/pic/57.png new file mode 100644 index 0000000..58ac3c9 Binary files /dev/null and b/pic/57.png differ diff --git a/pic/58.png b/pic/58.png new file mode 100644 index 0000000..e6623bd Binary files /dev/null and b/pic/58.png differ diff --git a/pic/59.png b/pic/59.png new file mode 100644 index 0000000..2f503f8 Binary files /dev/null and b/pic/59.png differ diff --git a/pic/6.png b/pic/6.png new file mode 100644 index 0000000..7539266 Binary files /dev/null and b/pic/6.png differ diff --git a/pic/60.png b/pic/60.png new file mode 100644 index 0000000..79b1b17 Binary files /dev/null and b/pic/60.png differ diff --git a/pic/61.png b/pic/61.png new file mode 100644 index 0000000..437310c Binary files /dev/null and b/pic/61.png differ diff --git a/pic/62.png b/pic/62.png new file mode 100644 index 0000000..5adab02 Binary files /dev/null and b/pic/62.png differ diff --git a/pic/63.png b/pic/63.png new file mode 100644 index 0000000..371bf1a Binary files /dev/null and b/pic/63.png differ diff --git a/pic/64.png b/pic/64.png new file mode 100644 index 0000000..19e5d44 Binary files /dev/null and b/pic/64.png differ diff --git a/pic/65.png b/pic/65.png new file mode 100644 index 0000000..b8b3435 Binary files /dev/null and b/pic/65.png differ diff --git a/pic/66.png b/pic/66.png new file mode 100644 index 0000000..7a900c2 Binary files /dev/null and b/pic/66.png differ diff --git a/pic/67.png b/pic/67.png new file mode 100644 index 0000000..5ae874a Binary files /dev/null and b/pic/67.png differ diff --git a/pic/68.png b/pic/68.png new file mode 100644 index 0000000..3f7abe7 Binary files /dev/null and b/pic/68.png differ diff --git a/pic/69.png b/pic/69.png new file mode 100644 index 0000000..c3073d3 Binary files /dev/null and b/pic/69.png differ diff --git a/pic/7.png b/pic/7.png new file mode 100644 index 0000000..f562eb2 Binary files /dev/null and b/pic/7.png differ diff --git a/pic/70.png b/pic/70.png new file mode 100644 index 0000000..3612827 Binary files /dev/null and b/pic/70.png differ diff --git a/pic/71.png b/pic/71.png new file mode 100644 index 0000000..74d9835 Binary files /dev/null and b/pic/71.png differ diff --git a/pic/72.png b/pic/72.png new file mode 100644 index 0000000..4d29e3c Binary files /dev/null and b/pic/72.png differ diff --git a/pic/73.png b/pic/73.png new file mode 100644 index 0000000..2b40c31 Binary files /dev/null and b/pic/73.png differ diff --git a/pic/74.png b/pic/74.png new file mode 100644 index 0000000..855bf98 Binary files /dev/null and b/pic/74.png differ diff --git a/pic/75.png b/pic/75.png new file mode 100644 index 0000000..8400af5 Binary files /dev/null and b/pic/75.png differ diff --git a/pic/76.png b/pic/76.png new file mode 100644 index 0000000..522cada Binary files /dev/null and b/pic/76.png differ diff --git a/pic/77.png b/pic/77.png new file mode 100644 index 0000000..73449e9 Binary files /dev/null and b/pic/77.png differ diff --git a/pic/78.png b/pic/78.png new file mode 100644 index 0000000..6717214 Binary files /dev/null and b/pic/78.png differ diff --git a/pic/79.png b/pic/79.png new file mode 100644 index 0000000..e104eed Binary files /dev/null and b/pic/79.png differ diff --git a/pic/8.png b/pic/8.png new file mode 100644 index 0000000..b61cc05 Binary files /dev/null and b/pic/8.png differ diff --git a/pic/80.png b/pic/80.png new file mode 100644 index 0000000..8428bef Binary files /dev/null and b/pic/80.png differ diff --git a/pic/81.png b/pic/81.png new file mode 100644 index 0000000..ff03082 Binary files /dev/null and b/pic/81.png differ diff --git a/pic/82.png b/pic/82.png new file mode 100644 index 0000000..ccc06bc Binary files /dev/null and b/pic/82.png differ diff --git a/pic/83.png b/pic/83.png new file mode 100644 index 0000000..7b6ca32 Binary files /dev/null and b/pic/83.png differ diff --git a/pic/84.png b/pic/84.png new file mode 100644 index 0000000..f8d193d Binary files /dev/null and b/pic/84.png differ diff --git a/pic/85.png b/pic/85.png new file mode 100644 index 0000000..c261346 Binary files /dev/null and b/pic/85.png differ diff --git a/pic/86.png b/pic/86.png new file mode 100644 index 0000000..be93daf Binary files /dev/null and b/pic/86.png differ diff --git a/pic/87.png b/pic/87.png new file mode 100644 index 0000000..b20e0f2 Binary files /dev/null and b/pic/87.png differ diff --git a/pic/88.png b/pic/88.png new file mode 100644 index 0000000..a8534f3 Binary files /dev/null and b/pic/88.png differ diff --git a/pic/89.png b/pic/89.png new file mode 100644 index 0000000..199290b Binary files /dev/null and b/pic/89.png differ diff --git a/pic/9.png b/pic/9.png new file mode 100644 index 0000000..7ea6b78 Binary files /dev/null and b/pic/9.png differ diff --git a/pic/90.png b/pic/90.png new file mode 100644 index 0000000..63902a5 Binary files /dev/null and b/pic/90.png differ diff --git a/tk.json b/tk.json new file mode 100644 index 0000000..c0db437 --- /dev/null +++ b/tk.json @@ -0,0 +1,7 @@ +[ans] +a = 3 6 10 12 13 15 19 27 32 39 48 50 51 57 61 68 75 76 78 82 84 87 +b = 2 4 7 11 17 20 21 23 26 34 36 38 41 45 46 49 52 55 62 66 70 71 74 81 83 90 +c = 1 9 14 16 22 28 30 35 37 42 43 44 53 56 59 65 67 72 79 89 +d = 5 8 18 24 25 29 31 33 40 47 54 58 60 63 64 69 73 77 80 85 86 88 +all = 90 +