it not ready

This commit is contained in:
dan63047 2020-08-27 12:43:09 +03:00
parent ccced85b2b
commit 3890a500ee
4 changed files with 217 additions and 126 deletions

View File

@ -1,6 +1,6 @@
# Мой личный бот на python для ВК
#### от dan63047
Этот бот просто отвечает на поддержваемые запросы в переписке с сообществом. Был написан мною в целях изучения python
Этот бот просто отвечает на поддержваемые запросы в переписке с сообществом. Был написан мною в целях изучения python. Этот README является инструкцией по установке и использованию бота.
# Функции бота
Чтобы воспользоваться функциями бота необходимо отправить команду и в некоторых случаях агрумент. Все команды начинаются с восклицательного знака
@ -64,9 +64,9 @@ mysql_db = 'pythonanywhere_nickname$default' # Название вашей БД.
## Если вы решите использовать MySQL БД
Вам необходимо будет купить тариф на pythonanywhere, где есть поддержка MySQL базы данных, с которой бот будет работать. Тыкаем на *Databases* в шапке сайта. Создаём пароль для вашей базы данных. По умолчанию будет создана база данных с названием `your_nickname$default`, но вы можете создать БД со своим названием.
## Запуск бота
Если аккаунт платный: в pythonanywhere тыкаем на *Tasks*, создаём и запускаем в *Always-on tasks* задачу `python3.8 /home/pythonanywhere_nickname/dan63047pythonbot/dan63047bot.py`, где вместо pythonanywhere_nickname должен быть ваш никнейм на pythonanywhere. После небольшого тупления pythonanywhere запустит вашего бота.
Если аккаунт платный: в pythonanywhere тыкаем на *Tasks*, создаём и запускаем в *Always-on tasks* задачу `python3.8 /home/pythonanywhere_nickname/dan63047pythonbot/longpulling.py`, где вместо pythonanywhere_nickname должен быть ваш никнейм на pythonanywhere. После небольшого тупления pythonanywhere запустит вашего бота.
Если аккаунт бесплатный: в bash консоли переходим в директорию бота и прописываем `python3 dan63047bot.py`. Консоль можно оставить в покое, но лучше раз в день проверять её чтобы консоль не отключили
Если аккаунт бесплатный: в bash консоли переходим в директорию бота и прописываем `python3 longpulling.py`. Консоль можно оставить в покое, но лучше раз в день проверять её чтобы консоль не отключили
## Другие варианты запуска бота
Скорее всего, вы можете найти где-нибудь VPS или огранизовать всё на своём компьютере. Там вы просто скачиваете и устанавливаете Python последней версии, настраиваете конфиг бота и запускаете его.
# botconsole.py
@ -75,6 +75,14 @@ mysql_db = 'pythonanywhere_nickname$default' # Название вашей БД.
* **exit** — завершает работу консоли
* **message *peer_id* *message*** — отправляет *message* в переписку *peer_id* от имени сообщества
* **msg_history *peer_id* *count*** — возвращает историю переписки, последние *count* сообщений. Если *count* не указано, то последние 20 сообщений
На следующей команде хотелось бы уделить особое внимание, ведь с помощью неё можно вызывать функции бота
## Команда **bot**
Если просто вызвать эту команду, то она покажет список существующих объектов ботов. Однако так же у команды есть и большое количество аргументов:
* **bot *update*** — обновляет список обьектов ботов и их состояние
* **bot *create* *peer_id*** — создаёт обьект бота для переписки *peer_id*
* **bot *delete* *peer_id*** — удаляет существующий обьект бота для переписки *peer_id*
* **bot *changeMidnightFlag* *peer_id*** — меняет флаг в обьекте бота *peer_id*, по которому он определяет, надо ли оповещать о Midnight
* **bot *midnight* *peer_id*** — инициирует ивент Midnight для обьекта бота *peer_id*
# Использованные библиотеки
* [vk_api](https://github.com/python273/vk_api) — модуль для создания скриптов для социальной сети Вконтакте
* [pyowm](https://github.com/csparpa/pyowm) — модуль для получения погоды через OpenWeather API

View File

@ -1,18 +1,13 @@
import vk_api
import datetime
import time
import requests
import logging
import pyowm
import random
import json
import threading
import pymysql
import vk_api
import wikipediaapi as wiki
import config
import dan63047VKbot
from pymysql.cursors import DictCursor
from pyowm.utils.config import get_default_config
from collections import deque
from vk_api.bot_longpoll import VkBotLongPoll, VkBotEventType
@ -37,7 +32,6 @@ def log(warning, text):
logging.info(msg)
print(msg)
log(False, "Script started")
try:
vk = vk_api.VkApi(token=config.vk_group_token)
longpoll = VkBotLongPoll(vk, config.group_id)
@ -46,8 +40,10 @@ except Exception as e:
log(True, "Can't connect to longpull: "+str(e))
exit(log(False, "[SHUTDOWN]"))
def cycle():
active = True
dan63047VKbot.load_users()
print("Welcome to dan63047bot console\nEnter command:")
while active:
command = input(">")
@ -81,7 +77,7 @@ def cycle():
log(True, "No arguments")
continue
try:
if 1 in command[1]:
if len(command[1]) == 2:
m = vk.method('messages.getHistory', {'count': command[1][1], 'peer_id': command[1][0]})
else:
m = vk.method('messages.getHistory', {'peer_id': command[1][0]})
@ -93,7 +89,7 @@ def cycle():
user_info = vk.method('users.get', {'user_ids': i["from_id"], 'fields': 'verified,last_seen,sex'})
f = f'{user_info[0]["first_name"]} {user_info[0]["last_name"]}'
datetime_time = datetime.datetime.fromtimestamp(i['date'])
date = datetime_time.strftime('%d.%m.%Y в %H:%M:%S')
date = datetime_time.strftime('%d.%m.%Y %H:%M:%S')
msg = f"{f} {date}: {i['text']}"
if i['attachments']:
for m in i['attachments']:
@ -110,6 +106,51 @@ def cycle():
print(msg)
except Exception as e:
log(True, f'Failed to get history: {str(e)}')
elif command[0] == "bot":
if len(command) == 2:
command[1] = command[1].split(' ', 1)
if len(command[1]) == 2:
if command[1][0] == "midnight":
try:
userinfo = dan63047VKbot.db.get_from_users(command[1][1])
log(False, "Midnight flag: "+str(userinfo["midnight"]))
dan63047VKbot.bot[int(command[1][1])].event("midnight")
except KeyError as e:
log(True, "Bot object is not exist: "+str(e))
elif command[1][0] == "changeMidnightFlag":
try:
userinfo = dan63047VKbot.db.get_from_users(command[1][1])
if userinfo["midnight"]:
dan63047VKbot.bot[int(command[1][1])].change_flag('midnight', False)
else:
dan63047VKbot.bot[int(command[1][1])].change_flag('midnight', True)
userinfo = dan63047VKbot.db.get_from_users(command[1][1])
log(False, "Midnight flag: "+str(userinfo["midnight"]))
except KeyError as e:
log(True, "Bot object is not exist: "+str(e))
elif command[1][0] == "create":
try:
dan63047VKbot.create_new_bot_object(command[1][1])
log(False, "Bot-object has been created")
except Exception as e:
log(True, "Can't create bot-object - "+str(e))
elif command[1][0] == "delete":
try:
del dan63047VKbot.bot[int(command[1][1])]
dan63047VKbot.db.delete_user(command[1][1])
log(False, "Bot-object has been deleted")
except Exception as e:
log(True, "Can't delete bot-object - "+str(e))
else:
if command[1][0] == "update":
dan63047VKbot.load_users()
else:
if dan63047VKbot.bot:
log(False, f"There is {len(dan63047VKbot.bot)} bot-objects")
for i in dan63047VKbot.bot:
print(dan63047VKbot.bot[i])
else:
print("No bot-objects")
else:
log(True, "Unknown command. Type 'help' for command list")

View File

@ -1,3 +1,4 @@
"""Here you can found Bot class and Database worker class"""
import vk_api
import datetime
import time
@ -8,7 +9,6 @@ import random
import json
import threading
import pymysql
import vk_api
import wikipediaapi as wiki
import config
from pymysql.cursors import DictCursor
@ -41,8 +41,10 @@ def log(warning, text):
logging.info(msg)
print(msg)
bot = {}
errors_array = {"access": "Отказано в доступе",
"miss_argument": "Отсуствует аргумент", "command_off": "Команда отключена"}
log(False, "Script started")
try:
vk = vk_api.VkApi(token=config.vk_group_token)
longpoll = VkBotLongPoll(vk, config.group_id)
@ -86,11 +88,6 @@ except Exception:
weather_command = False
log(True, "Invalid OpenWeatherMap API key, !weather command will be turned off")
bot = {}
errors_array = {"access": "Отказано в доступе",
"miss_argument": "Отсуствует аргумент", "command_off": "Команда отключена"}
class Database_worker():
def __init__(self):
@ -278,13 +275,32 @@ def get_weather(place):
class VkBot:
"""Bot object, which can answer to user commands\n\n
def __init__(self, peer_id, midnight=False, awaiting=None, access=1, new_post=False, admin_mode=False):
Keyword arguments:\n
peer_id -- id of conversation with user for answering. Int\n
midnight -- flag of midnight function, which send every midnigtht a message. Defalt: False. Bool\n
awaiting -- strind, what show, which function awaiting input. Defalt: None. Str\n
access -- flag, what set access level to bot functions. Defalt: True. Bool\n
new_post -- flag of notificaton function about new post on group. Defalt: False. Bool\n
admin_mode -- flag of moderating function, which moderate conversation. Defalt: False. Bool
"""
def __init__(self, peer_id, midnight=False, awaiting=None, access=True, new_post=False, admin_mode=False):
"""Initialise the bot object\n\n
Keyword arguments:\n
peer_id -- id of conversation with user for answering. Int\n
midnight -- flag of midnight function, which send every midnigtht a message. Defalt: False. Bool\n
awaiting -- strind, what show, which function awaiting input. Defalt: None. Str\n
access -- flag, what set access level to bot functions. Defalt: True. Bool\n
new_post -- flag of notificaton function about new post on group. Defalt: False. Bool\n
admin_mode -- flag of moderating function, which moderate conversation. Defalt: False. Bool
"""
log(False, f"[BOT_{peer_id}] Created new bot-object")
self._CHAT_ID = peer_id
self._AWAITING_INPUT_MODE = awaiting
self._ACCESS_LEVEL = access
self._ACCESS_TO_ALL = access
self._SET_UP_REMINDER = {"task": None, "time": None}
self._MIDNIGHT_EVENT = midnight
self._NEW_POST = new_post
@ -299,7 +315,7 @@ class VkBot:
"!echo", "!game", "!debug", "!midnight", "!access", "!turnoff", "!reminder", "!subscribe", "!random", "!admin_mode"]
def __str__(self):
return f"[BOT_{str(self._CHAT_ID)}] a: {str(self._ACCESS_LEVEL)}, mn: {str(self._MIDNIGHT_EVENT)}, await: {str(self._AWAITING_INPUT_MODE)}, sub: {str(self._NEW_POST)}, adm: {str(self._ADMIN_MODE)}"
return f"[BOT_{str(self._CHAT_ID)}] a: {str(self._ACCESS_TO_ALL)}, mn: {str(self._MIDNIGHT_EVENT)}, await: {str(self._AWAITING_INPUT_MODE)}, sub: {str(self._NEW_POST)}, adm: {str(self._ADMIN_MODE)}"
def __del__(self):
log(False, f"[BOT_{str(self._CHAT_ID)}] Bot-object has been deleted")
@ -435,7 +451,7 @@ class VkBot:
respond['text'] = errors_array["miss_argument"]
elif message[0] == self._COMMANDS[11]:
if self._ACCESS_LEVEL or int(user_id) == int(config.owner_id):
if self._ACCESS_TO_ALL or int(user_id) == int(config.owner_id):
try:
respond['text'] = self.debug(message[1])
except IndexError:
@ -444,14 +460,14 @@ class VkBot:
respond["text"] = errors_array["access"]
elif message[0] == self._COMMANDS[12]:
if self._ACCESS_LEVEL or int(user_id) == int(config.owner_id):
if self._ACCESS_TO_ALL or int(user_id) == int(config.owner_id):
if self._MIDNIGHT_EVENT:
self.change_midnight(False)
self.change_flag('midnight', False)
self.send("Уведомление о миднайте выключено")
log(False,
f"[BOT_{self._CHAT_ID}] Unsubscribed from event \"Midnight\"")
else:
self.change_midnight(True)
self.change_flag('midnight', True)
self.send("Бот будет уведомлять вас о каждом миднайте")
log(False,
f"[BOT_{self._CHAT_ID}] Subscribed on event \"Midnight\"")
@ -463,16 +479,16 @@ class VkBot:
try:
if message[1] == "owner":
respond['text'] = "Теперь некоторыми командами может пользоваться только владелец бота"
self.change_access(0)
self.change_flag('admin_mode', False)
elif message[1] == "all":
respond['text'] = "Теперь все могут пользоваться всеми командами"
self.change_access(1)
self.change_flag('admin_mode', True)
else:
respond['text'] = "Некорректный аргумент"
except IndexError:
respond['text'] = errors_array["miss_argument"]
log(False,
f"[BOT_{self._CHAT_ID}] Access level changed on {self._ACCESS_LEVEL}")
f"[BOT_{self._CHAT_ID}] Access level changed on {self._ACCESS_TO_ALL}")
else:
respond['text'] = errors_array["access"]
@ -485,14 +501,14 @@ class VkBot:
respond['text'] = "Функция удалена за ненадобнастью"
elif message[0] == self._COMMANDS[16]:
if self._ACCESS_LEVEL or int(user_id) == int(config.owner_id):
if self._ACCESS_TO_ALL or int(user_id) == int(config.owner_id):
if self._NEW_POST:
self.change_new_post(False)
self.change_flag('new_post', False)
self.send("Уведомление о новом посте выключено")
log(False,
f"[BOT_{self._CHAT_ID}] Unsubscribed from new posts")
else:
self.change_new_post(True)
self.change_flag('new_post', True)
self.send(
"Бот будет уведомлять вас о каждом новом посте")
log(False,
@ -522,12 +538,12 @@ class VkBot:
"peer_id": int(self._CHAT_ID), "group_id": config.group_id})
if self._ADMIN_MODE:
respond["text"] = "Режим модерирования выключен"
self.change_admin_mode(False)
self.change_flag('admin_mode', False)
log(False,
f"[BOT_{self._CHAT_ID}] Admin mode: {self._ADMIN_MODE}")
else:
respond["text"] = "Режим модерирования включён"
self.change_admin_mode(True)
self.change_flag('admin_mode', True)
log(False,
f"[BOT_{self._CHAT_ID}] Admin mode: {self._ADMIN_MODE}")
except Exception:
@ -761,26 +777,41 @@ class VkBot:
return f"Рандомное число от {lower} до {higher}:<br>{r}"
def change_await(self, awaiting=None):
"""Change the awaiting input state
Keyword arguments:
awaiting -- name of function, what awaiting input from user. Defalt: None. String
"""
self._AWAITING_INPUT_MODE = awaiting
db.update_user(self._CHAT_ID, "awaiting", self._AWAITING_INPUT_MODE)
def change_access(self, level):
self._ACCESS_LEVEL = level
db.update_user(self._CHAT_ID, "access", self._ACCESS_LEVEL)
def change_new_post(self, new_post):
self._NEW_POST = new_post
db.update_user(self._CHAT_ID, "new_post", self._NEW_POST)
def change_midnight(self, midnight):
self._MIDNIGHT_EVENT = midnight
db.update_user(self._CHAT_ID, "midnight", self._MIDNIGHT_EVENT)
def change_admin_mode(self, admin_mode):
self._ADMIN_MODE = admin_mode
db.update_user(self._CHAT_ID, "admin_mode", self._ADMIN_MODE)
def change_flag(self, flag, value):
"""Change 'flag' to 'value'
Keyword arguments:
flag -- name of flag. Can be 'access', 'new_post', 'midnight', 'admin_mode'. String
value -- set the flag state. Bool
"""
if flag == 'access':
self._ACCESS_TO_ALL = value
db.update_user(self._CHAT_ID, "access", self._ACCESS_TO_ALL)
elif flag == 'new_post':
self._NEW_POST = value
db.update_user(self._CHAT_ID, "new_post", self._NEW_POST)
elif flag == 'midnight':
self._MIDNIGHT_EVENT = value
db.update_user(self._CHAT_ID, "midnight", self._MIDNIGHT_EVENT)
elif flag == 'admin_mode':
self._ADMIN_MODE = value
db.update_user(self._CHAT_ID, "admin_mode", self._ADMIN_MODE)
def send(self, message=None, attachment=None):
"""Send to user something.
Keyword arguments:
message -- text of message. string
attachment -- name of attachment. string
"""
try:
random_id = random.randint(-9223372036854775808,
9223372036854775807)
@ -792,80 +823,3 @@ class VkBot:
debug_array['messages_answered'] += 1
except Exception as e:
log(True, f'Failed to send message: {str(e)}')
def bots():
log(False, "Started listening longpull server")
debug_array['start_time'] = time.time()
for event in MyVkLongPoll.listen(longpoll):
try:
if event.type == VkBotEventType.MESSAGE_NEW:
log_msg = f'[MESSAGE] id: {event.message.id}, peer_id: {event.message.peer_id}, user_id: {event.message.from_id}'
if event.message.action:
log_msg += f', action: '+event.message.action["type"]+', user id in action: '+ str(event.message.action["member_id"])
if event.message.text != "":
log_msg += f', text: "{event.message.text}"'
if event.message.attachments:
atch = ', attachments: '
for i in event.message.attachments:
if i['type'] == "sticker":
atch += f"sticker_id{i[i['type']]['sticker_id']}"
elif i['type'] == "wall":
atch += i['type'] + str(i[i['type']]['from_id']) + \
"_" + str(i[i['type']]['id']) + " "
elif i['type'] == "link":
atch += i['type'] + " " + i[i['type']]['title'] + " "
else:
atch += i['type'] + str(i[i['type']]['owner_id']) + \
"_" + str(i[i['type']]['id']) + " "
log_msg += atch
log(False, log_msg)
debug_array['messages_get'] += 1
if int(event.message.peer_id) in bot:
bot[event.message.peer_id].get_message(event)
else:
create_new_bot_object(event.message.peer_id)
bot[event.message.peer_id].get_message(event)
elif event.type == VkBotEventType.WALL_POST_NEW:
log(False, f"[NEW_POST] id{event.object.id}")
users = db.get_all_users()
for i in users:
if (config.use_database):
bot[int(i['chat_id'])].event("post", event.object)
else:
bot[int(i)].event("post", event.object)
elif event.type == VkBotEventType.MESSAGE_DENY:
log(False,
f"User {event.object.user_id} deny messages from that group")
del bot[int(event.object.user_id)]
db.delete_user(event.object.user_id)
else:
log(False, f"Event {str(event.type)} happend")
except Exception as kek:
log(True, f"Беды с ботом: {str(kek)}")
debug_array['bot_warnings'] += 1
continue
def midnight():
while True:
current_time = time.time()+10800
if int(current_time) % 86400 == 0:
log(False, "[EVENT_STARTED] \"Midnight\"")
users = db.get_all_users()
for i in users:
if (config.use_database):
bot[int(i['chat_id'])].event("midnight")
else:
bot[int(i)].event("midnight")
log(False, "[EVENT_ENDED] \"Midnight\"")
time.sleep(1)
else:
time.sleep(0.50)
load_users()
tread_bots = threading.Thread(target=bots)
tread_midnight = threading.Thread(target=midnight, daemon=True)
tread_bots.start()
tread_midnight.start()

88
longpulling.py Normal file
View File

@ -0,0 +1,88 @@
import datetime
import time
import logging
import dan63047VKbot
import config
import vk_api
import threading
dan63047VKbot.log(False, "Script started")
def bots():
dan63047VKbot.log(False, "Started listening longpull server")
dan63047VKbot.debug_array['start_time'] = time.time()
for event in dan63047VKbot.MyVkLongPoll.listen(dan63047VKbot.longpoll):
try:
if event.type == dan63047VKbot.VkBotEventType.MESSAGE_NEW:
log_msg = f'[MESSAGE] id: {event.message.id}, peer_id: {event.message.peer_id}, user_id: {event.message.from_id}'
if event.message.action:
log_msg += f', action: '+event.message.action["type"]+', user id in action: '+ str(event.message.action["member_id"])
if event.message.text != "":
log_msg += f', text: "{event.message.text}"'
if event.message.attachments:
atch = ', attachments: '
for i in event.message.attachments:
if i['type'] == "sticker":
atch += f"sticker_id{i[i['type']]['sticker_id']}"
elif i['type'] == "wall":
atch += i['type'] + str(i[i['type']]['from_id']) + \
"_" + str(i[i['type']]['id']) + " "
elif i['type'] == "link":
atch += i['type'] + " " + i[i['type']]['title'] + " "
else:
atch += i['type'] + str(i[i['type']]['owner_id']) + \
"_" + str(i[i['type']]['id']) + " "
log_msg += atch
dan63047VKbot.log(False, log_msg)
dan63047VKbot.debug_array['messages_get'] += 1
if int(event.message.peer_id) in dan63047VKbot.bot:
dan63047VKbot.bot[event.message.peer_id].get_message(event)
else:
dan63047VKbot.create_new_bot_object(event.message.peer_id)
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)
else:
dan63047VKbot.log(False, f"[NEW_OFFER] id{event.object.id}")
elif event.type == dan63047VKbot.VkBotEventType.MESSAGE_DENY:
dan63047VKbot.log(False,
f"User {event.object.user_id} deny messages from that group")
del dan63047VKbot.bot[int(event.object.user_id)]
dan63047VKbot.db.delete_user(event.object.user_id)
else:
dan63047VKbot.log(False, f"Event {str(event.type)} happend")
except Exception as kek:
dan63047VKbot.log(True, f"Беды с ботом: {str(kek)}")
dan63047VKbot.debug_array['bot_warnings'] += 1
continue
def midnight():
while True:
current_time = time.time()+10800
if int(current_time) % 86400 == 0:
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.log(False, "[EVENT_ENDED] \"Midnight\"")
time.sleep(1)
else:
time.sleep(0.50)
dan63047VKbot.load_users()
tread_bots = threading.Thread(target=bots)
tread_midnight = threading.Thread(target=midnight, daemon=True)
tread_bots.start()
tread_midnight.start()