diff --git a/README.md b/README.md index 8378572..5383d42 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Теперь заходим в директорию бота (`cd dan63047pythonbot/`) и устанавливаем необходимые библиотеки: -`pip3 install vk-api pyowm Wikipedia-API PyMySQL --user` +`pip3 install vk-api pyowm Wikipedia-API --user` Эта команда установит все необходимые библиотеки для работы бота. @@ -58,18 +58,11 @@ admins = [276193568, 629085267] # Список ид аккаунтов адми vk_service_token = "vk_service_token" # Сервисный ключ доступа для команды !image. Оставьте None, если хотите выключить эту команду album_for_command = 269199619 # Цифровой ид альбома для команды !image. Оставьте None, если хотите выключить эту команду openweathermap_api_key = 'openweathermap_api_key' # Ключ OpenWeather API. Оставьте None, если хотите выключить команду !weather -use_database = True # False, если вы не планируете использовать MySQL базу данных, иначе следующие 4 строки обязательны -mysql_host = 'pythonanywhere_nickname.mysql.pythonanywhere-services.com' # ссылка на хост БД. Вместо pythonanywhere_nickname должен быть ваш никнейм на pythonanywhere -mysql_user = 'pythonanywhere_nickname' # Ваш никнейм на pythonanywhere -mysql_pass = 'qwerty123' # Пароль, созданный вами для БД -mysql_db = 'pythonanywhere_nickname$default' # Название вашей БД. Вместо pythonanywhere_nickname должен быть ваш никнейм на pythonanywhere. Так же вместо default может быть ваше название БД ``` ## Если вы будете использовать команду !image Разберёмся с костылём для метода `photos.get`: Идем на `vk.com/dev`, нажимаем на *Мои приложения* и кликаем по *Создать приложение*. Новое приложение может называться как угодно, от него нам необходимо всего лишь сервисный ключ доступа, который можно найти, если нажать на *Настройки* ## Если вы будете использовать команду !weather Перейдите по `openweathermap.org`, пройдите там регистрацию и создайте API ключ -## Если вы решите использовать MySQL БД (давно не тестировалось, может не работать) -Вам необходимо будет купить тариф на pythonanywhere, где есть поддержка MySQL базы данных, с которой бот будет работать. Тыкаем на *Databases* в шапке сайта. Создаём пароль для вашей базы данных. По умолчанию будет создана база данных с названием `your_nickname$default`, но вы можете создать БД со своим названием. ## Запуск бота Если аккаунт платный: в pythonanywhere тыкаем на *Tasks*, создаём и запускаем в *Always-on tasks* задачу `python3.8 /home/pythonanywhere_nickname/dan63047pythonbot/longpulling.py`, где вместо pythonanywhere_nickname должен быть ваш никнейм на pythonanywhere. После небольшого тупления pythonanywhere запустит вашего бота. @@ -94,9 +87,7 @@ mysql_db = 'pythonanywhere_nickname$default' # Название вашей БД. * [vk_api](https://github.com/python273/vk_api) — модуль для создания скриптов для социальной сети Вконтакте * [pyowm](https://github.com/csparpa/pyowm) — модуль для получения погоды через OpenWeather API * [Wikipedia-API](https://github.com/martin-majlis/Wikipedia-API) — модуль для получения статей из Wikipedia -* [PyMySQL](https://github.com/PyMySQL/PyMySQL/) — Pure Python MySQL Client # Дополнительно - Автор бота: [Даниил Михайлов](https://vk.com/dan63047) Буду рад помощи и поддержке diff --git a/dan63047VKbot.py b/dan63047VKbot.py index 6773e42..11a02e0 100644 --- a/dan63047VKbot.py +++ b/dan63047VKbot.py @@ -43,6 +43,7 @@ def log(warning, text): print(msg) bot = {} +SPAMMER_LIST = {} errors_array = {"access": "Отказано в доступе", "miss_argument": "Отсуствует аргумент", "command_off": "Команда отключена", @@ -94,156 +95,61 @@ except Exception: class Database_worker(): def __init__(self): - if config.use_database: - log(False, "Trying to connect to database") - try: - self._CON = pymysql.connect( - host=config.mysql_host, - user=config.mysql_user, - password=config.mysql_pass, - db=config.mysql_db, - charset='utf8mb4', - cursorclass=DictCursor - ) - cur = self._CON.cursor() - except Exception as e: - debug_array['db_warnings'] += 1 - log(True, f"Unable to connect to database: {str(e)}") - try: - cur.execute("SELECT * FROM bot_users") - except: - cur.execute("CREATE TABLE bot_users ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, chat_id INT UNSIGNED, awaiting VARCHAR(128), access TINYINT, midnight BOOL, new_post BOOL, admin_mode BOOL, game_wins INT UNSIGNED, game_defeats INT UNSIGNED, game_draws INT UNSIGNED, banned BOOL)") - cur.close() - log(False, "Database connection established") - else: - log(False, "Bot will use JSON file as database") - try: - with open("data.json", "r") as data: - self._DATA_DIST = json.load(data) - data.close() - except Exception: - log(True, "data.json is not exist, it will be created soon") - self._DATA_DIST = {"users": {}} + try: + with open("data.json", "r") as data: + self._DATA_DIST = json.load(data) + data.close() + except Exception: + log(True, "data.json is not exist, it will be created") + self._DATA_DIST = {"users": {}, "spammers": []} + open("data.json", "w").write(json.dumps(self._DATA_DIST)) def set_new_user(self, peer_id, midnight=False, awaiting=None, access=1, new_post=False, admin_mode=False, game_wins=0, game_defeats=0, game_draws=0, banned=False): - if(config.use_database): - try: - cur = self._CON.cursor() - cur.execute("INSERT INTO bot_users (chat_id, awaiting, access, midnight, new_post, admin_mode, game_wins, game_defeats, game_draws, banned) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)", - (peer_id, awaiting, access, midnight, new_post, admin_mode, game_wins, game_defeats, game_draws, banned)) - self._CON.commit() - cur.close() - except Exception as e: - debug_array['db_warnings'] += 1 - log(True, f"Unable to add new user in database: {str(e)}") - else: - self._DATA_DIST['users'][peer_id] = {"awaiting": awaiting, "access": access, "midnight": midnight, "new_post": new_post, - "admin_mode": admin_mode, "game_wins": game_wins, "game_defeats": game_defeats, "game_draws": game_draws, "banned": banned} - open("data.json", "w").write(json.dumps(self._DATA_DIST)) + self._DATA_DIST['users'][peer_id] = {"awaiting": awaiting, "access": access, "midnight": midnight, "new_post": new_post, "admin_mode": admin_mode, "game_wins": game_wins, "game_defeats": game_defeats, "game_draws": game_draws, "banned": banned} + open("data.json", "w").write(json.dumps(self._DATA_DIST)) def get_all_users(self): - if not config.use_database: - with open("data.json", "r") as data: - self._DATA_DIST = json.load(data) - data.close() - return self._DATA_DIST['users'] - try: - cur = self._CON.cursor() - cur.execute("SELECT * FROM bot_users") - result = cur.fetchall() - cur.close() - return result - except Exception as e: - debug_array['db_warnings'] += 1 - log(True, f"Unable to load user from database: {str(e)}") + with open("data.json", "r") as data: + self._DATA_DIST = json.load(data) + data.close() + return self._DATA_DIST['users'] def get_from_users(self, from_id): - if not config.use_database: - with open("data.json", "r") as data: - self._DATA_DIST = json.load(data) - data.close() - if not self._DATA_DIST['users'].get(str(from_id)): - self.set_new_user(str(from_id)) - return self._DATA_DIST['users'][str(from_id)] - try: - cur = self._CON.cursor() - cur.execute( - "SELECT * FROM bot_users WHERE chat_id = %s", (from_id)) - result = cur.fetchall() - cur.close() - return result - except Exception as e: - debug_array['db_warnings'] += 1 - log(True, f"Unable to load user from database: {str(e)}") + with open("data.json", "r") as data: + self._DATA_DIST = json.load(data) + data.close() + if not self._DATA_DIST['users'].get(str(from_id)): + self.set_new_user(str(from_id)) + return self._DATA_DIST['users'][str(from_id)] def get_game_stat(self): - if not config.use_database: - return self._DATA_DIST['users'] - try: - cur = self._CON.cursor() - cur.execute( - "SELECT chat_id, game_wins, game_draws, game_defeats FROM bot_users WHERE game_wins > 0 OR game_draws > 0 OR game_defeats > 0") - result = cur.fetchall() - cur.close() - return result - except Exception as e: - debug_array['db_warnings'] += 1 - log(True, f"Unable to load stats from database: {str(e)}") - # Info: dist cannot return only the necessary keys + return self._DATA_DIST['users'] - def update_user(self, chat_id, thing, new_value): - if(config.use_database): - try: - cur = self._CON.cursor() - cur.execute(f"UPDATE bot_users SET {thing} = %s WHERE bot_users.chat_id = %s;", (new_value, chat_id)) - self._CON.commit() - cur.close() - except Exception as e: - debug_array['db_warnings'] += 1 - log(True, f"Unable to update info about user in database: {str(e)}") - else: - if not self._DATA_DIST['users'][str(chat_id)].get(thing): - if thing == "spam_list": - self._DATA_DIST['users'][str(chat_id)][thing] = [] - self._DATA_DIST['users'][str(chat_id)][thing] = new_value - open("data.json", "w").write(json.dumps(self._DATA_DIST)) + def update_user(self, chat_id, thing, new_value): + self._DATA_DIST['users'][str(chat_id)][thing] = new_value + open("data.json", "w").write(json.dumps(self._DATA_DIST)) def delete_user(self, chat_id): - if(config.use_database): - try: - cur = self._CON.cursor() - cur.execute( - "DELETE FROM bot_users, game_defeats WHERE chat_id = %s", (chat_id)) - self._CON.commit() - cur.close() - return True - except Exception as e: - debug_array['db_warnings'] += 1 - log(True, f"Unable to delete user from database: {str(e)}") - return False - else: - self._DATA_DIST['users'].pop(str(chat_id)) - open("data.json", "w").write(json.dumps(self._DATA_DIST)) + self._DATA_DIST['users'].pop(str(chat_id)) + open("data.json", "w").write(json.dumps(self._DATA_DIST)) + + def add_spammer(self, user_id): + SPAMMER_LIST.append(int(user_id)) + self._DATA_DIST["spammers"].append(int(user_id)) + open("data.json", "w").write(json.dumps(self._DATA_DIST)) + + def remove_spammer(self, user_id): + SPAMMER_LIST.pop(int(user_id)) + self._DATA_DIST["spammers"].pop(int(user_id)) + open("data.json", "w").write(json.dumps(self._DATA_DIST)) + + def read_spammers(self): + return self._DATA_DIST["spammers"] db = Database_worker() -def load_users(): - try: - log(False, "Reading database") - get_info = db.get_all_users() - for i in get_info: - if config.use_database: - bot[int(i['chat_id'])] = VkBot(int(i['chat_id']), bool(i['midnight']), i['awaiting'], int( - i['access']), bool(i['new_post']), bool(i['admin_mode']), bool(i['banned'])) - else: - bot[int(i)] = VkBot(int(i), bool(get_info[i]['midnight']), get_info[i]['awaiting'], int( - get_info[i]['access']), bool(get_info[i]['new_post']), bool(get_info[i]['admin_mode']), bool(get_info[i]['banned'])) - except Exception as lol: - debug_array['bot_warnings'] += 1 - log(True, f"Problem with creating objects: {str(lol)}") - def toFixed(numObj, digits=0): return f"{numObj:.{digits}f}" @@ -306,7 +212,6 @@ class VkBot: """ log(False, f"[BOT_{peer_id}] Created new bot-object") self._CHAT_ID = peer_id - self._SPAMMER_LIST = {} self._AWAITING_INPUT_MODE = awaiting self._ACCESS_TO_ALL = access self._MIDNIGHT_EVENT = midnight @@ -375,17 +280,19 @@ class VkBot: action = event.message.action if action['type'] == 'chat_invite_user' or action['type'] == 'chat_invite_user_by_link': user_info = vk.method('users.get', {'user_ids': action["member_id"], 'fields': 'verified,last_seen,sex'}) - chat_info = db.get_from_users(int(self._CHAT_ID)) - if chat_info.get("spam_list"): - if int(action["member_id"]) in chat_info["spam_list"]: - self.send(f'Привет, {user_info[0]["first_name"]}. К сожалению, администраторы этой беседы признали тебя спамером, поэтому мне придётся выгнать тебя отсюда') - vk.method("messages.removeChatUser", {"chat_id": int(self._CHAT_ID)-2000000000, "member_id": action["member_id"]}) - return + if int(action["member_id"]) in SPAMMER_LIST: + self.send(f'[id{action["member_id"]}|Данный пользователь] находится в антиспам базе. Исключаю...') + vk.method("messages.removeChatUser", {"chat_id": int(self._CHAT_ID)-2000000000, "member_id": action["member_id"]}) + return self.send(f'Добро пожаловать в беседу, {user_info[0]["first_name"]} {user_info[0]["last_name"]}') elif action['type'] == 'chat_kick_user': pass # user_info = vk.method('users.get', {'user_ids': action["member_id"], 'fields': 'verified,last_seen,sex'}) # self.send(f'{user_info[0]["first_name"]} {user_info[0]["last_name"]} покинул беседу') + if event.message.peer_id > 2000000000 and int(user_id) in SPAMMER_LIST: + self.send(f'[id{user_id}|Данный пользователь] находится в антиспам базе. Исключаю...') + vk.method("messages.removeChatUser", {"chat_id": int(self._CHAT_ID)-2000000000, "member_id": user_id}) + return if self._AWAITING_INPUT_MODE: if message == "Назад": self.change_await() @@ -645,14 +552,10 @@ class VkBot: message = message[1].split(' ', 1) victum = re.search(r'id\d+', message[1]) victum = victum[0][2:] - if db.get_from_users(int(self._CHAT_ID)).get("spam_list"): - chat_spammers_list = db.get_from_users(int(self._CHAT_ID))["spam_list"] - else: - chat_spammers_list = [] - if message[0] == "add" or message [0] == "добавить": + if message[0] == "add" or message[0] == "добавить": if int(victum) != int(config.owner_id): - if int(victum) not in chat_spammers_list: - chat_spammers_list.append(int(victum)) + if int(victum) not in SPAMMER_LIST: + db.add_spammer(int(victum)) respond["text"] = "Теперь он считается спамером" log(False, f"[BOT_{self._CHAT_ID}] user {victum} added to spammer list") else: @@ -661,15 +564,14 @@ class VkBot: log(False, f"[BOT_{self._CHAT_ID}] can't add to spammer list owner") elif message[0] == "remove" or message[0] == "удалить": if int(victum) != int(config.owner_id): - if int(victum) in chat_spammers_list: - chat_spammers_list.pop(int(victum)) + if int(victum) in SPAMMER_LIST: + db.remove_spammer(int(victum)) respond["text"] = "Теперь он не считается спамером" log(False, f"[BOT_{self._CHAT_ID}] user {victum} removed to spammer list") else: respond["text"] = "Его нет в этой базе" else: log(False, f"[BOT_{self._CHAT_ID}] can't restore owner") - db.update_user(int(self._CHAT_ID), "spam_list", chat_spammers_list) except IndexError: respond['text'] = errors_array["miss_argument"] except Exception as e: @@ -758,14 +660,7 @@ class VkBot: return answer def game(self, thing, user_id): - data = db.get_from_users(user_id) - if (config.use_database): - if len(data) == 0: - create_new_bot_object(user_id) - data = db.get_from_users(user_id) - d = data[0] - else: - d = data + d = db.get_from_users(user_id) if thing == "статистика": try: winrate = (d['game_wins']/(d['game_wins'] + diff --git a/longpulling.py b/longpulling.py index 529c07d..724f745 100644 --- a/longpulling.py +++ b/longpulling.py @@ -41,18 +41,20 @@ def bots(): log_msg += atch dan63047VKbot.log(False, log_msg) dan63047VKbot.debug_array['messages_get'] += 1 - if int(event.message.peer_id) not in dan63047VKbot.bot: - dan63047VKbot.create_new_bot_object(event.message.peer_id) + if event.message.peer_id not in dan63047VKbot.bot: + u = dan63047VKbot.db.get_all_users() + if str(event.message.peer_id) not in u: + dan63047VKbot.create_new_bot_object(event.message.peer_id) + else: + i = dan63047VKbot.db.get_from_users(event.message.peer_id) + dan63047VKbot.bot[event.message.peer_id] = dan63047VKbot.VkBot(event.message.peer_id, bool(i['midnight']), i['awaiting'], int(i['access']), bool(i['new_post']), bool(i['admin_mode']), bool(i['banned'])) dan63047VKbot.bot[event.message.peer_id].get_message(event) elif event.type == dan63047VKbot.VkBotEventType.WALL_POST_NEW: if event.object.post_type == "post": dan63047VKbot.log(False, f"[NEW_POST] id{event.object.id}") users = dan63047VKbot.db.get_all_users() for i in users: - if (config.use_database): - dan63047VKbot.bot[int(i['chat_id'])].event("post", event.object) - else: - dan63047VKbot.bot[int(i)].event("post", event.object) + dan63047VKbot.bot[int(i)].event("post", event.object) else: dan63047VKbot.log(False, f"[NEW_OFFER] id{event.object.id}") elif event.type == dan63047VKbot.VkBotEventType.MESSAGE_DENY: @@ -75,17 +77,14 @@ def midnight(): dan63047VKbot.log(False, "[EVENT_STARTED] \"Midnight\"") users = dan63047VKbot.db.get_all_users() for i in users: - if (config.use_database): - dan63047VKbot.bot[int(i['chat_id'])].event("midnight") - else: - dan63047VKbot.bot[int(i)].event("midnight") + dan63047VKbot.bot[int(i)].event("midnight") dan63047VKbot.log(False, "[EVENT_ENDED] \"Midnight\"") time.sleep(1) else: time.sleep(0.50) -dan63047VKbot.load_users() +dan63047VKbot.SPAMMER_LIST = dan63047VKbot.db.read_spammers() tread_bots = threading.Thread(target=bots) tread_midnight = threading.Thread(target=midnight, daemon=True) tread_bots.start()